Example usage for org.objectweb.asm Opcodes INVOKESTATIC

List of usage examples for org.objectweb.asm Opcodes INVOKESTATIC

Introduction

In this page you can find the example usage for org.objectweb.asm Opcodes INVOKESTATIC.

Prototype

int INVOKESTATIC

To view the source code for org.objectweb.asm Opcodes INVOKESTATIC.

Click Source Link

Usage

From source file:com.android.tools.layoutlib.create.DelegateMethodAdapter.java

License:Apache License

/**
 * Generates the new code for the method.
 * <p/>/*www . j av  a 2s  .c o m*/
 * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
 * (since they have no code to visit).
 * <p/>
 * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
 * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
 * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
 * this method will be invoked from {@link MethodVisitor#visitEnd()}.
 */
public void generateCode() {
    /*
     * The goal is to generate a call to a static delegate method.
     * If this method is non-static, the first parameter will be 'this'.
     * All the parameters must be passed and then the eventual return type returned.
     *
     * Example, let's say we have a method such as
     *   public void method_1(int a, Object b, ArrayList<String> c) { ... }
     *
     * We'll want to create a body that calls a delegate method like this:
     *   TheClass_Delegate.method_1(this, a, b, c);
     *
     * If the method is non-static and the class name is an inner class (e.g. has $ in its
     * last segment), we want to push the 'this' of the outer class first:
     *   OuterClass_InnerClass_Delegate.method_1(
     *     OuterClass.this,
     *     OuterClass$InnerClass.this,
     *     a, b, c);
     *
     * Only one level of inner class is supported right now, for simplicity and because
     * we don't need more.
     *
     * The generated class name is the current class name with "_Delegate" appended to it.
     * One thing to realize is that we don't care about generics -- since generic types
     * are erased at runtime, they have no influence on the method name being called.
     */

    // Add our annotation
    AnnotationVisitor aw = mParentVisitor.visitAnnotation(
            Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), true); // visible at runtime
    aw.visitEnd();

    if (!mVisitCodeCalled) {
        // If this is a direct call to generateCode() as done by DelegateClassAdapter
        // for natives, visitCode() hasn't been called yet.
        mParentVisitor.visitCode();
        mVisitCodeCalled = true;
    }

    ArrayList<Type> paramTypes = new ArrayList<Type>();
    String delegateClassName = mClassName + DELEGATE_SUFFIX;
    boolean pushedArg0 = false;
    int maxStack = 0;

    // For an instance method (e.g. non-static), push the 'this' preceded
    // by the 'this' of any outer class, if any.
    if (!mIsStatic) {
        // Check if the last segment of the class name has inner an class.
        // Right now we only support one level of inner classes.
        int slash = mClassName.lastIndexOf('/');
        int dol = mClassName.lastIndexOf('$');
        if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
            String outerClass = mClassName.substring(0, dol);
            Type outerType = Type.getObjectType(outerClass);

            // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
            delegateClassName = delegateClassName.replace('$', '_');

            // The first-level inner class has a package-protected member called 'this$0'
            // that points to the outer class.

            // Push this.getField("this$0") on the call stack.
            mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
            mParentVisitor.visitFieldInsn(Opcodes.GETFIELD, mClassName, // class where the field is defined
                    "this$0", // field name
                    outerType.getDescriptor()); // type of the field
            maxStack++;
            paramTypes.add(outerType);
        }

        // Push "this" for the instance method, which is always ALOAD 0
        mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
        maxStack++;
        pushedArg0 = true;
        paramTypes.add(Type.getObjectType(mClassName));
    }

    // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
    Type[] argTypes = Type.getArgumentTypes(mDesc);
    int maxLocals = pushedArg0 ? 1 : 0;
    for (Type t : argTypes) {
        int size = t.getSize();
        mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
        maxLocals += size;
        maxStack += size;
        paramTypes.add(t);
    }

    // Construct the descriptor of the delegate based on the parameters
    // we pushed on the call stack. The return type remains unchanged.
    String desc = Type.getMethodDescriptor(Type.getReturnType(mDesc),
            paramTypes.toArray(new Type[paramTypes.size()]));

    // Invoke the static delegate
    mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName, mMethodName, desc);

    Type returnType = Type.getReturnType(mDesc);
    mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));

    mParentVisitor.visitMaxs(maxStack, maxLocals);
    mParentVisitor.visitEnd();

    // For debugging now. Maybe we should collect these and store them in
    // a text file for helping create the delegates. We could also compare
    // the text file to a golden and break the build on unsupported changes
    // or regressions. Even better we could fancy-print something that looks
    // like the expected Java method declaration.
    mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
}

From source file:com.android.tools.layoutlib.create.DelegateMethodAdapter2.java

License:Apache License

/**
 * Generates the new code for the method.
 * <p/>/*  w ww  .j av  a  2  s . c om*/
 * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
 * (since they have no code to visit).
 * <p/>
 * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
 * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern
 * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
 * this method will be invoked from {@link MethodVisitor#visitEnd()}.
 */
public void generateDelegateCode() {
    /*
     * The goal is to generate a call to a static delegate method.
     * If this method is non-static, the first parameter will be 'this'.
     * All the parameters must be passed and then the eventual return type returned.
     *
     * Example, let's say we have a method such as
     *   public void myMethod(int a, Object b, ArrayList<String> c) { ... }
     *
     * We'll want to create a body that calls a delegate method like this:
     *   TheClass_Delegate.myMethod(this, a, b, c);
     *
     * If the method is non-static and the class name is an inner class (e.g. has $ in its
     * last segment), we want to push the 'this' of the outer class first:
     *   OuterClass_InnerClass_Delegate.myMethod(
     *     OuterClass.this,
     *     OuterClass$InnerClass.this,
     *     a, b, c);
     *
     * Only one level of inner class is supported right now, for simplicity and because
     * we don't need more.
     *
     * The generated class name is the current class name with "_Delegate" appended to it.
     * One thing to realize is that we don't care about generics -- since generic types
     * are erased at build time, they have no influence on the method name being called.
     */

    // Add our annotation
    AnnotationVisitor aw = mDelWriter.visitAnnotation(
            Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), true); // visible at runtime
    if (aw != null) {
        aw.visitEnd();
    }

    mDelWriter.visitCode();

    if (mDelegateLineNumber != null) {
        Object[] p = mDelegateLineNumber;
        mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
    }

    ArrayList<Type> paramTypes = new ArrayList<Type>();
    String delegateClassName = mClassName + DELEGATE_SUFFIX;
    boolean pushedArg0 = false;
    int maxStack = 0;

    // Check if the last segment of the class name has inner an class.
    // Right now we only support one level of inner classes.
    Type outerType = null;
    int slash = mClassName.lastIndexOf('/');
    int dol = mClassName.lastIndexOf('$');
    if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) {
        String outerClass = mClassName.substring(0, dol);
        outerType = Type.getObjectType(outerClass);

        // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
        delegateClassName = delegateClassName.replace('$', '_');
    }

    // For an instance method (e.g. non-static), push the 'this' preceded
    // by the 'this' of any outer class, if any.
    if (!mIsStatic) {

        if (outerType != null) {
            // The first-level inner class has a package-protected member called 'this$0'
            // that points to the outer class.

            // Push this.getField("this$0") on the call stack.
            mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this
            mDelWriter.visitFieldInsn(Opcodes.GETFIELD, mClassName, // class where the field is defined
                    "this$0", // field name
                    outerType.getDescriptor()); // type of the field
            maxStack++;
            paramTypes.add(outerType);

        }

        // Push "this" for the instance method, which is always ALOAD 0
        mDelWriter.visitVarInsn(Opcodes.ALOAD, 0);
        maxStack++;
        pushedArg0 = true;
        paramTypes.add(Type.getObjectType(mClassName));
    }

    // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
    Type[] argTypes = Type.getArgumentTypes(mDesc);
    int maxLocals = pushedArg0 ? 1 : 0;
    for (Type t : argTypes) {
        int size = t.getSize();
        mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
        maxLocals += size;
        maxStack += size;
        paramTypes.add(t);
    }

    // Construct the descriptor of the delegate based on the parameters
    // we pushed on the call stack. The return type remains unchanged.
    String desc = Type.getMethodDescriptor(Type.getReturnType(mDesc),
            paramTypes.toArray(new Type[paramTypes.size()]));

    // Invoke the static delegate
    mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName, mMethodName, desc);

    Type returnType = Type.getReturnType(mDesc);
    mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN));

    mDelWriter.visitMaxs(maxStack, maxLocals);
    mDelWriter.visitEnd();

    // For debugging now. Maybe we should collect these and store them in
    // a text file for helping create the delegates. We could also compare
    // the text file to a golden and break the build on unsupported changes
    // or regressions. Even better we could fancy-print something that looks
    // like the expected Java method declaration.
    mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
}

From source file:com.android.tools.layoutlib.create.StubMethodAdapter.java

License:Apache License

private void generateInvoke() {
    /* Generates the code:
     *  OverrideMethod.invoke("signature", mIsNative ? true : false, null or this);
     *///from  w w w  . java  2s .c  om
    mParentVisitor.visitLdcInsn(mInvokeSignature);
    // push true or false
    mParentVisitor.visitInsn(mIsNative ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
    // push null or this
    if (mIsStatic) {
        mParentVisitor.visitInsn(Opcodes.ACONST_NULL);
    } else {
        mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0);
    }

    int sort = mReturnType != null ? mReturnType.getSort() : Type.VOID;
    switch (sort) {
    case Type.VOID:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeV",
                "(Ljava/lang/String;ZLjava/lang/Object;)V");
        mParentVisitor.visitInsn(Opcodes.RETURN);
        break;
    case Type.BOOLEAN:
    case Type.CHAR:
    case Type.BYTE:
    case Type.SHORT:
    case Type.INT:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeI",
                "(Ljava/lang/String;ZLjava/lang/Object;)I");
        switch (sort) {
        case Type.BOOLEAN:
            Label l1 = new Label();
            mParentVisitor.visitJumpInsn(Opcodes.IFEQ, l1);
            mParentVisitor.visitInsn(Opcodes.ICONST_1);
            mParentVisitor.visitInsn(Opcodes.IRETURN);
            mParentVisitor.visitLabel(l1);
            mParentVisitor.visitInsn(Opcodes.ICONST_0);
            break;
        case Type.CHAR:
            mParentVisitor.visitInsn(Opcodes.I2C);
            break;
        case Type.BYTE:
            mParentVisitor.visitInsn(Opcodes.I2B);
            break;
        case Type.SHORT:
            mParentVisitor.visitInsn(Opcodes.I2S);
            break;
        }
        mParentVisitor.visitInsn(Opcodes.IRETURN);
        break;
    case Type.LONG:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeL",
                "(Ljava/lang/String;ZLjava/lang/Object;)J");
        mParentVisitor.visitInsn(Opcodes.LRETURN);
        break;
    case Type.FLOAT:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeF",
                "(Ljava/lang/String;ZLjava/lang/Object;)F");
        mParentVisitor.visitInsn(Opcodes.FRETURN);
        break;
    case Type.DOUBLE:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeD",
                "(Ljava/lang/String;ZLjava/lang/Object;)D");
        mParentVisitor.visitInsn(Opcodes.DRETURN);
        break;
    case Type.ARRAY:
    case Type.OBJECT:
        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
                "com/android/tools/layoutlib/create/OverrideMethod", "invokeA",
                "(Ljava/lang/String;ZLjava/lang/Object;)Ljava/lang/Object;");
        mParentVisitor.visitTypeInsn(Opcodes.CHECKCAST, mReturnType.getInternalName());
        mParentVisitor.visitInsn(Opcodes.ARETURN);
        break;
    }

}

From source file:com.android.tools.lint.checks.ApiDetector.java

License:Apache License

@SuppressWarnings("rawtypes") // ASM API
@Override/*from  ww  w .  j a va2  s. c  o  m*/
public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) {
    if (mApiDatabase == null) {
        return;
    }

    if (AOSP_BUILD && classNode.name.startsWith("android/support/")) { //$NON-NLS-1$
        return;
    }

    // Requires util package (add prebuilts/tools/common/asm-tools/asm-debug-all-4.0.jar)
    //classNode.accept(new TraceClassVisitor(new PrintWriter(System.out)));

    int classMinSdk = getClassMinSdk(context, classNode);
    if (classMinSdk == -1) {
        classMinSdk = getMinSdk(context);
    }

    List methodList = classNode.methods;
    if (methodList.isEmpty()) {
        return;
    }

    boolean checkCalls = context.isEnabled(UNSUPPORTED) || context.isEnabled(INLINED);
    boolean checkMethods = context.isEnabled(OVERRIDE) && context.getMainProject().getBuildSdk() >= 1;
    String frameworkParent = null;
    if (checkMethods) {
        LintDriver driver = context.getDriver();
        String owner = classNode.superName;
        while (owner != null) {
            // For virtual dispatch, walk up the inheritance chain checking
            // each inherited method
            if ((owner.startsWith("android/") //$NON-NLS-1$
                    && !owner.startsWith("android/support/")) //$NON-NLS-1$
                    || owner.startsWith("java/") //$NON-NLS-1$
                    || owner.startsWith("javax/")) { //$NON-NLS-1$
                frameworkParent = owner;
                break;
            }
            owner = driver.getSuperClass(owner);
        }
        if (frameworkParent == null) {
            checkMethods = false;
        }
    }

    if (checkCalls) { // Check implements/extends
        if (classNode.superName != null) {
            String signature = classNode.superName;
            checkExtendsClass(context, classNode, classMinSdk, signature);
        }
        if (classNode.interfaces != null) {
            @SuppressWarnings("unchecked") // ASM API
            List<String> interfaceList = classNode.interfaces;
            for (String signature : interfaceList) {
                checkExtendsClass(context, classNode, classMinSdk, signature);
            }
        }
    }

    for (Object m : methodList) {
        MethodNode method = (MethodNode) m;

        int minSdk = getLocalMinSdk(method.invisibleAnnotations);
        if (minSdk == -1) {
            minSdk = classMinSdk;
        }

        InsnList nodes = method.instructions;

        if (checkMethods && Character.isJavaIdentifierStart(method.name.charAt(0))) {
            int buildSdk = context.getMainProject().getBuildSdk();
            String name = method.name;
            assert frameworkParent != null;
            int api = mApiDatabase.getCallVersion(frameworkParent, name, method.desc);
            if (api > buildSdk && buildSdk != -1) {
                // TODO: Don't complain if it's annotated with @Override; that means
                // somehow the build target isn't correct.
                String fqcn;
                String owner = classNode.name;
                if (CONSTRUCTOR_NAME.equals(name)) {
                    fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$
                } else {
                    fqcn = ClassContext.getFqcn(owner) + '#' + name;
                }
                String message = String.format(
                        "This method is not overriding anything with the current build "
                                + "target, but will in API level %1$d (current target is %2$d): %3$s",
                        api, buildSdk, fqcn);

                Location location = context.getLocation(method, classNode);
                context.report(OVERRIDE, method, null, location, message, null);
            }
        }

        if (!checkCalls) {
            continue;
        }

        if (CHECK_DECLARATIONS) {
            // Check types in parameter list and types of local variables
            List localVariables = method.localVariables;
            if (localVariables != null) {
                for (Object v : localVariables) {
                    LocalVariableNode var = (LocalVariableNode) v;
                    String desc = var.desc;
                    if (desc.charAt(0) == 'L') {
                        // "Lpackage/Class;" => "package/Bar"
                        String className = desc.substring(1, desc.length() - 1);
                        int api = mApiDatabase.getClassVersion(className);
                        if (api > minSdk) {
                            String fqcn = ClassContext.getFqcn(className);
                            String message = String.format(
                                    "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk,
                                    fqcn);
                            report(context, message, var.start, method,
                                    className.substring(className.lastIndexOf('/') + 1), null,
                                    SearchHints.create(NEAREST).matchJavaSymbol());
                        }
                    }
                }
            }

            // Check return type
            // The parameter types are already handled as local variables so we can skip
            // right to the return type.
            // Check types in parameter list
            String signature = method.desc;
            if (signature != null) {
                int args = signature.indexOf(')');
                if (args != -1 && signature.charAt(args + 1) == 'L') {
                    String type = signature.substring(args + 2, signature.length() - 1);
                    int api = mApiDatabase.getClassVersion(type);
                    if (api > minSdk) {
                        String fqcn = ClassContext.getFqcn(type);
                        String message = String.format(
                                "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn);
                        AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null;
                        report(context, message, first, method, method.name, null,
                                SearchHints.create(BACKWARD).matchJavaSymbol());
                    }
                }
            }
        }

        for (int i = 0, n = nodes.size(); i < n; i++) {
            AbstractInsnNode instruction = nodes.get(i);
            int type = instruction.getType();
            if (type == AbstractInsnNode.METHOD_INSN) {
                MethodInsnNode node = (MethodInsnNode) instruction;
                String name = node.name;
                String owner = node.owner;
                String desc = node.desc;

                // No need to check methods in this local class; we know they
                // won't be an API match
                if (node.getOpcode() == Opcodes.INVOKEVIRTUAL && owner.equals(classNode.name)) {
                    owner = classNode.superName;
                }

                boolean checkingSuperClass = false;
                while (owner != null) {
                    int api = mApiDatabase.getCallVersion(owner, name, desc);
                    if (api > minSdk) {
                        if (method.name.startsWith(SWITCH_TABLE_PREFIX)) {
                            // We're in a compiler-generated method to generate an
                            // array indexed by enum ordinal values to enum values. The enum
                            // itself must be requiring a higher API number than is
                            // currently used, but the call site for the switch statement
                            // will also be referencing it, so no need to report these
                            // calls.
                            break;
                        }

                        if (!checkingSuperClass && node.getOpcode() == Opcodes.INVOKEVIRTUAL
                                && methodDefinedLocally(classNode, name, desc)) {
                            break;
                        }

                        String fqcn;
                        if (CONSTRUCTOR_NAME.equals(name)) {
                            fqcn = "new " + ClassContext.getFqcn(owner); //$NON-NLS-1$
                        } else {
                            fqcn = ClassContext.getFqcn(owner) + '#' + name;
                        }
                        String message = String.format(
                                "Call requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn);

                        if (name.equals(ORDINAL_METHOD) && instruction.getNext() != null
                                && instruction.getNext().getNext() != null
                                && instruction.getNext().getOpcode() == Opcodes.IALOAD
                                && instruction.getNext().getNext().getOpcode() == Opcodes.TABLESWITCH) {
                            message = String.format(
                                    "Enum for switch requires API level %1$d " + "(current min is %2$d): %3$s",
                                    api, minSdk, ClassContext.getFqcn(owner));
                        }

                        report(context, message, node, method, name, null,
                                SearchHints.create(FORWARD).matchJavaSymbol());
                    }

                    // For virtual dispatch, walk up the inheritance chain checking
                    // each inherited method
                    if (owner.startsWith("android/") //$NON-NLS-1$
                            || owner.startsWith("javax/")) { //$NON-NLS-1$
                        // The API map has already inlined all inherited methods
                        // so no need to keep checking up the chain
                        // -- unless it's the support library which is also in
                        // the android/ namespace:
                        if (owner.startsWith("android/support/")) { //$NON-NLS-1$
                            owner = context.getDriver().getSuperClass(owner);
                        } else {
                            owner = null;
                        }
                    } else if (owner.startsWith("java/")) { //$NON-NLS-1$
                        if (owner.equals(LocaleDetector.DATE_FORMAT_OWNER)) {
                            checkSimpleDateFormat(context, method, node, minSdk);
                        }
                        // Already inlined; see comment above
                        owner = null;
                    } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                        owner = context.getDriver().getSuperClass(owner);
                    } else if (node.getOpcode() == Opcodes.INVOKESTATIC && api == -1) {
                        // Inherit through static classes as well
                        owner = context.getDriver().getSuperClass(owner);
                    } else {
                        owner = null;
                    }

                    checkingSuperClass = true;
                }
            } else if (type == AbstractInsnNode.FIELD_INSN) {
                FieldInsnNode node = (FieldInsnNode) instruction;
                String name = node.name;
                String owner = node.owner;
                int api = mApiDatabase.getFieldVersion(owner, name);
                if (api > minSdk) {
                    if (method.name.startsWith(SWITCH_TABLE_PREFIX)) {
                        checkSwitchBlock(context, classNode, node, method, name, owner, api, minSdk);
                        continue;
                    }
                    String fqcn = ClassContext.getFqcn(owner) + '#' + name;
                    if (mPendingFields != null) {
                        mPendingFields.remove(fqcn);
                    }
                    String message = String.format("Field requires API level %1$d (current min is %2$d): %3$s",
                            api, minSdk, fqcn);
                    report(context, message, node, method, name, null,
                            SearchHints.create(FORWARD).matchJavaSymbol());
                }
            } else if (type == AbstractInsnNode.LDC_INSN) {
                LdcInsnNode node = (LdcInsnNode) instruction;
                if (node.cst instanceof Type) {
                    Type t = (Type) node.cst;
                    String className = t.getInternalName();

                    int api = mApiDatabase.getClassVersion(className);
                    if (api > minSdk) {
                        String fqcn = ClassContext.getFqcn(className);
                        String message = String.format(
                                "Class requires API level %1$d (current min is %2$d): %3$s", api, minSdk, fqcn);
                        report(context, message, node, method,
                                className.substring(className.lastIndexOf('/') + 1), null,
                                SearchHints.create(FORWARD).matchJavaSymbol());
                    }
                }
            }
        }
    }
}

From source file:com.android.tools.lint.checks.ApiDetector.java

License:Apache License

@SuppressWarnings("rawtypes") // ASM API
private static void checkSwitchBlock(ClassContext context, ClassNode classNode, FieldInsnNode field,
        MethodNode method, String name, String owner, int api, int minSdk) {
    // Switch statements on enums are tricky. The compiler will generate a method
    // which returns an array of the enum constants, indexed by their ordinal() values.
    // However, we only want to complain if the code is actually referencing one of
    // the non-available enum fields.
    ////from   w ww  .ja v  a2  s  . c om
    // For the android.graphics.PorterDuff.Mode enum for example, the first few items
    // in the array are populated like this:
    //
    //   L0
    //    ALOAD 0
    //    GETSTATIC android/graphics/PorterDuff$Mode.ADD : Landroid/graphics/PorterDuff$Mode;
    //    INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I
    //    ICONST_1
    //    IASTORE
    //   L1
    //    GOTO L3
    //   L2
    //   FRAME FULL [[I] [java/lang/NoSuchFieldError]
    //    POP
    //   L3
    //   FRAME SAME
    //    ALOAD 0
    //    GETSTATIC android/graphics/PorterDuff$Mode.CLEAR : Landroid/graphics/PorterDuff$Mode;
    //    INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I
    //    ICONST_2
    //    IASTORE
    //    ...
    // So if we for example find that the "ADD" field isn't accessible, since it requires
    // API 11, we need to
    //   (1) First find out what its ordinal number is. We can look at the following
    //       instructions to discover this; it's the "ICONST_1" and "IASTORE" instructions.
    //       (After ICONST_5 it moves on to BIPUSH 6, BIPUSH 7, etc.)
    //   (2) Find the corresponding *usage* of this switch method. For the above enum,
    //       the switch ordinal lookup method will be called
    //         "$SWITCH_TABLE$android$graphics$PorterDuff$Mode" with desc "()[I".
    //       This means we will be looking for an invocation in some other method which looks
    //       like this:
    //         INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I
    //       (obviously, it can be invoked more than once)
    //       Note that it can be used more than once in this class and all sites should be
    //       checked!
    //   (3) Look up the corresponding table switch, which should look something like this:
    //        INVOKESTATIC (current class).$SWITCH_TABLE$android$graphics$PorterDuff$Mode ()[I
    //        ALOAD 0
    //        INVOKEVIRTUAL android/graphics/PorterDuff$Mode.ordinal ()I
    //        IALOAD
    //        LOOKUPSWITCH
    //          2: L1
    //          11: L2
    //          default: L3
    //       Here we need to see if the LOOKUPSWITCH instruction is referencing our target
    //       case. Above we were looking for the "ADD" case which had ordinal 1. Since this
    //       isn't explicitly referenced, we can ignore this field reference.
    AbstractInsnNode next = field.getNext();
    if (next == null || next.getOpcode() != Opcodes.INVOKEVIRTUAL) {
        return;
    }
    next = next.getNext();
    if (next == null) {
        return;
    }
    int ordinal;
    switch (next.getOpcode()) {
    case Opcodes.ICONST_0:
        ordinal = 0;
        break;
    case Opcodes.ICONST_1:
        ordinal = 1;
        break;
    case Opcodes.ICONST_2:
        ordinal = 2;
        break;
    case Opcodes.ICONST_3:
        ordinal = 3;
        break;
    case Opcodes.ICONST_4:
        ordinal = 4;
        break;
    case Opcodes.ICONST_5:
        ordinal = 5;
        break;
    case Opcodes.BIPUSH: {
        IntInsnNode iin = (IntInsnNode) next;
        ordinal = iin.operand;
        break;
    }
    default:
        return;
    }

    // Find usages of this call site
    List methodList = classNode.methods;
    for (Object m : methodList) {
        InsnList nodes = ((MethodNode) m).instructions;
        for (int i = 0, n = nodes.size(); i < n; i++) {
            AbstractInsnNode instruction = nodes.get(i);
            if (instruction.getOpcode() != Opcodes.INVOKESTATIC) {
                continue;
            }
            MethodInsnNode node = (MethodInsnNode) instruction;
            if (node.name.equals(method.name) && node.desc.equals(method.desc)
                    && node.owner.equals(classNode.name)) {
                // Find lookup switch
                AbstractInsnNode target = getNextInstruction(node);
                while (target != null) {
                    if (target.getOpcode() == Opcodes.LOOKUPSWITCH) {
                        LookupSwitchInsnNode lookup = (LookupSwitchInsnNode) target;
                        @SuppressWarnings("unchecked") // ASM API
                        List<Integer> keys = lookup.keys;
                        if (keys != null && keys.contains(ordinal)) {
                            String fqcn = ClassContext.getFqcn(owner) + '#' + name;
                            String message = String.format(
                                    "Enum value requires API level %1$d " + "(current min is %2$d): %3$s", api,
                                    minSdk, fqcn);
                            report(context, message, lookup, (MethodNode) m, name, null,
                                    SearchHints.create(FORWARD).matchJavaSymbol());

                            // Break out of the inner target search only; the switch
                            // statement could be used in other places in this class as
                            // well and we want to report all problematic usages.
                            break;
                        }
                    }
                    target = getNextInstruction(target);
                }
            }
        }
    }
}

From source file:com.android.tools.lint.checks.InvalidPackageDetector.java

License:Apache License

@SuppressWarnings("rawtypes") // ASM API
@Override// w  w  w .  j  a  v  a 2  s.co m
public void checkClass(@NonNull final ClassContext context, @NonNull ClassNode classNode) {
    if (!context.isFromClassLibrary() || shouldSkip(context.file)) {
        return;
    }

    if (mApiDatabase == null) {
        return;
    }

    if (classNode.name.startsWith(JAVAX_PKG_PREFIX)) {
        mJavaxLibraryClasses.add(classNode.name);
    }

    List methodList = classNode.methods;
    for (Object m : methodList) {
        MethodNode method = (MethodNode) m;

        InsnList nodes = method.instructions;

        // Check return type
        // The parameter types are already handled as local variables so we can skip
        // right to the return type.
        // Check types in parameter list
        String signature = method.desc;
        if (signature != null) {
            int args = signature.indexOf(')');
            if (args != -1 && signature.charAt(args + 1) == 'L') {
                String type = signature.substring(args + 2, signature.length() - 1);
                if (isInvalidPackage(type)) {
                    AbstractInsnNode first = nodes.size() > 0 ? nodes.get(0) : null;
                    record(context, method, first, type);
                }
            }
        }

        for (int i = 0, n = nodes.size(); i < n; i++) {
            AbstractInsnNode instruction = nodes.get(i);
            int type = instruction.getType();
            if (type == AbstractInsnNode.METHOD_INSN) {
                MethodInsnNode node = (MethodInsnNode) instruction;
                String owner = node.owner;

                // No need to check methods in this local class; we know they
                // won't be an API match
                if (node.getOpcode() == Opcodes.INVOKEVIRTUAL && owner.equals(classNode.name)) {
                    owner = classNode.superName;
                }

                while (owner != null) {
                    if (isInvalidPackage(owner)) {
                        record(context, method, instruction, owner);
                    }

                    // For virtual dispatch, walk up the inheritance chain checking
                    // each inherited method
                    if (owner.startsWith("android/") //$NON-NLS-1$
                            || owner.startsWith(JAVA_PKG_PREFIX) || owner.startsWith(JAVAX_PKG_PREFIX)) {
                        owner = null;
                    } else if (node.getOpcode() == Opcodes.INVOKEVIRTUAL) {
                        owner = context.getDriver().getSuperClass(owner);
                    } else if (node.getOpcode() == Opcodes.INVOKESTATIC) {
                        // Inherit through static classes as well
                        owner = context.getDriver().getSuperClass(owner);
                    } else {
                        owner = null;
                    }
                }
            } else if (type == AbstractInsnNode.FIELD_INSN) {
                FieldInsnNode node = (FieldInsnNode) instruction;
                String owner = node.owner;
                if (isInvalidPackage(owner)) {
                    record(context, method, instruction, owner);
                }
            } else if (type == AbstractInsnNode.LDC_INSN) {
                LdcInsnNode node = (LdcInsnNode) instruction;
                if (node.cst instanceof Type) {
                    Type t = (Type) node.cst;
                    String className = t.getInternalName();
                    if (isInvalidPackage(className)) {
                        record(context, method, instruction, className);
                    }
                }
            }
        }
    }
}

From source file:com.android.tools.lint.checks.SecureRandomDetector.java

License:Apache License

private static void checkValidSetSeed(ClassContext context, MethodInsnNode call) {
    assert call.name.equals(SET_SEED);

    // Make sure the argument passed is not a literal
    AbstractInsnNode prev = LintUtils.getPrevInstruction(call);
    if (prev == null) {
        return;/*from  www .j  a v a 2  s.  c o  m*/
    }
    int opcode = prev.getOpcode();
    if (opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || opcode == Opcodes.LDC) {
        context.report(ISSUE, context.getLocation(call),
                "Do not call setSeed() on a SecureRandom with a fixed seed: "
                        + "it is not secure. Use getSeed().",
                null);
    } else if (opcode == Opcodes.INVOKESTATIC) {
        String methodName = ((MethodInsnNode) prev).name;
        if (methodName.equals("currentTimeMillis") || methodName.equals("nanoTime")) {
            context.report(ISSUE, context.getLocation(call),
                    "It is dangerous to seed SecureRandom with the current time because "
                            + "that value is more predictable to an attacker than the default seed.",
                    null);
        }
    }
}

From source file:com.android.tools.lint.checks.SecureRandomGeneratorDetector.java

License:Apache License

@Override
public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method,
        @NonNull MethodInsnNode call) {//from w  w  w . j  av  a2s .co m
    if (mIgnore) {
        return;
    }

    String owner = call.owner;
    String name = call.name;

    // Look for the workaround code: if we see a Class.forName on the harmony NativeCrypto,
    // we'll consider that a sign.

    if (name.equals(FOR_NAME)) {
        if (call.getOpcode() != Opcodes.INVOKESTATIC || !owner.equals(JAVA_LANG_CLASS)) {
            return;
        }
        AbstractInsnNode prev = LintUtils.getPrevInstruction(call);
        if (prev instanceof LdcInsnNode) {
            Object cst = ((LdcInsnNode) prev).cst;
            //noinspection SpellCheckingInspection
            if (cst instanceof String && "org.apache.harmony.xnet.provider.jsse.NativeCrypto".equals(cst)) {
                mIgnore = true;
            }
        }
        return;
    }

    // Look for calls that probably require a properly initialized random number generator.
    assert owner.equals(JAVAX_CRYPTO_KEY_GENERATOR) || owner.equals(JAVA_SECURITY_KEY_PAIR_GENERATOR)
            || owner.equals(JAVAX_CRYPTO_KEY_AGREEMENT) || owner.equals(OWNER_SECURE_RANDOM)
            || owner.equals(JAVAX_CRYPTO_CIPHER) || owner.equals(JAVAX_CRYPTO_SIGNATURE)
            || owner.equals(JAVAX_NET_SSL_SSLENGINE) : owner;
    boolean warn = false;

    if (owner.equals(JAVAX_CRYPTO_SIGNATURE)) {
        warn = name.equals(INIT_SIGN);
    } else if (owner.equals(JAVAX_CRYPTO_CIPHER)) {
        if (name.equals(INIT)) {
            int arity = getDescArity(call.desc);
            AbstractInsnNode node = call;
            for (int i = 0; i < arity; i++) {
                node = LintUtils.getPrevInstruction(node);
                if (node == null) {
                    break;
                }
            }
            if (node != null) {
                int opcode = node.getOpcode();
                if (opcode == Opcodes.ICONST_3 || // Cipher.WRAP_MODE
                        opcode == Opcodes.ICONST_1) { // Cipher.ENCRYPT_MODE
                    warn = true;
                }
            }
        }
    } else if (name.equals(GET_INSTANCE) || name.equals(CONSTRUCTOR_NAME) || name.equals(WRAP)
            || name.equals(UNWRAP)) { // For SSLEngine
        warn = true;
    }

    if (warn) {
        if (mLocation != null) {
            return;
        }
        if (context.getMainProject().getMinSdk() > 18) {
            // Fix no longer needed
            mIgnore = true;
            return;
        }

        if (context.getDriver().isSuppressed(ISSUE, classNode, method, call)) {
            mIgnore = true;
        } else {
            mLocation = context.getLocation(call);
        }
    }
}

From source file:com.asakusafw.dag.compiler.builtin.LoggingOperatorGenerator.java

License:Apache License

private ClassData generateClass(Context context, UserOperator operator, ClassDescription target) {
    String report = getReportName(context, operator);

    OperatorInput input = operator.getInput(Logging.ID_INPUT);
    OperatorOutput output = operator.getOutput(Logging.ID_OUTPUT);

    ClassWriter writer = newWriter(target, Object.class, Result.class);
    FieldRef impl = defineOperatorField(writer, operator, target);
    Map<OperatorProperty, FieldRef> map = defineConstructor(context, operator, target, writer, method -> {
        setOperatorField(method, operator, impl);
    });/*from  w  w w.j a va  2s.  c  o  m*/
    defineResultAdd(writer, method -> {
        cast(method, 1, input.getDataType());

        List<ValueRef> arguments = new ArrayList<>();
        arguments.add(impl);
        arguments.add(new LocalVarRef(Opcodes.ALOAD, 1));
        appendSecondaryInputs(arguments::add, operator, map::get);
        appendArguments(arguments::add, operator, map::get);
        invoke(method, context, operator, arguments);

        method.visitMethodInsn(Opcodes.INVOKESTATIC, typeOf(Report.class).getInternalName(), report,
                Type.getMethodDescriptor(Type.VOID_TYPE, typeOf(String.class)), false);

        method.visitVarInsn(Opcodes.ALOAD, 0);
        getField(method, map.get(output));
        method.visitVarInsn(Opcodes.ALOAD, 1);
        invokeResultAdd(method);
    });
    return new ClassData(target, writer::toByteArray);
}

From source file:com.asakusafw.dag.compiler.builtin.SummarizeOperatorGenerator.java

License:Apache License

private static void checkNull(MethodVisitor method, ClassDescription target, PropertyReference property,
        LocalVarRef variable, Set<PropertyReference> finished) {
    if (finished.contains(property)) {
        return;//from ww  w.j a v a 2 s  . c o m
    }
    finished.add(property);

    method.visitInsn(Opcodes.DUP);
    variable.load(method);
    getConst(method, property.getName().toName());
    method.visitMethodInsn(Opcodes.INVOKESTATIC, typeOf(target).getInternalName(), METHOD_CHECK_NON_NULL,
            Type.getMethodDescriptor(Type.VOID_TYPE, typeOf(ValueOption.class), typeOf(Object.class),
                    typeOf(String.class)),
            false);
}