List of usage examples for org.objectweb.asm.tree MethodNode MethodNode
public MethodNode(final int access, final String name, final String descriptor, final String signature, final String[] exceptions)
From source file:Test1.java
License:Apache License
private static void implementInterface(ClassNode cn) throws Exception { // MethodNode fromMethod = new MethodNode(ACC_PUBLIC, "fromData", // "(Ljava/io/DataInput;)V", null, new String[] { // "java/io/IOException", // "java/lang/ClassNotFoundException" }); // mv = cw.visitMethod(ACC_PUBLIC, "toData", "(Ljava/io/DataOutput;)V", // null, new String[] { "java/io/IOException" }); MethodNode toMethod = new MethodNode(ACC_PUBLIC, "toData", "(Ljava/io/DataOutput;)V", null, new String[] { "java/io/IOException" }); InsnList instToMethod = toMethod.instructions; for (int i = 0; i < cn.fields.size(); i++) { FieldNode fn = (FieldNode) cn.fields.get(i); toMethod(cn.name, fn, instToMethod); }/*from ww w . j av a 2s. c o m*/ instToMethod.add(new InsnNode(RETURN)); cn.methods.add(toMethod); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cn.accept(cw); byte[] bt = cw.toByteArray(); ClassReader cr = new ClassReader(bt); CheckClassAdapter ca = new CheckClassAdapter(cw); FileOutputStream fo = new FileOutputStream("d:/x1.log"); PrintWriter pw = new PrintWriter(fo); ca.verify(cr, true, pw); ByteArrayClassLoader bacl = new ByteArrayClassLoader(bt); Class cls = bacl.loadClass("ynd.test.Ac01"); DataSerializable di = (DataSerializable) cls.newInstance(); di.toData(null); }
From source file:appeng.transformer.asm.ASMTweaker.java
License:Open Source License
@Nullable @Override/* w w w . j a va 2 s . c o m*/ public byte[] transform(String name, String transformedName, byte[] basicClass) { if (basicClass == null) { return null; } try { if (transformedName != null && this.privateToPublicMethods.containsKey(transformedName)) { ClassNode classNode = new ClassNode(); ClassReader classReader = new ClassReader(basicClass); classReader.accept(classNode, 0); for (PublicLine set : this.privateToPublicMethods.get(transformedName)) { this.makePublic(classNode, set); } // CALL VIRTUAL! if (transformedName.equals("net.minecraft.client.gui.inventory.GuiContainer")) { for (MethodNode mn : classNode.methods) { if (mn.name.equals("func_146977_a") || (mn.name.equals("a") && mn.desc.equals("(Lzk;)V"))) { MethodNode newNode = new MethodNode(Opcodes.ACC_PUBLIC, "func_146977_a_original", mn.desc, mn.signature, EXCEPTIONS); newNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); newNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); newNode.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, classNode.name, mn.name, mn.desc, false)); newNode.instructions.add(new InsnNode(Opcodes.RETURN)); this.log(newNode.name + newNode.desc + " - New Method"); classNode.methods.add(newNode); break; } } for (MethodNode mn : classNode.methods) { if (mn.name.equals("func_73863_a") || mn.name.equals("drawScreen") || (mn.name.equals("a") && mn.desc.equals("(IIF)V"))) { Iterator<AbstractInsnNode> i = mn.instructions.iterator(); while (i.hasNext()) { AbstractInsnNode in = i.next(); if (in.getOpcode() == Opcodes.INVOKESPECIAL) { MethodInsnNode n = (MethodInsnNode) in; if (n.name.equals("func_146977_a") || (n.name.equals("a") && n.desc.equals("(Lzk;)V"))) { this.log(n.name + n.desc + " - Invoke Virtual"); mn.instructions.insertBefore(n, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, n.owner, n.name, n.desc, false)); mn.instructions.remove(in); break; } } } } } } ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } } catch (Throwable ignored) { } return basicClass; }
From source file:clientapi.load.transform.impl.ValueAccessorTransformer.java
License:Apache License
/** * Creates the field-getter getter method in the specified {@code ClassNode} * * @param cn The ClassNode/*from ww w . ja v a 2 s . com*/ */ private void createFieldGetter(ClassNode cn) { MethodNode mn = new MethodNode(ACC_PUBLIC | ACC_FINAL, "getFieldGetter", "(Ljava/lang/String;)Ljava/util/function/Supplier;", null, null); // Create a check for all labeled fields in the cache fieldCache.forEach((id, fn) -> { MethodNode handle; { // Create lambda handle method handle = new MethodNode(ACC_PRIVATE | ACC_SYNTHETIC, "lambda$getFieldGetter$" + current++, "()Ljava/lang/Object;", null, null); // Get the field value handle.visitVarInsn(ALOAD, 0); handle.visitFieldInsn(GETFIELD, cn.name, fn.name, fn.desc); // If the field is a primitive type, get the object representation String object = getObject(fn.desc); if (!object.equals(fn.desc)) handle.visitMethodInsn(INVOKESTATIC, object, "valueOf", "(" + fn.desc + ")L" + object + ";", false); // Return the value handle.visitInsn(ARETURN); // Add the handle method to the class cn.methods.add(handle); } // Create label for IF statement jump Label skip = new Label(); // Compare the target value with the expected value mn.visitVarInsn(ALOAD, 1); mn.visitLdcInsn(id); mn.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); // Jump if the input doesn't match the expected value mn.visitJumpInsn(IFEQ, skip); // Return the getter mn.visitVarInsn(ALOAD, 0); mn.visitInvokeDynamicInsn("get", "(L" + cn.name + ";)Ljava/util/function/Supplier;", // Define the bootstrap method METAFACTORY, // Fill the remaining 3 args for the method Type.getMethodType("()Ljava/lang/Object;"), createMethodHandle(H_INVOKESPECIAL, cn, handle), Type.getMethodType("()Ljava/lang/Object;")); mn.visitInsn(ARETURN); // Indicate where the IF statement should jump to if it fails mn.visitLabel(skip); }); mn.visitInsn(ACONST_NULL); mn.visitInsn(ARETURN); cn.methods.add(mn); }
From source file:clientapi.load.transform.impl.ValueAccessorTransformer.java
License:Apache License
/** * Creates the field-setter getter method in the specified {@code ClassNode} * * @see ValueAccessor//from w w w. jav a 2s. c om * * @param cn The ClassNode */ private void createFieldSetter(ClassNode cn) { MethodNode mn = new MethodNode(ACC_PUBLIC | ACC_FINAL, "getFieldSetter", "(Ljava/lang/String;)Ljava/util/function/Consumer;", null, null); // Create a check for all labeled fields in the cache fieldCache.forEach((id, fn) -> { MethodNode handle; { // Create lambda handle method handle = new MethodNode(ACC_PRIVATE | ACC_SYNTHETIC, "lambda$getFieldSetter$" + current++, "(Ljava/lang/Object;)V", null, null); handle.visitVarInsn(ALOAD, 0); handle.visitVarInsn(ALOAD, 1); handle.visitTypeInsn(CHECKCAST, getStrippedDesc(getObject(fn.desc))); // If the field is a primitive type, get the primitive value String object = getObject(fn.desc); if (!object.equals(fn.desc)) handle.visitMethodInsn(INVOKEVIRTUAL, object, getClassName(fn.desc) + "Value", "()" + fn.desc, false); // Set the field value handle.visitFieldInsn(PUTFIELD, cn.name, fn.name, fn.desc); handle.visitInsn(RETURN); // Add the handle method to the class cn.methods.add(handle); } // Create label for IF statement jump Label skip = new Label(); // Compare the target value with the expected value mn.visitVarInsn(ALOAD, 1); mn.visitLdcInsn(id); mn.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); // Jump if the input doesn't match the expected value mn.visitJumpInsn(IFEQ, skip); // Return the setter mn.visitVarInsn(ALOAD, 0); mn.visitInvokeDynamicInsn("accept", "(L" + cn.name + ";)Ljava/util/function/Consumer;", // Define the bootstrap method METAFACTORY, // Fill the remaining 3 args for the method Type.getMethodType("(Ljava/lang/Object;)V"), createMethodHandle(H_INVOKESPECIAL, cn, handle), Type.getMethodType("(Ljava/lang/Object;)V")); mn.visitInsn(ARETURN); // Indicate where the IF statement should jump to if it fails mn.visitLabel(skip); }); mn.visitInsn(ACONST_NULL); mn.visitInsn(ARETURN); cn.methods.add(mn); }
From source file:co.paralleluniverse.fibers.instrument.InstrumentClass.java
License:Open Source License
@Override public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { SuspendableType markedSuspendable = null; if (suspendableInterface) markedSuspendable = SuspendableType.SUSPENDABLE_SUPER; if (markedSuspendable == null) markedSuspendable = classifier.isSuspendable(db, className, classEntry.getSuperName(), classEntry.getInterfaces(), name, desc, signature, exceptions); final SuspendableType setSuspendable = classEntry.check(name, desc); if (setSuspendable == null) classEntry.set(name, desc,/*www. j a v a 2 s.c o m*/ markedSuspendable != null ? markedSuspendable : SuspendableType.NON_SUSPENDABLE); final boolean suspendable = markedSuspendable == SuspendableType.SUSPENDABLE | setSuspendable == SuspendableType.SUSPENDABLE; if (checkAccess(access) && !isYieldMethod(className, name)) { if (isSynchronized(access)) { if (!db.isAllowMonitors()) throw new UnableToInstrumentException("synchronization", className, name, desc); else db.log(LogLevel.WARNING, "Method %s#%s%s is synchronized", className, name, desc); } if (methods == null) methods = new ArrayList<MethodNode>(); final MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); // if (suspendable) { // if (db.isDebug()) // db.log(LogLevel.INFO, "Method %s#%s suspendable: %s (markedSuspendable: %s setSuspendable: %s)", className, name, suspendable, markedSuspendable, setSuspendable); // // methods.add(mn); // return mn; // this causes the mn to be initialized // } else { // look for @Suspendable or @DontInstrument annotation return new MethodVisitor(Opcodes.ASM4, mn) { private boolean susp = suspendable; private boolean commited = false; @Override public AnnotationVisitor visitAnnotation(String adesc, boolean visible) { if (adesc.equals(ANNOTATION_DESC)) susp = true; else if (adesc.equals(DONT_INSTRUMENT_ANNOTATION_DESC)) susp = false; return super.visitAnnotation(adesc, visible); } @Override public void visitCode() { commit(); super.visitCode(); } @Override public void visitEnd() { if (exception != null) return; commit(); try { super.visitEnd(); } catch (RuntimeException e) { exception = e; } } private void commit() { if (commited) return; commited = true; if (db.isDebug()) db.log(LogLevel.INFO, "Method %s#%s suspendable: %s (markedSuspendable: %s setSuspendable: %s)", className, name, susp, susp, setSuspendable); classEntry.set(name, desc, susp ? SuspendableType.SUSPENDABLE : SuspendableType.NON_SUSPENDABLE); if (susp) methods.add(mn); else { MethodVisitor _mv = makeOutMV(mn); _mv = new JSRInlinerAdapter(_mv, access, name, desc, signature, exceptions); mn.accept(new MethodVisitor(Opcodes.ASM4, _mv) { @Override public void visitEnd() { // don't call visitEnd on MV } }); // write method as-is this.mv = _mv; } } }; // } } return super.visitMethod(access, name, desc, signature, exceptions); }
From source file:co.paralleluniverse.fibers.instrument.SuspOffsetsAfterInstrClassVisitor.java
License:Open Source License
@Override public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { if ((access & Opcodes.ACC_NATIVE) == 0 && !isYieldMethod(className, name)) { // Bytecode-level AST of a method, being a MethodVisitor itself can be filled through delegation from another visitor final MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); // Analyze, fill and enqueue method ASTs final MethodVisitor outMV = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(ASMAPI, outMV) { private Label currLabel = null; private int prevOffset = -1; private boolean instrumented; private boolean optimized = false; private int methodStart = -1, methodEnd = -1; private List<Integer> suspOffsetsAfterInstrL = new ArrayList<>(); private int[] suspCallSites = new int[0]; @Override//from w w w . j ava2 s. co m public AnnotationVisitor visitAnnotation(final String adesc, boolean visible) { if (Classes.INSTRUMENTED_DESC.equals(adesc)) { instrumented = true; return new AnnotationVisitor(ASMAPI) { // Only collect info @Override public void visit(String name, Object value) { if (Instrumented.FIELD_NAME_METHOD_START.equals(name)) methodStart = (Integer) value; else if (Instrumented.FIELD_NAME_METHOD_END.equals(name)) methodEnd = (Integer) value; else if (Instrumented.FIELD_NAME_METHOD_OPTIMIZED.equals(name)) optimized = (Boolean) value; else if (Instrumented.FIELD_NAME_SUSPENDABLE_CALL_SITES.equals(name)) suspCallSites = (int[]) value; else //noinspection StatementWithEmptyBody if (Instrumented.FIELD_NAME_SUSPENDABLE_CALL_SITES_OFFSETS_AFTER_INSTR.equals(name)) ; // Ignore, we're filling it else throw new RuntimeException("Unexpected `@Instrumented` field: " + name); } }; } return super.visitAnnotation(adesc, visible); } @Override public void visitLocalVariable(String name, String desc, String sig, Label lStart, Label lEnd, int slot) { super.visitLocalVariable(name, desc, sig, lStart, lEnd, slot); } @Override public void visitLabel(Label label) { if (instrumented) { currLabel = label; } super.visitLabel(label); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) { if (instrumented) { final int type = AbstractInsnNode.METHOD_INSN; if (InstrumentMethod.isSuspendableCall(db, type, opcode, owner, name, desc) && !Classes.STACK_NAME.equals(owner) && // postRestore currLabel != null && currLabel.info instanceof Integer) addLine(); } super.visitMethodInsn(opcode, owner, name, desc, isInterface); } @Override public void visitInvokeDynamicInsn(String name, String desc, Handle handle, Object... objects) { if (instrumented) { final int type = AbstractInsnNode.INVOKE_DYNAMIC_INSN; final int opcode = Opcodes.INVOKEDYNAMIC; if (InstrumentMethod.isSuspendableCall(db, type, opcode, handle.getOwner(), name, desc) && !Classes.STACK_NAME.equals(handle.getOwner()) && // postRestore currLabel != null && currLabel.info instanceof Integer) addLine(); } super.visitInvokeDynamicInsn(name, desc, handle, objects); } @Override public void visitEnd() { if (instrumented) InstrumentMethod.emitInstrumentedAnn(db, outMV, mn, sourceName, className, optimized, methodStart, methodEnd, suspCallSites, toIntArray(suspOffsetsAfterInstrL)); super.visitEnd(); } private void addLine() { final int currOffset = (Integer) currLabel.info; if (currOffset > prevOffset) { suspOffsetsAfterInstrL.add(currOffset); prevOffset = currOffset; } } }; } return super.visitMethod(access, name, desc, signature, exceptions); }
From source file:com.alibaba.hotswap.processor.constructor.ConstructorVisitor.java
License:Open Source License
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (name.equals(HotswapConstants.INIT)) { MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); String mk = HotswapMethodUtil.getMethodKey(name, desc); initKeys.add(mk);//from ww w .j av a2s.c o m initNodes.put(mk, mn); MethodVisitor mv = new FieldHolderInitModifier(mn, access, name, desc, className); return mv; } MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); mv = new ConstructorInvokeModifier(mv, access, name, desc); return mv; }
From source file:com.alibaba.hotswap.processor.v.VClassGenerateVisitor.java
License:Open Source License
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (!isInterface && name.equals(HotswapConstants.CLINIT)) { return null; }/*from w ww. ja va2s . c o m*/ if (name.equals(HotswapConstants.INIT)) { MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); String mk = HotswapMethodUtil.getMethodKey(name, desc); initKeys.add(mk); initNodes.put(mk, mn); return mn; } return super.visitMethod(access, name, desc, signature, exceptions); }
From source file:com.android.build.gradle.internal.incremental.ConstructorBuilder.java
License:Apache License
/** * Splits the constructor in two methods, the "set up" and the "body" parts (see above). */// www. j a va2s.c om @NonNull private static Constructor split(@NonNull String owner, @NonNull MethodNode method, @NonNull VarInsnNode loadThis, @NonNull MethodInsnNode delegation, int loadThisLine, @NonNull List<LocalVariable> variables, int localsAtLoadThis) { String[] exceptions = ((List<String>) method.exceptions).toArray(new String[method.exceptions.size()]); // Do not add the local array yet, as we treat it as a new variable. String newDesc = method.desc.replace(")V", ")Ljava/lang/Object;"); newDesc = newDesc.replace("(", "([L" + owner + ";"); Type[] argumentTypes = Type.getArgumentTypes(newDesc); // Store the non hotswappable part of the constructor List<AbstractInsnNode> fixed = Lists.newLinkedList(); AbstractInsnNode insn = method.instructions.getFirst(); while (insn != loadThis) { fixed.add(insn); insn = insn.getNext(); } fixed.add(loadThis); MethodNode initArgs = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "init$args", newDesc, null, exceptions); GeneratorAdapter mv = new GeneratorAdapter(initArgs, initArgs.access, initArgs.name, initArgs.desc); int newArgument = mv.newLocal(Type.getType("[Ljava/lang/Object;")); mv.loadLocal(newArgument); ByteCodeUtils.restoreVariables(mv, variables.subList(0, localsAtLoadThis)); // Now insert the original method insn = loadThis.getNext(); while (insn != delegation) { insn.accept(mv); insn = insn.getNext(); } LabelNode labelBefore = new LabelNode(); labelBefore.accept(mv); // Create the args array with the local variables and the values to send to the delegated constructor Type[] returnTypes = Type.getArgumentTypes(delegation.desc); // The extra elements for the local variables and the qualified name of the constructor. mv.push(returnTypes.length + 2); 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 + 2); mv.swap(type, Type.INT_TYPE); mv.box(type); mv.arrayStore(Type.getType(Object.class)); } // Store the qualified name of the constructor in the second element of the array. mv.loadLocal(args); mv.push(1); mv.push(delegation.owner + "." + delegation.desc); // Name of the constructor to be called. mv.arrayStore(Type.getType(Object.class)); // Create the locals array and place it in the first element of the return array mv.loadLocal(args); mv.push(0); mv.push(argumentTypes.length + 1); mv.newArray(Type.getType(Object.class)); ByteCodeUtils.loadVariableArray(mv, ByteCodeUtils.toLocalVariables(Arrays.asList(argumentTypes)), 0); mv.dup(); mv.push(argumentTypes.length); ByteCodeUtils.newVariableArray(mv, variables); mv.arrayStore(Type.getType(Object.class)); mv.arrayStore(Type.getType(Object.class)); mv.loadLocal(args); mv.returnValue(); // Move the first variable up to be an argument initArgs.desc = initArgs.desc.replace(")", "[Ljava/lang/Object;)"); newDesc = method.desc.replace("(", "(L" + owner + ";"); MethodNode body = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "init$body", newDesc, null, exceptions); mv = new GeneratorAdapter(body, body.access, body.name, body.desc); newArgument = mv.newLocal(Type.getType("[Ljava/lang/Object;")); LabelNode labelAfter = new LabelNode(); labelAfter.accept(body); Set<LabelNode> bodyLabels = new HashSet<LabelNode>(); mv.loadLocal(newArgument); ByteCodeUtils.restoreVariables(mv, variables); insn = delegation.getNext(); while (insn != null) { if (insn instanceof LabelNode) { bodyLabels.add((LabelNode) insn); } insn.accept(mv); 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(mv); } //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."); } } // Move the first variable up to be an argument body.desc = body.desc.replace(")", "[Ljava/lang/Object;)"); return new Constructor(owner, fixed, loadThis, loadThisLine, initArgs, delegation, body, variables, localsAtLoadThis); }
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 www . java 2 s. c om @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); }