Search in sources :

Example 6 with MethodNode

use of org.jetbrains.org.objectweb.asm.tree.MethodNode in project intellij-community by JetBrains.

the class KeyedMethodVisitor method visitMethod.

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    MethodNode node = new MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions);
    Method method = new Method(className, node.name, node.desc);
    boolean stable = stableClass || (node.access & STABLE_FLAGS) != 0 || "<init>".equals(node.name);
    return visitMethod(node, new Key(method, Out, stable));
}
Also used : MethodNode(org.jetbrains.org.objectweb.asm.tree.MethodNode)

Example 7 with MethodNode

use of org.jetbrains.org.objectweb.asm.tree.MethodNode in project intellij-community by JetBrains.

the class BytecodeAnalysisIndex method collectKeys.

@NotNull
private static Map<Bytes, Void> collectKeys(byte[] content) throws NoSuchAlgorithmException {
    HashMap<Bytes, Void> map = new HashMap<>();
    MessageDigest md = BytecodeAnalysisConverter.getMessageDigest();
    new ClassReader(content).accept(new KeyedMethodVisitor() {

        @Nullable
        @Override
        MethodVisitor visitMethod(MethodNode node, Key key) {
            map.put(ClassDataIndexer.compressKey(md, key), null);
            return null;
        }
    }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
    return map;
}
Also used : MethodNode(org.jetbrains.org.objectweb.asm.tree.MethodNode) ClassReader(org.jetbrains.org.objectweb.asm.ClassReader) MessageDigest(java.security.MessageDigest) Nullable(org.jetbrains.annotations.Nullable) MethodVisitor(org.jetbrains.org.objectweb.asm.MethodVisitor) NotNull(org.jetbrains.annotations.NotNull)

Example 8 with MethodNode

use of org.jetbrains.org.objectweb.asm.tree.MethodNode in project intellij-community by JetBrains.

the class BytecodeAnalysisTest method checkLeakingParameters.

private static void checkLeakingParameters(Class<?> jClass) throws IOException {
    final HashMap<Method, boolean[]> map = new HashMap<>();
    // collecting leakedParameters
    final ClassReader classReader = new ClassReader(jClass.getResourceAsStream("/" + jClass.getName().replace('.', '/') + ".class"));
    classReader.accept(new ClassVisitor(Opcodes.API_VERSION) {

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            final MethodNode node = new MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions);
            final Method method = new Method(classReader.getClassName(), name, desc);
            return new MethodVisitor(Opcodes.API_VERSION, node) {

                @Override
                public void visitEnd() {
                    super.visitEnd();
                    try {
                        map.put(method, LeakingParameters.build(classReader.getClassName(), node, false).parameters);
                    } catch (AnalyzerException ignore) {
                    }
                }
            };
        }
    }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
    for (java.lang.reflect.Method jMethod : jClass.getDeclaredMethods()) {
        Method method = new Method(Type.getType(jClass).getInternalName(), jMethod.getName(), Type.getMethodDescriptor(jMethod));
        Annotation[][] annotations = jMethod.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            boolean isLeaking = false;
            Annotation[] parameterAnnotations = annotations[i];
            for (Annotation parameterAnnotation : parameterAnnotations) {
                if (parameterAnnotation.annotationType() == ExpectLeaking.class) {
                    isLeaking = true;
                }
            }
            assertEquals(method.toString() + " #" + i, isLeaking, map.get(method)[i]);
        }
    }
}
Also used : AnalyzerException(org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException) HashMap(java.util.HashMap) Annotation(java.lang.annotation.Annotation) MethodNode(org.jetbrains.org.objectweb.asm.tree.MethodNode)

Example 9 with MethodNode

use of org.jetbrains.org.objectweb.asm.tree.MethodNode in project intellij-community by JetBrains.

the class ClassDataIndexer method processClass.

public static Map<Key, List<Equation>> processClass(final ClassReader classReader, final String presentableUrl) {
    // It is OK to share pending states, actions and results for analyses.
    // Analyses are designed in such a way that they first write to states/actions/results and then read only those portion
    // of states/actions/results which were written by the current pass of the analysis.
    // Since states/actions/results are quite expensive to create (32K array) for each analysis, we create them once per class analysis.
    final State[] sharedPendingStates = new State[Analysis.STEPS_LIMIT];
    final PendingAction[] sharedPendingActions = new PendingAction[Analysis.STEPS_LIMIT];
    final PResults.PResult[] sharedResults = new PResults.PResult[Analysis.STEPS_LIMIT];
    final Map<Key, List<Equation>> equations = new HashMap<>();
    classReader.accept(new KeyedMethodVisitor() {

        protected MethodVisitor visitMethod(final MethodNode node, final Key key) {
            return new MethodVisitor(Opcodes.API_VERSION, node) {

                private boolean jsr;

                @Override
                public void visitJumpInsn(int opcode, Label label) {
                    if (opcode == Opcodes.JSR) {
                        jsr = true;
                    }
                    super.visitJumpInsn(opcode, label);
                }

                @Override
                public void visitEnd() {
                    super.visitEnd();
                    equations.put(key, processMethod(node, jsr, key.method, key.stable));
                }
            };
        }

        /**
       * Facade for analysis, it invokes specialized analyses for branching/non-branching methods.
       *
       * @param methodNode asm node for method
       * @param jsr whether a method has jsr instruction
       */
        private List<Equation> processMethod(final MethodNode methodNode, boolean jsr, Method method, boolean stable) {
            ProgressManager.checkCanceled();
            final Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
            final Type resultType = Type.getReturnType(methodNode.desc);
            final boolean isReferenceResult = ASMUtils.isReferenceType(resultType);
            final boolean isBooleanResult = ASMUtils.isBooleanType(resultType);
            final boolean isInterestingResult = isReferenceResult || isBooleanResult;
            // 4*n: for each reference parameter: @NotNull IN, @Nullable, null -> ... contract, !null -> contract
            // 3: @NotNull OUT, @Nullable OUT, purity analysis
            List<Equation> equations = new ArrayList<>(argumentTypes.length * 4 + 3);
            equations.add(PurityAnalysis.analyze(method, methodNode, stable));
            if (argumentTypes.length == 0 && !isInterestingResult) {
                // no need to continue analysis
                return equations;
            }
            try {
                final ControlFlowGraph graph = ControlFlowGraph.build(className, methodNode, jsr);
                if (graph.transitions.length > 0) {
                    final DFSTree dfs = DFSTree.build(graph.transitions, graph.edgeCount);
                    boolean branching = !dfs.back.isEmpty();
                    if (!branching) {
                        for (int[] transition : graph.transitions) {
                            if (transition != null && transition.length > 1) {
                                branching = true;
                                break;
                            }
                        }
                    }
                    if (branching) {
                        RichControlFlow richControlFlow = new RichControlFlow(graph, dfs);
                        if (richControlFlow.reducible()) {
                            NegationAnalysis negated = tryNegation(method, argumentTypes, graph, isBooleanResult, dfs, jsr);
                            processBranchingMethod(method, methodNode, richControlFlow, argumentTypes, isReferenceResult, isBooleanResult, stable, jsr, equations, negated);
                            return equations;
                        }
                        LOG.debug(method + ": CFG is not reducible");
                    } else // simple
                    {
                        processNonBranchingMethod(method, argumentTypes, graph, isReferenceResult, isBooleanResult, stable, equations);
                        return equations;
                    }
                }
                return topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable);
            } catch (ProcessCanceledException e) {
                throw e;
            } catch (Throwable e) {
                // incorrect bytecode may result in Runtime exceptions during analysis
                // so here we suppose that exception is due to incorrect bytecode
                LOG.debug("Unexpected Error during processing of " + method + " in " + presentableUrl, e);
                return topEquations(method, argumentTypes, isReferenceResult, isInterestingResult, stable);
            }
        }

        private NegationAnalysis tryNegation(final Method method, final Type[] argumentTypes, final ControlFlowGraph graph, final boolean isBooleanResult, final DFSTree dfs, final boolean jsr) throws AnalyzerException {
            class Util {

                boolean isMethodCall(int opCode) {
                    return opCode == Opcodes.INVOKESTATIC || opCode == Opcodes.INVOKESPECIAL || opCode == Opcodes.INVOKEVIRTUAL || opCode == Opcodes.INVOKEINTERFACE;
                }

                boolean singleIfBranch() {
                    int branch = 0;
                    for (int i = 0; i < graph.transitions.length; i++) {
                        int[] transition = graph.transitions[i];
                        if (transition.length == 2) {
                            branch++;
                            int opCode = graph.methodNode.instructions.get(i).getOpcode();
                            boolean isIfInsn = opCode == Opcodes.IFEQ || opCode == Opcodes.IFNE;
                            if (!isIfInsn) {
                                return false;
                            }
                        }
                        if (branch > 1)
                            return false;
                    }
                    return branch == 1;
                }

                boolean singleMethodCall() {
                    int callCount = 0;
                    for (int i = 0; i < graph.transitions.length; i++) {
                        if (isMethodCall(graph.methodNode.instructions.get(i).getOpcode())) {
                            callCount++;
                            if (callCount > 1) {
                                return false;
                            }
                        }
                    }
                    return callCount == 1;
                }

                public boolean booleanConstResult() {
                    try {
                        final boolean[] origins = OriginsAnalysis.resultOrigins(leakingParametersAndFrames(method, graph.methodNode, argumentTypes, jsr).frames, graph.methodNode.instructions, graph);
                        for (int i = 0; i < origins.length; i++) {
                            if (origins[i]) {
                                int opCode = graph.methodNode.instructions.get(i).getOpcode();
                                boolean isBooleanConst = opCode == Opcodes.ICONST_0 || opCode == Opcodes.ICONST_1;
                                if (!isBooleanConst) {
                                    return false;
                                }
                            }
                        }
                        return true;
                    } catch (AnalyzerException ignore) {
                    }
                    return false;
                }
            }
            if (graph.methodNode.instructions.size() < 20 && isBooleanResult && dfs.back.isEmpty() && !jsr) {
                Util util = new Util();
                if (util.singleIfBranch() && util.singleMethodCall() && util.booleanConstResult()) {
                    NegationAnalysis analyzer = new NegationAnalysis(method, graph);
                    try {
                        analyzer.analyze();
                        return analyzer;
                    } catch (NegationAnalysisFailure ignore) {
                        return null;
                    }
                }
            }
            return null;
        }

        private void processBranchingMethod(final Method method, final MethodNode methodNode, final RichControlFlow richControlFlow, Type[] argumentTypes, boolean isReferenceResult, boolean isBooleanResult, final boolean stable, boolean jsr, List<Equation> result, NegationAnalysis negatedAnalysis) throws AnalyzerException {
            boolean isInterestingResult = isBooleanResult || isReferenceResult;
            boolean maybeLeakingParameter = isInterestingResult;
            for (Type argType : argumentTypes) {
                if (ASMUtils.isReferenceType(argType)) {
                    maybeLeakingParameter = true;
                    break;
                }
            }
            final LeakingParameters leakingParametersAndFrames = maybeLeakingParameter ? leakingParametersAndFrames(method, methodNode, argumentTypes, jsr) : null;
            boolean[] leakingParameters = leakingParametersAndFrames != null ? leakingParametersAndFrames.parameters : null;
            boolean[] leakingNullableParameters = leakingParametersAndFrames != null ? leakingParametersAndFrames.nullableParameters : null;
            final boolean[] origins = isInterestingResult ? OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow) : null;
            Equation outEquation = isInterestingResult ? new InOutAnalysis(richControlFlow, Out, origins, stable, sharedPendingStates).analyze() : null;
            if (isReferenceResult) {
                result.add(outEquation);
                result.add(new Equation(new Key(method, NullableOut, stable), NullableMethodAnalysis.analyze(methodNode, origins, jsr)));
            }
            boolean withCycle = !richControlFlow.dfsTree.back.isEmpty();
            if (argumentTypes.length > 50 && withCycle) {
                // IDEA-137443 - do not analyze very complex methods
                return;
            }
            // arguments and contract clauses
            for (int i = 0; i < argumentTypes.length; i++) {
                boolean notNullParam = false;
                if (ASMUtils.isReferenceType(argumentTypes[i])) {
                    boolean possibleNPE = false;
                    if (leakingParameters[i]) {
                        NonNullInAnalysis notNullInAnalysis = new NonNullInAnalysis(richControlFlow, new In(i, In.NOT_NULL_MASK), stable, sharedPendingActions, sharedResults);
                        Equation notNullParamEquation = notNullInAnalysis.analyze();
                        possibleNPE = notNullInAnalysis.possibleNPE;
                        notNullParam = notNullParamEquation.rhs.equals(FINAL_NOT_NULL);
                        result.add(notNullParamEquation);
                    } else {
                        // parameter is not leaking, so it is definitely NOT @NotNull
                        result.add(new Equation(new Key(method, new In(i, In.NOT_NULL_MASK), stable), FINAL_TOP));
                    }
                    if (leakingNullableParameters[i]) {
                        if (notNullParam || possibleNPE) {
                            result.add(new Equation(new Key(method, new In(i, In.NULLABLE_MASK), stable), FINAL_TOP));
                        } else {
                            result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE_MASK), stable, sharedPendingStates).analyze());
                        }
                    } else {
                        result.add(new Equation(new Key(method, new In(i, In.NULLABLE_MASK), stable), FINAL_NULL));
                    }
                    if (isInterestingResult) {
                        if (leakingParameters[i]) {
                            if (notNullParam) {
                                // @NotNull, so "null->fail"
                                result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT));
                            } else {
                                // may be null on some branch, running "null->..." analysis
                                if (isBooleanResult && negatedAnalysis != null) {
                                    result.add(negatedAnalysis.contractEquation(i, Value.Null, stable));
                                } else {
                                    result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable, sharedPendingStates).analyze());
                                }
                            }
                            if (isBooleanResult && negatedAnalysis != null) {
                                result.add(negatedAnalysis.contractEquation(i, Value.NotNull, stable));
                            } else {
                                result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable, sharedPendingStates).analyze());
                            }
                        } else {
                            // parameter is not leaking, so a contract is the same as for the whole method
                            result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), outEquation.rhs));
                            result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.rhs));
                        }
                    }
                }
            }
        }

        private void processNonBranchingMethod(Method method, Type[] argumentTypes, ControlFlowGraph graph, boolean isReferenceResult, boolean isBooleanResult, boolean stable, List<Equation> result) throws AnalyzerException {
            CombinedAnalysis analyzer = new CombinedAnalysis(method, graph);
            analyzer.analyze();
            if (isReferenceResult) {
                result.add(analyzer.outContractEquation(stable));
                result.add(analyzer.nullableResultEquation(stable));
            }
            for (int i = 0; i < argumentTypes.length; i++) {
                Type argType = argumentTypes[i];
                if (ASMUtils.isReferenceType(argType)) {
                    result.add(analyzer.notNullParamEquation(i, stable));
                    result.add(analyzer.nullableParamEquation(i, stable));
                    if (isReferenceResult || isBooleanResult) {
                        result.add(analyzer.contractEquation(i, Value.Null, stable));
                        result.add(analyzer.contractEquation(i, Value.NotNull, stable));
                    }
                }
            }
        }

        private List<Equation> topEquations(Method method, Type[] argumentTypes, boolean isReferenceResult, boolean isInterestingResult, boolean stable) {
            // 4 = @NotNull parameter, @Nullable parameter, null -> ..., !null -> ...
            List<Equation> result = new ArrayList<>(argumentTypes.length * 4 + 2);
            if (isReferenceResult) {
                result.add(new Equation(new Key(method, Out, stable), FINAL_TOP));
                result.add(new Equation(new Key(method, NullableOut, stable), FINAL_BOT));
            }
            for (int i = 0; i < argumentTypes.length; i++) {
                if (ASMUtils.isReferenceType(argumentTypes[i])) {
                    result.add(new Equation(new Key(method, new In(i, In.NOT_NULL_MASK), stable), FINAL_TOP));
                    result.add(new Equation(new Key(method, new In(i, In.NULLABLE_MASK), stable), FINAL_TOP));
                    if (isInterestingResult) {
                        result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_TOP));
                        result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), FINAL_TOP));
                    }
                }
            }
            return result;
        }

        @NotNull
        private LeakingParameters leakingParametersAndFrames(Method method, MethodNode methodNode, Type[] argumentTypes, boolean jsr) throws AnalyzerException {
            return argumentTypes.length < 32 ? LeakingParameters.buildFast(method.internalClassName, methodNode, jsr) : LeakingParameters.build(method.internalClassName, methodNode, jsr);
        }
    }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
    return equations;
}
Also used : HashMap(java.util.HashMap) ContainerUtil(com.intellij.util.containers.ContainerUtil) NotNull(org.jetbrains.annotations.NotNull) MethodNode(org.jetbrains.org.objectweb.asm.tree.MethodNode) ArrayList(java.util.ArrayList) List(java.util.List) ProcessCanceledException(com.intellij.openapi.progress.ProcessCanceledException) AnalyzerException(org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException)

Example 10 with MethodNode

use of org.jetbrains.org.objectweb.asm.tree.MethodNode in project kotlin by JetBrains.

the class CodegenTestCase method verifyWithAsm.

private static boolean verifyWithAsm(@NotNull OutputFile file, ClassLoader loader) {
    ClassNode classNode = new ClassNode();
    new ClassReader(file.asByteArray()).accept(classNode, 0);
    SimpleVerifier verifier = new SimpleVerifier();
    verifier.setClassLoader(loader);
    Analyzer<BasicValue> analyzer = new Analyzer<BasicValue>(verifier);
    boolean noErrors = true;
    for (MethodNode method : classNode.methods) {
        try {
            analyzer.analyze(classNode.name, method);
        } catch (Throwable e) {
            System.err.println(file.asText());
            System.err.println(classNode.name + "::" + method.name + method.desc);
            //noinspection InstanceofCatchParameter
            if (e instanceof AnalyzerException) {
                // Print the erroneous instruction
                TraceMethodVisitor tmv = new TraceMethodVisitor(new Textifier());
                ((AnalyzerException) e).node.accept(tmv);
                PrintWriter pw = new PrintWriter(System.err);
                tmv.p.print(pw);
                pw.flush();
            }
            e.printStackTrace();
            noErrors = false;
        }
    }
    return noErrors;
}
Also used : ClassNode(org.jetbrains.org.objectweb.asm.tree.ClassNode) AnalyzerException(org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException) SimpleVerifier(org.jetbrains.org.objectweb.asm.tree.analysis.SimpleVerifier) Analyzer(org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer) Textifier(org.jetbrains.org.objectweb.asm.util.Textifier) TraceMethodVisitor(org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor) BasicValue(org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue) MethodNode(org.jetbrains.org.objectweb.asm.tree.MethodNode) ClassReader(org.jetbrains.org.objectweb.asm.ClassReader) PrintWriter(java.io.PrintWriter)

Aggregations

MethodNode (org.jetbrains.org.objectweb.asm.tree.MethodNode)16 NotNull (org.jetbrains.annotations.NotNull)8 List (java.util.List)4 ClassNode (org.jetbrains.org.objectweb.asm.tree.ClassNode)4 DeserializedCallableMemberDescriptor (org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor)3 MethodVisitor (org.jetbrains.org.objectweb.asm.MethodVisitor)3 AbstractInsnNode (org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode)3 AnalyzerException (org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException)3 PsiElement (com.intellij.psi.PsiElement)2 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 KotlinType (org.jetbrains.kotlin.types.KotlinType)2 ClassReader (org.jetbrains.org.objectweb.asm.ClassReader)2 Method (org.jetbrains.org.objectweb.asm.commons.Method)2 InsnList (org.jetbrains.org.objectweb.asm.tree.InsnList)2 MethodInsnNode (org.jetbrains.org.objectweb.asm.tree.MethodInsnNode)2 Detector (com.android.tools.klint.detector.api.Detector)1 ClassScanner (com.android.tools.klint.detector.api.Detector.ClassScanner)1 Location (com.android.tools.klint.detector.api.Location)1 ProcessCanceledException (com.intellij.openapi.progress.ProcessCanceledException)1