List of usage examples for org.objectweb.asm.tree.analysis BasicInterpreter BasicInterpreter
public BasicInterpreter()
From source file:cl.inria.stiq.instrumenter.BCIUtils.java
License:Open Source License
/** * Checks that a method is correct (ie. likely to pass the JVM verifier). */// w w w .j ava 2 s . c om public static void checkMethod(ClassNode aClassNode, MethodNode aNode) { checkMethod(aClassNode, aNode, new BasicInterpreter(), false); }
From source file:cl.inria.stiq.instrumenter.BCIUtils.java
License:Open Source License
public static void checkMethod(ClassNode aClassNode, MethodNode aNode, boolean aAlwaysPrint) { checkMethod(aClassNode, aNode, new BasicInterpreter(), aAlwaysPrint); }
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 ww . ja va2 s . c o 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 ww w. j a v a 2 s .c o m * <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.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/* w w w. j a v a 2s. 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(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.tools.klint.checks.ControlFlowGraph.java
License:Apache License
/** * Creates a new {@link ControlFlowGraph} and populates it with the flow * control for the given method. If the optional {@code initial} parameter is * provided with an existing graph, then the graph is simply populated, not * created. This allows subclassing of the graph instance, if necessary. * * @param initial usually null, but can point to an existing instance of a * {@link ControlFlowGraph} in which that graph is reused (but * populated with new edges)/* w w w .j a v a2s. co m*/ * @param classNode the class containing the method to be analyzed * @param method the method to be analyzed * @return a {@link ControlFlowGraph} with nodes for the control flow in the * given method * @throws AnalyzerException if the underlying bytecode library is unable to * analyze the method bytecode */ @NonNull public static ControlFlowGraph create(@Nullable ControlFlowGraph initial, @NonNull ClassNode classNode, @NonNull MethodNode method) throws AnalyzerException { final ControlFlowGraph graph = initial != null ? initial : new ControlFlowGraph(); final InsnList instructions = method.instructions; graph.mNodeMap = Maps.newHashMapWithExpectedSize(instructions.size()); graph.mMethod = method; // Create a flow control graph using ASM5's analyzer. According to the ASM 4 guide // (download.forge.objectweb.org/asm/asm4-guide.pdf) there are faster ways to construct // it, but those require a lot more code. Analyzer analyzer = new Analyzer(new BasicInterpreter()) { @Override protected void newControlFlowEdge(int insn, int successor) { // Update the information as of whether the this object has been // initialized at the given instruction. AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.add(from, to); } @Override protected boolean newControlFlowExceptionEdge(int insn, TryCatchBlockNode tcb) { AbstractInsnNode from = instructions.get(insn); graph.exception(from, tcb); return super.newControlFlowExceptionEdge(insn, tcb); } @Override protected boolean newControlFlowExceptionEdge(int insn, int successor) { AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.exception(from, to); return super.newControlFlowExceptionEdge(insn, successor); } }; analyzer.analyze(classNode.name, method); return graph; }
From source file:com.android.tools.lint.checks.ControlFlowGraph.java
License:Apache License
/** * Creates a new {@link ControlFlowGraph} and populates it with the flow * control for the given method. If the optional {@code initial} parameter is * provided with an existing graph, then the graph is simply populated, not * created. This allows subclassing of the graph instance, if necessary. * * @param initial usually null, but can point to an existing instance of a * {@link ControlFlowGraph} in which that graph is reused (but * populated with new edges)//from w w w . j av a 2s .c o m * @param classNode the class containing the method to be analyzed * @param method the method to be analyzed * @return a {@link ControlFlowGraph} with nodes for the control flow in the * given method * @throws AnalyzerException if the underlying bytecode library is unable to * analyze the method bytecode */ @NonNull public static ControlFlowGraph create(@Nullable ControlFlowGraph initial, @NonNull ClassNode classNode, @NonNull MethodNode method) throws AnalyzerException { final ControlFlowGraph graph = initial != null ? initial : new ControlFlowGraph(); final InsnList instructions = method.instructions; graph.mNodeMap = Maps.newHashMapWithExpectedSize(instructions.size()); // Create a flow control graph using ASM4's analyzer. According to the ASM 4 guide // (download.forge.objectweb.org/asm/asm4-guide.pdf) there are faster ways to construct // it, but those require a lot more code. Analyzer analyzer = new Analyzer(new BasicInterpreter()) { @Override protected void newControlFlowEdge(int insn, int successor) { // Update the information as of whether the this object has been // initialized at the given instruction. AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.add(from, to); } @Override protected boolean newControlFlowExceptionEdge(int insn, TryCatchBlockNode tcb) { AbstractInsnNode from = instructions.get(insn); graph.exception(from, tcb); return super.newControlFlowExceptionEdge(insn, tcb); } @Override protected boolean newControlFlowExceptionEdge(int insn, int successor) { AbstractInsnNode from = instructions.get(insn); AbstractInsnNode to = instructions.get(successor); graph.exception(from, to); return super.newControlFlowExceptionEdge(insn, successor); } }; analyzer.analyze(classNode.name, method); return graph; }
From source file:com.android.tools.lint.checks.SecureRandomDetector.java
License:Apache License
@Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode call) {/* w ww .j av a 2 s . com*/ String owner = call.owner; String desc = call.desc; if (owner.equals(OWNER_SECURE_RANDOM)) { if (desc.startsWith(LONG_ARG)) { checkValidSetSeed(context, call); } else if (desc.startsWith("([B)")) { //$NON-NLS-1$ // setSeed(byte[]) ... // We could do some flow analysis here to see whether the byte array getting // passed in appears to be fixed. // However, people calling this constructor rather than the simpler one // with a fixed integer are probably less likely to make that mistake... right? } } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { // Called setSeed(long) on an instanceof a Random object. Flag this if the instance // is likely a SecureRandom. // Track allocations such that we know whether the type of the call // is on a SecureRandom rather than a Random Analyzer analyzer = new Analyzer(new BasicInterpreter() { @Override public BasicValue newValue(Type type) { if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { return new BasicValue(type); } return super.newValue(type); } }); try { Frame[] frames = analyzer.analyze(classNode.name, method); InsnList instructions = method.instructions; Frame frame = frames[instructions.indexOf(call)]; int stackSlot = frame.getStackSize(); for (Type type : Type.getArgumentTypes(desc)) { stackSlot -= type.getSize(); } BasicValue stackValue = (BasicValue) frame.getStack(stackSlot); Type type = stackValue.getType(); if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { checkValidSetSeed(context, call); } } catch (AnalyzerException e) { context.log(e, null); } } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { // Called setSeed(long) on an instanceof a Random object. Flag this if the instance // is likely a SecureRandom. // TODO } }
From source file:com.android.tools.lint.checks.ViewTagDetector.java
License:Apache License
@Override public void checkCall(@NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode call) {//from ww w. j a v a2 s .com // The leak behavior is fixed in ICS: // http://code.google.com/p/android/issues/detail?id=18273 if (context.getMainProject().getMinSdk() >= 14) { return; } String owner = call.owner; String desc = call.desc; if (owner.equals("android/view/View") //$NON-NLS-1$ && desc.equals("(ILjava/lang/Object;)V")) { //$NON-NLS-1$ Analyzer analyzer = new Analyzer(new BasicInterpreter() { @Override public BasicValue newValue(Type type) { if (type == null) { return BasicValue.UNINITIALIZED_VALUE; } else if (type.getSort() == Type.VOID) { return null; } else { return new BasicValue(type); } } }); try { Frame[] frames = analyzer.analyze(classNode.name, method); InsnList instructions = method.instructions; Frame frame = frames[instructions.indexOf(call)]; if (frame.getStackSize() < 3) { return; } BasicValue stackValue = (BasicValue) frame.getStack(2); Type type = stackValue.getType(); if (type == null) { return; } String internalName = type.getInternalName(); String className = type.getClassName(); LintDriver driver = context.getDriver(); SdkInfo sdkInfo = context.getClient().getSdkInfo(context.getMainProject()); String objectType = null; while (className != null) { if (className.equals("android.view.View")) { //$NON-NLS-1$ objectType = "views"; break; } else if (className.endsWith("ViewHolder")) { //$NON-NLS-1$ objectType = "view holders"; break; } else if (className.endsWith("Cursor") //$NON-NLS-1$ && className.startsWith("android.")) { //$NON-NLS-1$ objectType = "cursors"; break; } // TBD: Bitmaps, drawables? That's tricky, because as explained in // http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html // apparently these are used along with nulling out the callbacks, // and that's harder to detect here String parent = sdkInfo.getParentViewClass(className); if (parent == null) { if (internalName == null) { internalName = className.replace('.', '/'); } assert internalName != null; parent = driver.getSuperClass(internalName); } className = parent; internalName = null; } if (objectType != null) { Location location = context.getLocation(call); String message = String.format("Avoid setting %1$s as values for setTag: " + "Can lead to memory leaks in versions older than Android 4.0", objectType); context.report(ISSUE, method, call, location, message, null); } } catch (AnalyzerException e) { context.log(e, null); } } }
From source file:dodola.anole.lib.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 av a 2 s . co m * <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}; * } * <p> * void init$body(int x) { * doSomething(x); * } * </code> * * @param owner the owning class. * @param method the constructor method. */ public static Constructor deconstruct(String owner, 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); } }