Example usage for org.objectweb.asm Type VOID

List of usage examples for org.objectweb.asm Type VOID

Introduction

In this page you can find the example usage for org.objectweb.asm Type VOID.

Prototype

int VOID

To view the source code for org.objectweb.asm Type VOID.

Click Source Link

Document

The sort of the void type.

Usage

From source file:ch.eiafr.cojac.instrumenters.FloatProxyMethod.java

License:Apache License

private Object[] addReturnTypeTo(Object[] stackContent, Type returnType) {
    int sort = returnType.getSort();
    if (sort == Type.VOID)
        return stackContent;
    if (sort == Type.ARRAY || sort == Type.OBJECT)
        return addTo(stackContent, returnType.getInternalName());
    // else primitive type
    switch (sort) {
    case Type.BOOLEAN:
    case Type.BYTE:
    case Type.CHAR:
    case Type.SHORT:
    case Type.INT:
        return addTo(stackContent, Opcodes.INTEGER);
    case Type.LONG:
        return addTo(stackContent, Opcodes.LONG);
    //return addTo(addTo(stackContent, Opcodes.LONG), Opcodes.TOP);
    }//from   www.  j a  va2  s .  co  m
    return stackContent;
}

From source file:cl.inria.stiq.db.structure.impl.StructureDatabase.java

License:Open Source License

public static ITypeInfo getType(IMutableStructureDatabase aStructureDatabase, String aName,
        boolean aCreateIfAbsent, boolean aFailIfAbsent) {
    aName = transformClassName(aName);/*w  w w . java 2s. c  o m*/
    Type theType = Type.getType(aName);
    switch (theType.getSort()) {
    case Type.OBJECT: {
        String theClassName = theType.getClassName();
        return aCreateIfAbsent ? aStructureDatabase.getNewClass(theClassName)
                : aStructureDatabase.getClass(theClassName, aFailIfAbsent);
    }

    case Type.ARRAY: {
        ITypeInfo theElementType = getType(aStructureDatabase, theType.getElementType().getDescriptor(),
                aCreateIfAbsent, aFailIfAbsent);

        int theDimensions = theType.getDimensions();

        return new ArrayTypeInfo(null, // That should be safe... if there is a problem we'll see what we do
                theElementType, theDimensions);
    }

    case Type.VOID:
        return PrimitiveTypeInfo.VOID;
    case Type.BOOLEAN:
        return PrimitiveTypeInfo.BOOLEAN;
    case Type.BYTE:
        return PrimitiveTypeInfo.BYTE;
    case Type.CHAR:
        return PrimitiveTypeInfo.CHAR;
    case Type.DOUBLE:
        return PrimitiveTypeInfo.DOUBLE;
    case Type.FLOAT:
        return PrimitiveTypeInfo.FLOAT;
    case Type.INT:
        return PrimitiveTypeInfo.INT;
    case Type.LONG:
        return PrimitiveTypeInfo.LONG;
    case Type.SHORT:
        return PrimitiveTypeInfo.SHORT;

    default:
        // This is not a "normal" failure, so always throw exception
        throw new RuntimeException("Not handled: " + theType);
    }
}

From source file:cl.inria.stiq.instrumenter.BCIUtils.java

License:Open Source License

/**
 * Returns the sort of data (as defined by {@link Type} that correponds to
 * the given opcode.//w  w  w .  ja  v  a 2  s. co  m
 */
public static int getSort(int aOpcode) {
    switch (aOpcode) {
    case AALOAD:
    case AASTORE:
    case ACONST_NULL:
    case ALOAD:
    case ANEWARRAY:
    case ARETURN:
    case ASTORE:
    case IF_ACMPEQ:
    case IF_ACMPNE:
        return Type.OBJECT;

    case BALOAD:
    case BASTORE:
        return Type.BYTE;

    case CALOAD:
    case CASTORE:
        return Type.CHAR;

    case DADD:
    case DALOAD:
    case DASTORE:
    case DCMPG:
    case DCMPL:
    case DCONST_0:
    case DCONST_1:
    case DDIV:
    case DLOAD:
    case DMUL:
    case DNEG:
    case DREM:
    case DRETURN:
    case DSTORE:
    case DSUB:
        return Type.DOUBLE;

    case FADD:
    case FALOAD:
    case FASTORE:
    case FCMPG:
    case FCMPL:
    case FCONST_0:
    case FCONST_1:
    case FCONST_2:
    case FDIV:
    case FLOAD:
    case FMUL:
    case FNEG:
    case FREM:
    case FRETURN:
    case FSTORE:
    case FSUB:
        return Type.FLOAT;

    case BIPUSH:
    case IADD:
    case IALOAD:
    case IAND:
    case IASTORE:
    case ICONST_0:
    case ICONST_1:
    case ICONST_2:
    case ICONST_3:
    case ICONST_4:
    case ICONST_5:
    case ICONST_M1:
    case IDIV:
    case IF_ICMPEQ:
    case IF_ICMPGE:
    case IF_ICMPGT:
    case IF_ICMPLE:
    case IF_ICMPLT:
    case IF_ICMPNE:
    case IINC:
    case ILOAD:
    case IMUL:
    case INEG:
    case IOR:
    case IREM:
    case IRETURN:
    case ISHL:
    case ISHR:
    case ISTORE:
    case ISUB:
    case IUSHR:
    case IXOR:
        return Type.INT;

    case LADD:
    case LALOAD:
    case LAND:
    case LASTORE:
    case LCMP:
    case LCONST_0:
    case LCONST_1:
    case LDIV:
    case LLOAD:
    case LMUL:
    case LNEG:
    case LOR:
    case LREM:
    case LRETURN:
    case LSHL:
    case LSHR:
    case LSTORE:
    case LSUB:
    case LUSHR:
    case LXOR:
        return Type.LONG;

    case SALOAD:
    case SASTORE:
    case SIPUSH:
        return Type.SHORT;

    case RETURN:
        return Type.VOID;

    default:
        return -1;
    }
}

From source file:cl.inria.stiq.instrumenter.BCIUtils.java

License:Open Source License

/**
 * Returns a one-character descriptor for the given type. These are the classical
 * JVM descriptor characters, with just L for reference types.
 *///w  w w  . j a v  a  2 s .co  m
public static char getCompleteSigForSort(int aSort) {
    switch (aSort) {
    case Type.ARRAY:
    case Type.OBJECT:
        return 'L';

    case Type.BOOLEAN:
        return 'Z';
    case Type.BYTE:
        return 'B';
    case Type.CHAR:
        return 'C';
    case Type.INT:
        return 'I';
    case Type.SHORT:
        return 'S';
    case Type.DOUBLE:
        return 'D';
    case Type.FLOAT:
        return 'F';
    case Type.LONG:
        return 'J';
    case Type.VOID:
        return 'V';

    default:
        throw new RuntimeException("Not handled: " + aSort);
    }
}

From source file:com.android.build.gradle.internal.incremental.ConstructorBuilder.java

License:Apache License

/**
 * Deconstruct a constructor into its components and adds the necessary code to link the components
 * later. This code is not valid java, but it can be expressed in bytecode. In essence for this constructor:
 * <pre>{@code/*from w w  w.  j  av a 2 s.co  m*/
 *   <init>(int x) {
 *     int a = 2;
 *     super(int b = 3, x = 1, expr2() ? 3 : a++)
 *     doSomething(x + a)
 *   }
 * }</pre>
 * it creates two parts:
 * <pre>{@code
 *   Object[] init$args(Clazz this, int x, Object[] locals) { // this is always null here
 *     int a = locals[0];
 *     int b = 3;
 *     Object[] args = new Object[3];
 *     args[0] = b;
 *     args[1] = (x = 1)
 *     args[2] = expr2() ? 3 : a++;
 *     locals = new Object[3]; // The arguments + the locals
 *     locals[0] = NULL;
 *     locals[1] = x;
 *     locals[2] = new Object[2];
 *     locals[2][0] = a;
 *     locals[2][1] = b;
 *     return new Object[] {locals, "myclass.<init>(I;I;)V", args};
 *   }
 *
 *   void init$body(int x, Object[] locals) {
 *     int a = locals[0];
 *     int b = locals[1];
 *     doSomething(x + a);
 *   }
 * }</pre>
 *
 * @param owner the owning class.
 * @param method the constructor method.
 */
@NonNull
public static Constructor build(@NonNull String owner, @NonNull MethodNode method) {
    // Basic interpreter uses BasicValue.REFERENCE_VALUE for all object types. However
    // we need to distinguish one in particular. The value of the local variable 0, ie. the
    // uninitialized this. By doing it this way we ensure that whenever there is a ALOAD_0
    // a LocalValue instance will be on the stack.
    BasicInterpreter interpreter = new BasicInterpreter() {
        boolean done = false;

        @Override
        // newValue is called first to initialize the frame values of all the local variables
        // we intercept the first one to create our own special value.
        public BasicValue newValue(Type type) {
            if (type == null) {
                return BasicValue.UNINITIALIZED_VALUE;
            } else if (type.getSort() == Type.VOID) {
                return null;
            } else {
                // If this is the first value created (i.e. the first local variable)
                // we use a special marker.
                BasicValue ret = done ? super.newValue(type) : new LocalValue(type);
                done = true;
                return ret;
            }
        }
    };

    Analyzer analyzer = new Analyzer(interpreter);
    AbstractInsnNode[] instructions = method.instructions.toArray();
    try {
        Frame[] frames = analyzer.analyze(owner, method);
        if (frames.length != instructions.length) {
            // Should never happen.
            throw new IllegalStateException("The number of frames is not equals to the number of instructions");
        }
        int stackAtThis = -1;
        boolean poppedThis = false;
        int firstLocal = 1;
        for (Type type : Type.getArgumentTypes(method.desc)) {
            firstLocal += type.getSize();
        }

        LinkedHashSet<LocalVariable> variables = new LinkedHashSet<LocalVariable>();
        VarInsnNode lastThis = null;
        int localsAtLastThis = 0;
        // Records the most recent line number encountered. For javac, there should always be
        // a line number node before the call of interest to this(...) or super(...). For robustness,
        // -1 is recorded as a sentinel to indicate this assumption didn't hold. Upstream consumers
        // should check for -1 and recover in a reasonable way (for example, don't set the line
        // number in generated code).
        int recentLine = -1;
        for (int i = 0; i < instructions.length; i++) {
            AbstractInsnNode insn = instructions[i];
            Frame frame = frames[i];
            if (frame.getStackSize() < stackAtThis) {
                poppedThis = true;
            }
            if (insn instanceof MethodInsnNode) {
                // TODO: Do we need to check that the stack is empty after this super call?
                MethodInsnNode methodhInsn = (MethodInsnNode) insn;
                Type[] types = Type.getArgumentTypes(methodhInsn.desc);
                Value value = frame.getStack(frame.getStackSize() - types.length - 1);
                if (value instanceof LocalValue && methodhInsn.name.equals("<init>")) {
                    if (poppedThis) {
                        throw new IllegalStateException("Unexpected constructor structure.");
                    }
                    return split(owner, method, lastThis, methodhInsn, recentLine,
                            new ArrayList<LocalVariable>(variables), localsAtLastThis);
                }
            } else if (insn instanceof VarInsnNode) {
                VarInsnNode var = (VarInsnNode) insn;
                if (var.var == 0) {
                    lastThis = var;
                    localsAtLastThis = variables.size();
                    stackAtThis = frame.getStackSize();
                    poppedThis = false;
                }
                Type type = ByteCodeUtils.getTypeForStoreOpcode(var.getOpcode());
                if (type != null && var.var >= firstLocal) {
                    // Variables are equals based on their number, so they will be added
                    // to the set only if they are new, and in the order they are seen.
                    variables.add(new LocalVariable(type, var.var));
                }
            } else if (insn instanceof LineNumberNode) {
                // Record the most recent line number encountered so that call to this(...)
                // or super(...) has line number information. Ultimately used to emit a line
                // number in the generated code.
                LineNumberNode lineNumberNode = (LineNumberNode) insn;
                recentLine = lineNumberNode.line;
            }
        }
        throw new IllegalStateException("Unexpected constructor structure.");
    } catch (AnalyzerException e) {
        throw new IllegalStateException(e);
    }
}

From source file:com.android.build.gradle.internal.incremental.ConstructorDelegationDetector.java

License:Apache License

/**
 * Deconstruct a constructor into its components and adds the necessary code to link the components
 * later. The generated bytecode does not correspond exactly to this code, but in essence, for
 * a constructor of this form:/*from   w  w w. j ava 2 s .  c  om*/
 * <p/>
 * <code>
 *   <init>(int x) {
 *     super(x = 1, expr2() ? 3 : 7)
 *     doSomething(x)
 *   }
 * </code>
 * <p/>
 * it creates the two parts:
 * <code>
 *   Object[] init$args(Object[] locals, int x) {
 *     Object[] args = new Object[2];
 *     args[0] = (x = 1)
 *     args[1] = expr2() ? 3 : 7;
 *     locals[0] = x;
 *     return new Object[] {"myclass.<init>(I;I;)V", args};
 *   }
 *
 *   void init$body(int x) {
 *     doSomething(x);
 *   }
 * </code>
 *
 * @param owner the owning class.
 * @param method the constructor method.
 */
@NonNull
public static Constructor deconstruct(@NonNull String owner, @NonNull MethodNode method) {
    // Basic interpreter uses BasicValue.REFERENCE_VALUE for all object types. However
    // we need to distinguish one in particular. The value of the local variable 0, ie. the
    // uninitialized this. By doing it this way we ensure that whenever there is a ALOAD_0
    // a LocalValue instance will be on the stack.
    BasicInterpreter interpreter = new BasicInterpreter() {
        boolean done = false;

        @Override
        // newValue is called first to initialize the frame values of all the local variables
        // we intercept the first one to create our own special value.
        public BasicValue newValue(Type type) {
            if (type == null) {
                return BasicValue.UNINITIALIZED_VALUE;
            } else if (type.getSort() == Type.VOID) {
                return null;
            } else {
                // If this is the first value created (i.e. the first local variable)
                // we use a special marker.
                BasicValue ret = done ? super.newValue(type) : new LocalValue(type);
                done = true;
                return ret;
            }
        }
    };

    Analyzer analyzer = new Analyzer(interpreter);
    AbstractInsnNode[] instructions = method.instructions.toArray();
    try {
        Frame[] frames = analyzer.analyze(owner, method);
        if (frames.length != instructions.length) {
            // Should never happen.
            throw new IllegalStateException("The number of frames is not equals to the number of instructions");
        }
        VarInsnNode lastThis = null;
        int stackAtThis = -1;
        boolean poppedThis = false;
        // Records the most recent line number encountered. For javac, there should always be
        // a line number node before the call of interest to this(...) or super(...). For robustness,
        // -1 is recorded as a sentinel to indicate this assumption didn't hold. Upstream consumers
        // should check for -1 and recover in a reasonable way (for example, don't set the line
        // number in generated code).
        int recentLine = -1;
        for (int i = 0; i < instructions.length; i++) {
            AbstractInsnNode insn = instructions[i];
            Frame frame = frames[i];
            if (frame.getStackSize() < stackAtThis) {
                poppedThis = true;
            }
            if (insn instanceof MethodInsnNode) {
                // TODO: Do we need to check that the stack is empty after this super call?
                MethodInsnNode methodhInsn = (MethodInsnNode) insn;
                Type[] types = Type.getArgumentTypes(methodhInsn.desc);
                Value value = frame.getStack(frame.getStackSize() - types.length - 1);
                if (value instanceof LocalValue && methodhInsn.name.equals("<init>")) {
                    if (poppedThis) {
                        throw new IllegalStateException("Unexpected constructor structure.");
                    }
                    return split(owner, method, lastThis, methodhInsn, recentLine);
                }
            } else if (insn instanceof VarInsnNode) {
                VarInsnNode var = (VarInsnNode) insn;
                if (var.var == 0) {
                    lastThis = var;
                    stackAtThis = frame.getStackSize();
                    poppedThis = false;
                }
            } else if (insn instanceof LineNumberNode) {
                // Record the most recent line number encountered so that call to this(...)
                // or super(...) has line number information. Ultimately used to emit a line
                // number in the generated code.
                LineNumberNode lineNumberNode = (LineNumberNode) insn;
                recentLine = lineNumberNode.line;
            }
        }
        throw new IllegalStateException("Unexpected constructor structure.");
    } catch (AnalyzerException e) {
        throw new IllegalStateException(e);
    }
}

From source file:com.android.build.gradle.internal.incremental.IncrementalChangeVisitor.java

License:Apache License

/**
 * To each class, add the dispatch method called by the original code that acts as a trampoline to
 * invoke the changed methods.//from   ww w .j  av  a2 s  . com
 * <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("access$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 we are disabled, do not generate any dispatch, the method will throw an exception
    // 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",
                    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();
}

From source file:com.android.build.gradle.internal.incremental.IncrementalSupportVisitor.java

License:Apache License

/***
 * Inserts a trampoline to this class so that the updated methods can make calls to super
 * class methods./* w ww  . j ava2 s  . co m*/
 * <p>
 * Pseudo code for this trampoline:
 * <code>
 *   Object access$super($classType instance, String name, object[] args) {
 *      switch(name) {
 *          case "firstMethod.(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;":
 *            return super~instance.firstMethod((String)arg[0], arg[1]);
 *          case "secondMethod.(Ljava/lang/String;I)V":
 *            return super~instance.firstMethod((String)arg[0], arg[1]);
 *
 *          default:
 *            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 createAccessSuper() {
    int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_VARARGS;
    Method m = new Method("access$super",
            "(L" + visitedClassName + ";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);

    // Gather all methods from itself and its superclasses to generate a giant access$super
    // implementation.
    // This will work fine as long as we don't support adding methods to a class.
    final Map<String, MethodReference> uniqueMethods = new HashMap<>();
    if (parentNodes.isEmpty()) {
        // if we cannot determine the parents for this class, let's blindly add all the
        // method of the current class as a gateway to a possible parent version.
        addAllNewMethods(classNode, classNode, uniqueMethods);
    } else {
        // otherwise, use the parent list.
        for (ClassNode parentNode : parentNodes) {
            addAllNewMethods(classNode, parentNode, uniqueMethods);
        }
    }

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

        @Override
        void visitCase(String methodName) {
            MethodReference methodRef = uniqueMethods.get(methodName);

            mv.visitVarInsn(Opcodes.ALOAD, 0);

            Type[] args = Type.getArgumentTypes(methodRef.method.desc);
            int argc = 0;
            for (Type t : args) {
                mv.visitVarInsn(Opcodes.ALOAD, 2);
                mv.push(argc);
                mv.visitInsn(Opcodes.AALOAD);
                ByteCodeUtils.unbox(mv, t);
                argc++;
            }

            if (TRACING_ENABLED) {
                trace(mv, "super selected ", methodRef.owner.name, methodRef.method.name,
                        methodRef.method.desc);
            }
            String parentName = findParentClassForMethod(methodRef);
            logger.verbose("Generating access$super for %1$s recev %2$s", methodRef.method.name, parentName);

            // Call super on the other object, yup this works cos we are on the right place to
            // call from.
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, parentName, methodRef.method.name, methodRef.method.desc,
                    false);

            Type ret = Type.getReturnType(methodRef.method.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, uniqueMethods.keySet());

    mv.visitMaxs(0, 0);
    mv.visitEnd();
}

From source file:com.android.build.gradle.internal2.incremental.ConstructorBuilder.java

License:Apache License

/**
 * Deconstruct a constructor into its components and adds the necessary code to link the components
 * later. This code is not valid java, but it can be expressed in bytecode. In essence for this constructor:
 * <pre>{@code/*from  ww  w. ja  va2s  .  com*/
 *   <init>(int x) {
 *     int a = 2;
 *     super(int b = 3, x = 1, expr2() ? 3 : a++)
 *     doSomething(x + a)
 *   }
 * }</pre>
 * it creates two parts:
 * <pre>{@code
 *   Object[] init$args(Clazz this, int x, Object[] locals) { // this is always null here
 *     int a = locals[0];
 *     int b = 3;
 *     Object[] args = new Object[3];
 *     args[0] = b;
 *     args[1] = (x = 1)
 *     args[2] = expr2() ? 3 : a++;
 *     locals = new Object[3]; // The arguments + the locals
 *     locals[0] = NULL;
 *     locals[1] = x;
 *     locals[2] = new Object[2];
 *     locals[2][0] = a;
 *     locals[2][1] = b;
 *     return new Object[] {locals, "myclass.<init>(I;I;)V", args};
 *   }
 *
 *   void init$body(int x, Object[] locals) {
 *     int a = locals[0];
 *     int b = locals[1];
 *     doSomething(x + a);
 *   }
 * }</pre>
 *
 * @param owner the owning class.
 * @param method the constructor method.
 */
@NonNull
public static Constructor build(@NonNull String owner, @NonNull MethodNode method) {
    // Basic interpreter uses BasicValue.REFERENCE_VALUE for all object types. However
    // we need to distinguish one in particular. The value of the local variable 0, ie. the
    // uninitialized this. By doing it this way we ensure that whenever there is a ALOAD_0
    // a LocalValue instance will be on the stack.
    BasicInterpreter interpreter = new BasicInterpreter() {
        boolean done = false;

        @Override
        // newValue is called first to initialize the frame values of all the local variables
        // we intercept the first one to create our own special value.
        public BasicValue newValue(Type type) {
            if (type == null) {
                return BasicValue.UNINITIALIZED_VALUE;
            } else if (type.getSort() == Type.VOID) {
                return null;
            } else {
                // If this is the first value created (i.e. the first local variable)
                // we use a special marker.
                BasicValue ret = done ? super.newValue(type) : new LocalValue(type);
                done = true;
                return ret;
            }
        }
    };

    Analyzer analyzer = new Analyzer(interpreter);
    AbstractInsnNode[] instructions = method.instructions.toArray();
    try {
        Frame[] frames = analyzer.analyze(owner, method);
        if (frames.length != instructions.length) {
            // Should never happen.
            throw new IllegalStateException("The number of frames is not equals to the number of instructions");
        }
        int stackAtThis = -1;
        boolean poppedThis = false;
        int firstLocal = 1;
        for (Type type : Type.getArgumentTypes(method.desc)) {
            firstLocal += type.getSize();
        }

        LinkedHashSet<LocalVariable> variables = new LinkedHashSet<LocalVariable>();
        VarInsnNode lastThis = null;
        int localsAtLastThis = 0;
        // Records the most recent line number encountered. For javac, there should always be
        // a line number node before the call of interest to this(...) or super(...). For robustness,
        // -1 is recorded as a sentinel to indicate this assumption didn't hold. Upstream consumers
        // should check for -1 and recover in a reasonable way (for example, don't set the line
        // number in generated code).
        int recentLine = -1;
        for (int i = 0; i < instructions.length; i++) {
            AbstractInsnNode insn = instructions[i];
            Frame frame = frames[i];
            if (frame.getStackSize() < stackAtThis) {
                poppedThis = true;
            }
            if (insn instanceof MethodInsnNode) {
                // TODO: Do we need to check that the stack is empty after this super call?
                MethodInsnNode methodhInsn = (MethodInsnNode) insn;
                Type[] types = Type.getArgumentTypes(methodhInsn.desc);
                Value value = frame.getStack(frame.getStackSize() - types.length - 1);
                if (value instanceof LocalValue && methodhInsn.name.equals(ByteCodeUtils.CONSTRUCTOR)) {
                    if (poppedThis) {
                        throw new IllegalStateException("Unexpected constructor structure.");
                    }
                    return split(owner, method, lastThis, methodhInsn, recentLine,
                            new ArrayList<LocalVariable>(variables), localsAtLastThis);
                }
            } else if (insn instanceof VarInsnNode) {
                VarInsnNode var = (VarInsnNode) insn;
                if (var.var == 0) {
                    lastThis = var;
                    localsAtLastThis = variables.size();
                    stackAtThis = frame.getStackSize();
                    poppedThis = false;
                }
                Type type = ByteCodeUtils.getTypeForStoreOpcode(var.getOpcode());
                if (type != null && var.var >= firstLocal) {
                    // Variables are equals based on their number, so they will be added
                    // to the set only if they are new, and in the order they are seen.
                    variables.add(new LocalVariable(type, var.var));
                }
            } else if (insn instanceof LineNumberNode) {
                // Record the most recent line number encountered so that call to this(...)
                // or super(...) has line number information. Ultimately used to emit a line
                // number in the generated code.
                LineNumberNode lineNumberNode = (LineNumberNode) insn;
                recentLine = lineNumberNode.line;
            }
        }
        throw new IllegalStateException("Unexpected constructor structure.");
    } catch (AnalyzerException e) {
        throw new IllegalStateException(e);
    }
}

From source file:com.android.build.gradle.internal2.incremental.IncrementalChangeVisitor.java

License:Apache License

/**
 * To each class, add the dispatch method called by the original code that acts as a trampoline to
 * invoke the changed methods.//from www .  j a va 2s .co m
 * <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("access$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 we are disabled, do not generate any dispatch, the method will throw an exception
    // 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(ByteCodeUtils.CLASS_INITIALIZER)
                || methodNode.name.equals(ByteCodeUtils.CONSTRUCTOR)) {
            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",
                    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();
}