Search in sources :

Example 86 with MethodNode

use of org.objectweb.asm.tree.MethodNode in project atlas by alibaba.

the class TBIncrementalChangeVisitor method addDispatchMethod.

/**
 * To each class, add the dispatch method called by the original code that acts as a trampoline to
 * invoke the changed methods.
 * <p>
 * Pseudo code:
 * <code>
 *   Object access$dispatch(String name, object[] args) {
 *      if (name.equals(
 *          "firstMethod.(L$type;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;")) {
 *        return firstMethod(($type)arg[0], (String)arg[1], arg[2]);
 *      }
 *      if (name.equals("secondMethod.(L$type;Ljava/lang/String;I;)V")) {
 *        secondMethod(($type)arg[0], (String)arg[1], (int)arg[2]);
 *        return;
 *      }
 *      ...
 *      StringBuilder $local1 = new StringBuilder();
 *      $local1.append("Method not found ");
 *      $local1.append(name);
 *      $local1.append(" in " + visitedClassName +
 *          "$dispatch implementation, restart the application");
 *      throw new $package/InstantReloadException($local1.toString());
 *   }
 * </code>
 */
private void addDispatchMethod() {
    int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_VARARGS;
    Method m = new Method("ipc$dispatch", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;");
    MethodVisitor visitor = super.visitMethod(access, m.getName(), m.getDescriptor(), null, null);
    final GeneratorAdapter mv = new GeneratorAdapter(access, m, visitor);
    if (TRACING_ENABLED) {
        mv.push("Redirecting ");
        mv.loadArg(0);
        trace(mv, 2);
    }
    List<MethodNode> allMethods = new ArrayList<>();
    // if invoked which should never happen.
    if (!instantRunDisabled) {
        // noinspection unchecked
        allMethods.addAll(classNode.methods);
        allMethods.addAll(addedMethods);
    }
    final Map<String, MethodNode> methods = new HashMap<>();
    for (MethodNode methodNode : allMethods) {
        if (methodNode.name.equals("<clinit>") || methodNode.name.equals("<init>")) {
            continue;
        }
        if (!isAccessCompatibleWithInstantRun(methodNode.access)) {
            continue;
        }
        methods.put(methodNode.name + "." + methodNode.desc, methodNode);
    }
    new StringSwitch() {

        @Override
        void visitString() {
            mv.visitVarInsn(Opcodes.ALOAD, 1);
        }

        @Override
        void visitCase(String methodName) {
            MethodNode methodNode = methods.get(methodName);
            String name = methodNode.name;
            boolean isStatic = (methodNode.access & Opcodes.ACC_STATIC) != 0;
            String newDesc = computeOverrideMethodDesc(methodNode.desc, isStatic);
            if (TRACING_ENABLED) {
                trace(mv, "M: " + name + " P:" + newDesc);
            }
            Type[] args = Type.getArgumentTypes(newDesc);
            int argc = 0;
            for (Type t : args) {
                mv.visitVarInsn(Opcodes.ALOAD, 2);
                mv.push(argc);
                mv.visitInsn(Opcodes.AALOAD);
                ByteCodeUtils.unbox(mv, t);
                argc++;
            }
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, visitedClassName + OVERRIDE_SUFFIX, isStatic ? computeOverrideMethodName(name, methodNode.desc) : name, newDesc, false);
            Type ret = Type.getReturnType(methodNode.desc);
            if (ret.getSort() == Type.VOID) {
                mv.visitInsn(Opcodes.ACONST_NULL);
            } else {
                mv.box(ret);
            }
            mv.visitInsn(Opcodes.ARETURN);
        }

        @Override
        void visitDefault() {
            writeMissingMessageWithHash(mv, visitedClassName);
        }
    }.visit(mv, methods.keySet());
    mv.visitMaxs(0, 0);
    mv.visitEnd();
    super.visitEnd();
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Method(org.objectweb.asm.commons.Method) MethodNode(org.objectweb.asm.tree.MethodNode) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter)

Example 87 with MethodNode

use of org.objectweb.asm.tree.MethodNode in project atlas by alibaba.

the class TBIncrementalSupportVisitor method createDispatchingThis.

/**
 * Inserts a trampoline to this class so that the updated methods can make calls to
 * constructors.
 *
 * <p>
 * Pseudo code for this trampoline:
 * <code>
 *   ClassName(Object[] args, Marker unused) {
 *      String name = (String) args[0];
 *      if (name.equals(
 *          "java/lang/ClassName.(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;")) {
 *        this((String)arg[1], arg[2]);
 *        return
 *      }
 *      if (name.equals("SuperClassName.(Ljava/lang/String;I)V")) {
 *        super((String)arg[1], (int)arg[2]);
 *        return;
 *      }
 *      ...
 *      StringBuilder $local1 = new StringBuilder();
 *      $local1.append("Method not found ");
 *      $local1.append(name);
 *      $local1.append(" in " $classType $super implementation");
 *      throw new $package/InstantReloadException($local1.toString());
 *   }
 * </code>
 */
private void createDispatchingThis() {
    // Gather all methods from itself and its superclasses to generate a giant constructor
    // implementation.
    // This will work fine as long as we don't support adding constructors to classes.
    final Map<String, MethodNode> uniqueMethods = new HashMap<>();
    addAllNewConstructors(uniqueMethods, classNode, true);
    for (ClassNode parentNode : parentNodes) {
        addAllNewConstructors(uniqueMethods, parentNode, false);
    }
    int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
    Method m = new Method(ByteCodeUtils.CONSTRUCTOR, ALI_DISPATCHING_THIS_SIGNATURE);
    MethodVisitor visitor = super.visitMethod(0, m.getName(), m.getDescriptor(), null, null);
    final GeneratorAdapter mv = new GeneratorAdapter(access, m, visitor);
    mv.visitCode();
    // Mark this code as redirection code
    Label label = new Label();
    mv.visitLineNumber(0, label);
    // Get and store the constructor canonical name.
    mv.visitVarInsn(Opcodes.ALOAD, 1);
    mv.push(1);
    mv.visitInsn(Opcodes.AALOAD);
    mv.unbox(Type.getType("Ljava/lang/String;"));
    final int constructorCanonicalName = mv.newLocal(Type.getType("Ljava/lang/String;"));
    mv.storeLocal(constructorCanonicalName);
    new StringSwitch() {

        @Override
        void visitString() {
            mv.loadLocal(constructorCanonicalName);
        }

        @Override
        void visitCase(String canonicalName) {
            MethodNode methodNode = uniqueMethods.get(canonicalName);
            String owner = canonicalName.split("\\.")[0];
            // Parse method arguments and
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            Type[] args = Type.getArgumentTypes(methodNode.desc);
            int argc = 1;
            for (Type t : args) {
                mv.visitVarInsn(Opcodes.ALOAD, 1);
                mv.push(argc + 1);
                mv.visitInsn(Opcodes.AALOAD);
                ByteCodeUtils.unbox(mv, t);
                argc++;
            }
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, ByteCodeUtils.CONSTRUCTOR, methodNode.desc, false);
            mv.visitInsn(Opcodes.RETURN);
        }

        @Override
        void visitDefault() {
            // mv.visitInsn(Opcodes.RETURN);
            writeMissingMessageWithHash(mv, visitedClassName);
        }

        @Override
        void visit(GeneratorAdapter mv, Set<String> strings) {
            super.visit(mv, strings);
        }
    }.visit(mv, uniqueMethods.keySet());
    mv.visitMaxs(1, 3);
    mv.visitEnd();
}
Also used : ClassNode(org.objectweb.asm.tree.ClassNode) Method(org.objectweb.asm.commons.Method) MethodNode(org.objectweb.asm.tree.MethodNode) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter)

Example 88 with MethodNode

use of org.objectweb.asm.tree.MethodNode in project robolectric by robolectric.

the class ClassInstrumentor method addSetToSparseArray.

// See https://github.com/robolectric/robolectric/issues/6840
// Adds Set(int, object) to android.util.SparseArray.
private void addSetToSparseArray(MutableClass mutableClass) {
    for (MethodNode method : mutableClass.getMethods()) {
        if ("set".equals(method.name)) {
            return;
        }
    }
    MethodNode setFunction = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "set", "(ILjava/lang/Object;)V", "(ITE;)V", null);
    RobolectricGeneratorAdapter generator = new RobolectricGeneratorAdapter(setFunction);
    generator.loadThis();
    generator.loadArg(0);
    generator.loadArg(1);
    generator.invokeVirtual(mutableClass.classType, new Method("put", "(ILjava/lang/Object;)V"));
    generator.returnValue();
    mutableClass.addMethod(setFunction);
}
Also used : MethodNode(org.objectweb.asm.tree.MethodNode) Method(org.objectweb.asm.commons.Method)

Example 89 with MethodNode

use of org.objectweb.asm.tree.MethodNode in project robolectric by robolectric.

the class ClassInstrumentor method addNoArgsConstructor.

private static void addNoArgsConstructor(MutableClass mutableClass) {
    if (!mutableClass.foundMethods.contains("<init>()V")) {
        MethodNode defaultConstructor = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "<init>", "()V", "()V", null);
        RobolectricGeneratorAdapter generator = new RobolectricGeneratorAdapter(defaultConstructor);
        generator.loadThis();
        generator.visitMethodInsn(Opcodes.INVOKESPECIAL, mutableClass.classNode.superName, "<init>", "()V", false);
        generator.loadThis();
        generator.invokeVirtual(mutableClass.classType, new Method(ROBO_INIT_METHOD_NAME, "()V"));
        generator.returnValue();
        mutableClass.addMethod(defaultConstructor);
    }
}
Also used : MethodNode(org.objectweb.asm.tree.MethodNode) Method(org.objectweb.asm.commons.Method)

Example 90 with MethodNode

use of org.objectweb.asm.tree.MethodNode in project robolectric by robolectric.

the class ClassInstrumentor method addRoboInitMethod.

/**
 * Generates code like this:
 *
 * <pre>
 * protected void $$robo$init() {
 *   if (__robo_data__ == null) {
 *     __robo_data__ = RobolectricInternals.initializing(this);
 *   }
 * }
 * </pre>
 */
private void addRoboInitMethod(MutableClass mutableClass) {
    MethodNode initMethodNode = new MethodNode(Opcodes.ACC_PROTECTED | Opcodes.ACC_SYNTHETIC, ROBO_INIT_METHOD_NAME, "()V", null, null);
    RobolectricGeneratorAdapter generator = new RobolectricGeneratorAdapter(initMethodNode);
    Label alreadyInitialized = new Label();
    // this
    generator.loadThis();
    generator.getField(mutableClass.classType, ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, // contents of __robo_data__
    OBJECT_TYPE);
    generator.ifNonNull(alreadyInitialized);
    // this
    generator.loadThis();
    // this, this
    generator.loadThis();
    writeCallToInitializing(mutableClass, generator);
    // this, __robo_data__
    generator.putField(mutableClass.classType, ShadowConstants.CLASS_HANDLER_DATA_FIELD_NAME, OBJECT_TYPE);
    generator.mark(alreadyInitialized);
    generator.returnValue();
    mutableClass.addMethod(initMethodNode);
}
Also used : MethodNode(org.objectweb.asm.tree.MethodNode) Label(org.objectweb.asm.Label)

Aggregations

MethodNode (org.objectweb.asm.tree.MethodNode)322 ClassNode (org.objectweb.asm.tree.ClassNode)123 Test (org.junit.Test)94 AbstractInsnNode (org.objectweb.asm.tree.AbstractInsnNode)59 ClassReader (org.objectweb.asm.ClassReader)57 InsnList (org.objectweb.asm.tree.InsnList)49 MethodInsnNode (org.objectweb.asm.tree.MethodInsnNode)47 Label (org.objectweb.asm.Label)44 VarInsnNode (org.objectweb.asm.tree.VarInsnNode)41 InsnNode (org.objectweb.asm.tree.InsnNode)34 ClassWriter (org.objectweb.asm.ClassWriter)26 FieldNode (org.objectweb.asm.tree.FieldNode)26 JumpInsnNode (org.objectweb.asm.tree.JumpInsnNode)26 ArrayList (java.util.ArrayList)24 FieldInsnNode (org.objectweb.asm.tree.FieldInsnNode)24 LdcInsnNode (org.objectweb.asm.tree.LdcInsnNode)21 LabelNode (org.objectweb.asm.tree.LabelNode)19 TypeInsnNode (org.objectweb.asm.tree.TypeInsnNode)19 List (java.util.List)17 Type (org.objectweb.asm.Type)17