Example usage for org.objectweb.asm Type getSize

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

Introduction

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

Prototype

public int getSize() 

Source Link

Document

Returns the size of values of this type.

Usage

From source file:br.usp.each.saeg.badua.core.internal.instr.InstrSupport.java

License:Open Source License

public static void swap(final MethodVisitor mv, final Type stackTop, final Type belowTop) {
    if (stackTop.getSize() == 1) {
        if (belowTop.getSize() == 1) {
            // Top = 1, below = 1
            mv.visitInsn(Opcodes.SWAP);//from  ww w  .  ja  v  a2 s.c o m
        } else {
            // Top = 1, below = 2
            mv.visitInsn(Opcodes.DUP_X2);
            mv.visitInsn(Opcodes.POP);
        }
    } else {
        if (belowTop.getSize() == 1) {
            // Top = 2, below = 1
            mv.visitInsn(Opcodes.DUP2_X1);
        } else {
            // Top = 2, below = 2
            mv.visitInsn(Opcodes.DUP2_X2);
        }
        mv.visitInsn(Opcodes.POP2);
    }
}

From source file:ch.eiafr.cojac.FloatVariablesSorter.java

License:Apache License

public FloatVariablesSorter(int access, String oldDesc, AnalyzerAdapter mv) {
    super(Opcodes.ASM5, mv);
    Type[] args = Type.getArgumentTypes(oldDesc);
    firstFrameMapping = new int[1 + args.length * 2]; // +1 for 'this'
    Arrays.fill(firstFrameMapping, -1); // so that erroneously using unwritten cells will cause problems...
    boolean hasTarget = (Opcodes.ACC_STATIC & access) == 0; // not static -> there is a 'this' param
    int oldVarIndex = 0;
    int newVarIndex = 0;
    int lastIndexSet = -1;
    if (hasTarget) {
        oldVarIndex = newVarIndex = 1;// w w w.j a  va 2 s  .com
        firstFrameMapping[0] = 0; // 'this' remains 'this' after remapping...
        lastIndexSet = 0;
    }
    for (Type arg : args) {
        firstFrameMapping[oldVarIndex] = newVarIndex;
        lastIndexSet = oldVarIndex;
        oldVarIndex += arg.getSize();
        newVarIndex += arg.getSize();
        if (arg.equals(Type.DOUBLE_TYPE)) {
            newVarIndex--;
        }
        assert !arg.equals(COJAC_DOUBLE_WRAPPER_TYPE); // this would be strange (the descriptor is the old one)
    }
    maxRenumber = lastIndexSet;
}

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

License:Apache License

public void nativeCall(MethodVisitor mv, int access, String owner, String name, String desc) {
    boolean isStatic = (access & ACC_STATIC) > 0;
    String newDesc = replaceFloatMethodDescription(desc);
    MethodVisitor newMv = ccv.addProxyMethod(access & ~ACC_NATIVE, name, newDesc, null, null);
    int varIndex = 0;
    int opcode = INVOKESTATIC;
    if (!isStatic) {
        newMv.visitVarInsn(ALOAD, 0);/*  w ww  . ja va  2s  . c  o m*/
        varIndex = 1;
        opcode = INVOKEVIRTUAL;
    }
    ConversionContext cc = new ConversionContext(opcode, owner, name, desc);
    Type args[] = Type.getArgumentTypes(newDesc);
    for (Type type : args) {
        newMv.visitVarInsn(getLoadOpcode(type), varIndex);
        varIndex += type.getSize();
    }
    convertArgumentsToReal(mv, cc);
    // stack >> [target] allParamsArr [target] allParamsArr
    maybeConvertTarget(mv, cc.opcode, cc.owner);
    // stack >> [target] allParamsArr [newTarget] allParamsArr
    explodeOnStack(mv, cc, true);
    // stack >> [target] allParamsArr [newTarget] nprm0 nprm1 nprm2...
    newMv.visitMethodInsn(opcode, owner, name, desc, false);
    // stack >> [target] allParamsArr [possibleResult]
    checkArraysAfterCall(newMv, cc.convertedArrays, desc);
    // stack >> [target] [possibleResult]
    convertReturnType(newMv, desc);
    // stack >> [target] [newPossibleResult]
    newMv.visitInsn(afterFloatReplacement(Type.getReturnType(desc)).getOpcode(IRETURN));
    // stack >> [target]       // left on the stack... not a problem!
    newMv.visitMaxs(0, 0);
}

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

License:Apache License

private static void explodeOnStack(MethodVisitor mv, ConversionContext cc, boolean wantTheConversion) {
    Type[] args = wantTheConversion ? cc.outArgs : cc.inArgs;
    // stack >> ... allParamsArr
    for (int i = 0; i < args.length; i++) {
        // stack >> ... allParamsArr
        Type oa = args[i];
        int oaSort = oa.getSort();
        mv.visitInsn(DUP);/*from   ww w. j  a v  a  2 s  . c om*/
        mv.visitLdcInsn(i);
        mv.visitInsn(AALOAD);
        boolean keepBothVersions = (oa.getSort() == Type.ARRAY);
        if (keepBothVersions) {
            mv.visitTypeInsn(CHECKCAST, "[" + OBJ_TYPE.getDescriptor());
            mv.visitLdcInsn(wantTheConversion ? 1 : 0);
            mv.visitInsn(AALOAD);
        }

        if (oaSort == Type.ARRAY || oaSort == Type.OBJECT) { // else: primitive type
            mv.visitTypeInsn(CHECKCAST, oa.getInternalName());
        } else {
            String jWrapperName = getJWrapper(oa).getInternalName();
            mv.visitTypeInsn(CHECKCAST, jWrapperName);
            mv.visitMethodInsn(INVOKEVIRTUAL, jWrapperName, getWrapperToPrimitiveMethod(oa),
                    "()" + oa.getDescriptor(), false);
        }

        if (oa.getSize() == 2) { //    Object DD  Swap when double or long
            mv.visitInsn(DUP2_X1); // DD Object DD
            mv.visitInsn(POP2); // DD Object
        } else {
            mv.visitInsn(SWAP);
        }
        // stack >> ... nprmI allParamsArr
    }
    mv.visitInsn(POP);
    // stack >> ... nprm0 nprm1 nprm2...
}

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

License:Apache License

private static void checkArraysAfterCall(MethodVisitor mv, Map<Integer, Type> convertedArrays, String desc) {
    // stack >> allParamsArr [possibleResult]
    Type returnType = Type.getReturnType(desc);
    int returnTypeSize = returnType.getSize();
    if (returnTypeSize == 1) {
        mv.visitInsn(SWAP);//from ww  w  .  j a v  a2s .  c  om
    } else if (returnTypeSize == 2) {
        // mv.visitInsn(NOP); just a marker as a debugging helper
        mv.visitInsn(DUP2_X1); // D D Object D D
        mv.visitInsn(POP2); // D D Object
    }
    // stack >> [possibleResult] allParamsArr
    for (int pos : convertedArrays.keySet()) {
        // Type type = convertedArrays.get(pos);
        mv.visitInsn(DUP);
        // stack >> [possibleResult] allParamsArr allParamsArr
        mv.visitLdcInsn(pos);
        // stack >> [possibleResult] allParamsArr allParamsArr i
        mv.visitInsn(AALOAD);
        // stack >> [possibleResult] allParamsArr allParamsArr[i]
        mv.visitTypeInsn(CHECKCAST, "[" + OBJ_TYPE.getDescriptor());
        // stack >> [possibleResult] allParamsArr allParamsArr[i]  (of type Object[])
        mv.visitInsn(DUP);
        // stack >> [possibleResult] allParamsArr allParamsArr[i]  allParamsArr[i]
        mv.visitLdcInsn(0);
        // stack >> [possibleResult] allParamsArr allParamsArr[i]  allParamsArr[i] 0  
        mv.visitInsn(AALOAD);
        // stack >> [possibleResult] allParamsArr allParamsArr[i]  allParamsArr[i][0]    
        mv.visitInsn(SWAP);
        // stack >> [possibleResult] allParamsArr allParamsArr[i][0] allParamsArr[i]
        mv.visitLdcInsn(1);
        // stack >> [possibleResult] allParamsArr allParamsArr[i][0] allParamsArr[i] 1
        mv.visitInsn(AALOAD);
        // stack >> [possibleResult] allParamsArr allParamsArr[i][0] allParamsArr[i][1]
        // mergeOriginalArrayIntoCojac
        mv.visitMethodInsn(INVOKESTATIC, DN_NAME, "mergeOriginalArrayIntoCojac",
                "(" + OBJ_DESC + OBJ_DESC + ")V", false);
        // stack >> [possibleResult] allParamsArr
    }
    // stack >> [possibleResult] allParamsArr
    mv.visitInsn(POP);
    // stack >> [possibleResult]
}

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

License:Apache License

/**
 * Converts Types to LocalVariables, assuming they start from variable 0.
 *//* ww  w  .  j a  v a 2 s .  c om*/
static List<LocalVariable> toLocalVariables(@NonNull List<Type> types) {
    List<LocalVariable> variables = Lists.newArrayList();
    int stack = 0;
    for (int i = 0; i < types.size(); i++) {
        Type type = types.get(i);
        variables.add(new LocalVariable(type, stack));
        stack += type.getSize();
    }
    return variables;
}

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

License:Apache License

@Override
protected void restore(GeneratorAdapter mv, List<Type> args) {
    // At this point, init$args has been called and the result Object is on the stack.
    // The value of that Object is Object[] with exactly n + 1 elements.
    // The first element is a string with the qualified name of the constructor to call.
    // The remaining elements are the constructtor arguments.

    // Create a new local that holds the result of init$args call.
    mv.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/Object;");
    int constructorArgs = mv.newLocal(Type.getType("[Ljava/lang/Object;"));
    mv.storeLocal(constructorArgs);//from   w  w w.j a  va2 s.  c  om

    // Reinstate local values
    mv.loadLocal(locals);
    int stackIndex = 0;
    for (int arrayIndex = 0; arrayIndex < args.size(); arrayIndex++) {
        Type arg = args.get(arrayIndex);
        // Do not restore "this"
        if (arrayIndex > 0) {
            // duplicates the array
            mv.dup();
            // index in the array of objects to restore the boxed parameter.
            mv.push(arrayIndex);
            // get it from the array
            mv.arrayLoad(Type.getType(Object.class));
            // unbox the argument
            ByteCodeUtils.unbox(mv, arg);
            // restore the argument
            mv.visitVarInsn(arg.getOpcode(Opcodes.ISTORE), stackIndex);
        }
        // stack index must progress according to the parameter type we just processed.
        stackIndex += arg.getSize();
    }
    // pops the array
    mv.pop();

    // Push a null for the marker parameter.
    mv.loadLocal(constructorArgs);
    mv.visitInsn(Opcodes.ACONST_NULL);

    // Invoke the constructor
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, thisClassName, "<init>", DISPATCHING_THIS_SIGNATURE, false);

    mv.goTo(end.getLabel());
}

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 ww  w  .j a  va 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

/**
 * Splits the constructor in two methods, the "set up" and the "body" parts (see above).
 *///from  w w w  .  j  ava2 s  .c o m
@NonNull
private static Constructor split(@NonNull String owner, @NonNull MethodNode method,
        @NonNull VarInsnNode loadThis, @NonNull MethodInsnNode delegation, int loadThisLine) {
    String[] exceptions = ((List<String>) method.exceptions).toArray(new String[method.exceptions.size()]);
    String newDesc = method.desc.replaceAll("\\((.*)\\)V", "([Ljava/lang/Object;$1)Ljava/lang/Object;");

    MethodNode initArgs = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "init$args", newDesc, null,
            exceptions);
    AbstractInsnNode insn = loadThis.getNext();
    while (insn != delegation) {
        insn.accept(initArgs);
        insn = insn.getNext();
    }
    LabelNode labelBefore = new LabelNode();
    labelBefore.accept(initArgs);

    GeneratorAdapter mv = new GeneratorAdapter(initArgs, initArgs.access, initArgs.name, initArgs.desc);
    // Copy the arguments back to the argument array
    // The init_args part cannot access the "this" object and can have side effects on the
    // local variables. Because of this we use the first argument (which we want to keep
    // so all the other arguments remain unchanged) as a reference to the array where to
    // return the values of the modified local variables.
    Type[] types = Type.getArgumentTypes(initArgs.desc);
    int stack = 1; // Skip the first one which is a reference to the local array.
    for (int i = 1; i < types.length; i++) {
        Type type = types[i];
        // This is not this, but the array of local arguments final values.
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.push(i);
        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), stack);
        mv.box(type);
        mv.arrayStore(Type.getType(Object.class));
        stack += type.getSize();
    }
    // Create the args array with the values to send to the delegated constructor
    Type[] returnTypes = Type.getArgumentTypes(delegation.desc);
    // The extra element for the qualified name of the constructor.
    mv.push(returnTypes.length + 1);
    mv.newArray(Type.getType(Object.class));
    int args = mv.newLocal(Type.getType("[Ljava/lang/Object;"));
    mv.storeLocal(args);
    for (int i = returnTypes.length - 1; i >= 0; i--) {
        Type type = returnTypes[i];
        mv.loadLocal(args);
        mv.swap(type, Type.getType(Object.class));
        mv.push(i + 1);
        mv.swap(type, Type.INT_TYPE);
        mv.box(type);
        mv.arrayStore(Type.getType(Object.class));
    }

    // Store the qualified name of the constructor in the first element of the array.
    mv.loadLocal(args);
    mv.push(0);
    mv.push(delegation.owner + "." + delegation.desc); // Name of the constructor to be called.
    mv.arrayStore(Type.getType(Object.class));

    mv.loadLocal(args);
    mv.returnValue();

    newDesc = method.desc.replace("(", "(L" + owner + ";");
    MethodNode body = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "init$body", newDesc, null,
            exceptions);
    LabelNode labelAfter = new LabelNode();
    labelAfter.accept(body);
    Set<LabelNode> bodyLabels = new HashSet<LabelNode>();

    insn = delegation.getNext();
    while (insn != null) {
        if (insn instanceof LabelNode) {
            bodyLabels.add((LabelNode) insn);
        }
        insn.accept(body);
        insn = insn.getNext();
    }

    // manually transfer the exception table from the existing constructor to the new
    // "init$body" method. The labels were transferred just above so we can reuse them.

    //noinspection unchecked
    for (TryCatchBlockNode tryCatch : (List<TryCatchBlockNode>) method.tryCatchBlocks) {
        tryCatch.accept(body);
    }

    //noinspection unchecked
    for (LocalVariableNode variable : (List<LocalVariableNode>) method.localVariables) {
        boolean startsInBody = bodyLabels.contains(variable.start);
        boolean endsInBody = bodyLabels.contains(variable.end);
        if (!startsInBody && !endsInBody) {
            if (variable.index != 0) { // '#0' on init$args is not 'this'
                variable.accept(initArgs);
            }
        } else if (startsInBody && endsInBody) {
            variable.accept(body);
        } else if (!startsInBody && endsInBody) {
            // The variable spans from the args to the end of the method, create two:
            if (variable.index != 0) { // '#0' on init$args is not 'this'
                LocalVariableNode var0 = new LocalVariableNode(variable.name, variable.desc, variable.signature,
                        variable.start, labelBefore, variable.index);
                var0.accept(initArgs);
            }
            LocalVariableNode var1 = new LocalVariableNode(variable.name, variable.desc, variable.signature,
                    labelAfter, variable.end, variable.index);
            var1.accept(body);
        } else {
            throw new IllegalStateException("Local variable starts after it ends.");
        }
    }

    return new Constructor(loadThis, loadThisLine, initArgs, delegation, body);
}

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 www .ja v  a  2 s  .  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);
    }
}