Example usage for org.objectweb.asm.tree InsnList InsnList

List of usage examples for org.objectweb.asm.tree InsnList InsnList

Introduction

In this page you can find the example usage for org.objectweb.asm.tree InsnList InsnList.

Prototype

InsnList

Source Link

Usage

From source file:org.jooby.internal.apitool.Insns.java

License:Apache License

private static InsnList instructions(final MethodNode method, final AbstractInsnNode n) {
    InsnList instructions = new InsnList();
    instructions.add(n);/*from w ww  . j  a  v a  2s  .  co  m*/
    new Insn<>(method, n).next().forEach(instructions::add);
    return instructions;
}

From source file:org.spongepowered.asm.mixin.injection.invoke.ModifyArgInjector.java

License:MIT License

/**
 * Do the injection/*from   w  w  w  .j  a v a  2s  .  com*/
 */
@Override
protected void inject(Target target, MethodInsnNode node) {
    Type[] args = Type.getArgumentTypes(node.desc);
    int argIndex = this.findArgIndex(target, args);
    InsnList insns = new InsnList();
    int extraLocals = 0;

    if (this.singleArgMode) {
        extraLocals = this.injectSingleArgHandler(target, args, argIndex, insns);
    } else {
        extraLocals = this.injectMultiArgHandler(target, args, argIndex, insns);
    }

    target.method.instructions.insertBefore(node, insns);
    target.method.maxLocals = Math.max(target.maxLocals, target.method.maxLocals + extraLocals);
    target.method.maxStack = Math.max(target.maxStack, target.method.maxStack + 2 - (extraLocals - 1));
}

From source file:org.spongepowered.asm.mixin.injection.invoke.RedirectInjector.java

License:MIT License

/**
 * Do the injection//from w  ww.  ja  v  a2s  . c  om
 */
@Override
protected void inject(Target target, MethodInsnNode node) {
    boolean injectTargetParams = false;
    boolean targetIsStatic = node.getOpcode() == Opcodes.INVOKESTATIC;
    Type ownerType = Type.getType("L" + node.owner + ";");
    Type returnType = Type.getReturnType(node.desc);
    Type[] args = Type.getArgumentTypes(node.desc);
    Type[] stackVars = targetIsStatic ? args : ObjectArrays.concat(ownerType, args);

    String desc = Injector.printArgs(stackVars) + returnType;
    if (!desc.equals(this.methodNode.desc)) {
        String alternateDesc = Injector.printArgs(ObjectArrays.concat(stackVars, target.arguments, Type.class))
                + returnType;
        if (alternateDesc.equals(this.methodNode.desc)) {
            injectTargetParams = true;
        } else {
            throw new InvalidInjectionException(this.info, "@Redirect handler method has an invalid signature "
                    + ", expected " + desc + " found " + this.methodNode.desc);
        }
    }

    InsnList insns = new InsnList();
    int extraLocals = ASMHelper.getArgsSize(stackVars) + 1;
    int[] argMap = this.storeArgs(target, stackVars, insns, 0);
    if (injectTargetParams) {
        extraLocals += ASMHelper.getArgsSize(target.arguments);
        argMap = Ints.concat(argMap, target.argIndices);
    }
    this.invokeHandlerWithArgs(this.methodArgs, insns, argMap);

    target.method.instructions.insertBefore(node, insns);
    target.method.instructions.remove(node);
    target.method.maxLocals = Math.max(target.method.maxLocals, target.maxLocals + extraLocals);
    target.method.maxStack = Math.max(target.method.maxStack, target.maxStack + 1);
}

From source file:org.spongepowered.asm.mixin.transformer.MixinTransformer.java

License:MIT License

/**
 * Get insns corresponding to the instance initialiser (hopefully) from the
 * supplied constructor.//from w w  w .j ava  2  s.  c om
 * 
 * TODO Potentially rewrite this to be less horrible.
 * 
 * @param mixin
 * @param ctor
 * @return initialiser bytecode extracted from the supplied constructor, or
 *      null if the constructor range could not be parsed
 */
private InsnList getInitialiser(MixinTargetContext mixin, MethodNode ctor) {
    // Find the range of line numbers which corresponds to the constructor body
    Range init = this.getConstructorRange(ctor);
    if (!init.isValid()) {
        return null;
    }

    // Now we know where the constructor is, look for insns which lie OUTSIDE the method body
    int line = 0;
    InsnList initialiser = new InsnList();
    boolean gatherNodes = false;
    int trimAtOpcode = -1;
    LabelNode optionalInsn = null;
    for (Iterator<AbstractInsnNode> iter = ctor.instructions.iterator(init.marker); iter.hasNext();) {
        AbstractInsnNode insn = iter.next();
        if (insn instanceof LineNumberNode) {
            line = ((LineNumberNode) insn).line;
            AbstractInsnNode next = ctor.instructions.get(ctor.instructions.indexOf(insn) + 1);
            if (line == init.end && next.getOpcode() != Opcodes.RETURN) {
                gatherNodes = true;
                trimAtOpcode = Opcodes.RETURN;
            } else {
                gatherNodes = init.excludes(line);
                trimAtOpcode = -1;
            }
        } else if (gatherNodes) {
            if (optionalInsn != null) {
                initialiser.add(optionalInsn);
                optionalInsn = null;
            }

            if (insn instanceof LabelNode) {
                optionalInsn = (LabelNode) insn;
            } else {
                int opcode = insn.getOpcode();
                if (opcode == trimAtOpcode) {
                    trimAtOpcode = -1;
                    continue;
                }
                for (int ivalidOp : MixinTransformer.INITIALISER_OPCODE_BLACKLIST) {
                    if (opcode == ivalidOp) {
                        // At the moment I don't handle any transient locals because I haven't seen any in the wild, but let's avoid writing
                        // code which will likely break things and fix it if a real test case ever appears
                        throw new InvalidMixinException(mixin,
                                "Cannot handle " + ASMHelper.getOpcodeName(opcode) + " opcode (0x"
                                        + Integer.toHexString(opcode).toUpperCase() + ") in class initialiser");
                    }
                }

                initialiser.add(insn);
            }
        }
    }

    // Check that the last insn is a PUTFIELD, if it's not then 
    AbstractInsnNode last = initialiser.getLast();
    if (last != null) {
        if (last.getOpcode() != Opcodes.PUTFIELD) {
            throw new InvalidMixinException(mixin, "Could not parse initialiser, expected 0xB5, found 0x"
                    + Integer.toHexString(last.getOpcode()));
        }
    }

    return initialiser;
}

From source file:org.summer.aop.ltw.AspectWeaver.java

License:Open Source License

private void mergeInitializingInstructions(String superClassName, int maxStack, int maxLocals, String className,
        MethodNode methodNode, AbstractInsnNode firstSuperInsn) {
    InsnList superInsnList = new InsnList();
    while (!(firstSuperInsn instanceof InsnNode) || (((InsnNode) firstSuperInsn).getOpcode() != RETURN
            && ((InsnNode) firstSuperInsn).getOpcode() != ATHROW)) {
        if (firstSuperInsn instanceof MethodInsnNode
                && ((MethodInsnNode) firstSuperInsn).owner.equals(superClassName))
            ((MethodInsnNode) firstSuperInsn).owner = className;
        else if (firstSuperInsn instanceof FieldInsnNode
                && ((FieldInsnNode) firstSuperInsn).owner.equals(superClassName))
            ((FieldInsnNode) firstSuperInsn).owner = className;
        superInsnList.add(firstSuperInsn);
        firstSuperInsn = firstSuperInsn.getNext();
    }/*  w w  w.  jav  a2  s.  c  o  m*/
    firstSuperInsn = methodNode.instructions.getFirst();
    while (!(firstSuperInsn instanceof MethodInsnNode)
            || !((MethodInsnNode) firstSuperInsn).owner.equals(superClassName)
            || ((MethodInsnNode) firstSuperInsn).getOpcode() != INVOKESPECIAL
            || !((MethodInsnNode) firstSuperInsn).name.equals("<init>")) {
        firstSuperInsn = firstSuperInsn.getNext();
    }
    firstSuperInsn = firstSuperInsn.getNext();
    methodNode.instructions.insertBefore(firstSuperInsn, superInsnList);
    methodNode.maxStack = Math.max(methodNode.maxStack, maxStack);
    methodNode.maxLocals = Math.max(methodNode.maxLocals, maxLocals);
    for (Iterator<?> it = methodNode.localVariables.iterator(); it.hasNext();) {
        LocalVariableNode localVar = (LocalVariableNode) it.next();
        if (localVar.desc.equals("L" + superClassName + ";"))
            localVar.desc = "L" + className + ";";
    }
}

From source file:pku.sei.checkedcoverage.tracer.instrumentation.TracingMethodInstrumenter.java

License:Creative Commons License

@SuppressWarnings("unchecked")
public void transform(final ListIterator<MethodNode> methodIt) {

    // do not modify abstract or native methods
    if ((this.methodNode.access & ACC_ABSTRACT) != 0 || (this.methodNode.access & ACC_NATIVE) != 0)
        return;/*ww  w .jav  a 2s. c  o m*/

    // check out what labels are jump targets (only these have to be traced)
    analyze(this.methodNode);

    this.instructionIterator = new FixedInstructionIterator(this.methodNode.instructions);
    // in the old method, initialize the new local variable for the threadtracer
    this.instructionIterator.add(new MethodInsnNode(INVOKESTATIC, Type.getInternalName(Tracer.class),
            "getInstance", "()L" + Type.getInternalName(Tracer.class) + ";"));
    this.instructionIterator.add(new MethodInsnNode(INVOKEVIRTUAL, Type.getInternalName(Tracer.class),
            "getThreadTracer", "()L" + Type.getInternalName(ThreadTracer.class) + ";"));
    this.instructionIterator.add(new InsnNode(DUP));
    this.instructionIterator.add(new VarInsnNode(ASTORE, this.tracerLocalVarIndex));

    this.instructionIterator.add(
            new MethodInsnNode(INVOKEINTERFACE, Type.getInternalName(ThreadTracer.class), "isPaused", "()Z"));
    final LabelNode noTracingLabel = new LabelNode();
    this.instructionIterator.add(new JumpInsnNode(IFNE, noTracingLabel));
    // create a copy of the (uninstrumented) instructions (later, while iterating through the instructions)
    final InsnList oldInstructions = new InsnList();
    final Map<LabelNode, LabelNode> labelCopies = LazyMap.decorate(new HashMap<LabelNode, LabelNode>(),
            new Factory() {
                @Override
                public Object create() {
                    return new LabelNode();
                }
            });
    // copy the try-catch-blocks
    final Object[] oldTryCatchblockNodes = this.methodNode.tryCatchBlocks.toArray();
    for (final Object o : oldTryCatchblockNodes) {
        final TryCatchBlockNode tcb = (TryCatchBlockNode) o;
        final TryCatchBlockNode newTcb = new TryCatchBlockNode(labelCopies.get(tcb.start),
                labelCopies.get(tcb.end), labelCopies.get(tcb.handler), tcb.type);
        this.methodNode.tryCatchBlocks.add(newTcb);
    }

    // increment number of local variables by one (for the threadtracer)
    ++this.methodNode.maxLocals;

    // and increment all local variable indexes after the new one by one
    for (final Object o : this.methodNode.localVariables) {
        final LocalVariableNode localVar = (LocalVariableNode) o;
        if (localVar.index >= this.tracerLocalVarIndex)
            ++localVar.index;
    }

    // store information about local variables in the ReadMethod object
    List<LocalVariable> localVariables = new ArrayList<LocalVariable>();
    for (final Object o : this.methodNode.localVariables) {
        final LocalVariableNode localVar = (LocalVariableNode) o;
        while (localVariables.size() <= localVar.index)
            localVariables.add(null);
        localVariables.set(localVar.index, new LocalVariable(localVar.index, localVar.name, localVar.desc));
    }
    this.readMethod.setLocalVariables(localVariables.toArray(new LocalVariable[localVariables.size()]));
    localVariables = null;

    // each method must start with a (dedicated) label:
    assert this.readMethod.getInstructions().isEmpty();
    traceLabel(null, InstructionType.METHODENTRY);
    assert this.readMethod.getInstructions().size() == 1
            && this.readMethod.getInstructions().get(0) instanceof LabelMarker
            && ((LabelMarker) this.readMethod.getInstructions().get(0)).isAdditionalLabel();
    this.readMethod.setMethodEntryLabel((LabelMarker) this.readMethod.getInstructions().get(0));

    // needed later:
    final LabelNode l0 = new LabelNode();
    this.instructionIterator.add(l0);

    // then, visit the instructions that were in the method before
    while (this.instructionIterator.hasNext()) {
        final AbstractInsnNode insnNode = this.instructionIterator.next();
        switch (insnNode.getType()) {
        case AbstractInsnNode.INSN:
            transformInsn((InsnNode) insnNode);
            break;
        case AbstractInsnNode.INT_INSN:
            transformIntInsn((IntInsnNode) insnNode);
            break;
        case AbstractInsnNode.VAR_INSN:
            transformVarInsn((VarInsnNode) insnNode);
            break;
        case AbstractInsnNode.TYPE_INSN:
            transformTypeInsn((TypeInsnNode) insnNode);
            break;
        case AbstractInsnNode.FIELD_INSN:
            transformFieldInsn((FieldInsnNode) insnNode);
            break;
        case AbstractInsnNode.METHOD_INSN:
            transformMethodInsn((MethodInsnNode) insnNode);
            break;
        case AbstractInsnNode.JUMP_INSN:
            transformJumpInsn((JumpInsnNode) insnNode);
            break;
        case AbstractInsnNode.LABEL:
            transformLabel((LabelNode) insnNode);
            break;
        case AbstractInsnNode.LDC_INSN:
            transformLdcInsn((LdcInsnNode) insnNode);
            break;
        case AbstractInsnNode.IINC_INSN:
            transformIincInsn((IincInsnNode) insnNode);
            break;
        case AbstractInsnNode.TABLESWITCH_INSN:
            transformTableSwitchInsn((TableSwitchInsnNode) insnNode);
            break;
        case AbstractInsnNode.LOOKUPSWITCH_INSN:
            transformLookupSwitchInsn((LookupSwitchInsnNode) insnNode);
            break;
        case AbstractInsnNode.MULTIANEWARRAY_INSN:
            transformMultiANewArrayInsn((MultiANewArrayInsnNode) insnNode);
            break;
        case AbstractInsnNode.FRAME:
            // ignore
            break;
        case AbstractInsnNode.LINE:
            // ignore
            break;
        default:
            throw new RuntimeException("Unknown instruction type " + insnNode.getType() + " ("
                    + insnNode.getClass().getSimpleName() + ")");
        }
        oldInstructions.add(insnNode.clone(labelCopies));
    }

    assert this.outstandingInitializations == 0;

    // add the (old) try-catch blocks to the readMethods
    // (can only be done down here since we use the information in the
    // labels map)
    for (final Object o : oldTryCatchblockNodes) {
        final TryCatchBlockNode tcb = (TryCatchBlockNode) o;
        this.readMethod.addTryCatchBlock(new TryCatchBlock(this.labels.get(tcb.start), this.labels.get(tcb.end),
                this.labels.get(tcb.handler), tcb.type));
    }

    final LabelNode l1 = new LabelNode();
    this.instructionIterator.add(l1);
    final int newPos = this.readMethod.getInstructions().size();
    traceLabel(null, InstructionType.METHODEXIT);
    assert this.readMethod.getInstructions().size() == newPos + 1;
    final AbstractInstruction abnormalTerminationLabel = this.readMethod.getInstructions().get(newPos);
    assert abnormalTerminationLabel instanceof LabelMarker;
    this.readMethod.setAbnormalTerminationLabel((LabelMarker) abnormalTerminationLabel);
    this.methodNode.instructions.add(new InsnNode(ATHROW));

    // add a try catch block around the method so that we can trace when this method is left
    // a thrown exception
    this.methodNode.tryCatchBlocks.add(new TryCatchBlockNode(l0, l1, l1, null));

    // now add the code that is executed if no tracing should be performed
    this.methodNode.instructions.add(noTracingLabel);
    if (this.firstLine != -1)
        this.methodNode.instructions.add(new LineNumberNode(this.firstLine, noTracingLabel));
    this.methodNode.instructions.add(new InsnNode(ACONST_NULL));
    this.methodNode.instructions.add(new VarInsnNode(ASTORE, this.tracerLocalVarIndex));
    this.methodNode.instructions.add(oldInstructions);

    // finally: create a copy of the method that gets the ThreadTracer as argument
    // this is only necessary for private methods or "<init>"
    if (this.tracer.wasRedefined(this.readMethod.getReadClass().getName())
            && (this.methodNode.access & ACC_PRIVATE) != 0) {
        final Type[] oldMethodArguments = Type.getArgumentTypes(this.methodNode.desc);
        final Type[] newMethodArguments = Arrays.copyOf(oldMethodArguments, oldMethodArguments.length + 1);
        newMethodArguments[oldMethodArguments.length] = Type.getType(ThreadTracer.class);
        final String newMethodDesc = Type.getMethodDescriptor(Type.getReturnType(this.methodNode.desc),
                newMethodArguments);
        final MethodNode newMethod = new MethodNode(this.methodNode.access, this.methodNode.name, newMethodDesc,
                this.methodNode.signature,
                (String[]) this.methodNode.exceptions.toArray(new String[this.methodNode.exceptions.size()]));
        methodIt.add(newMethod);

        int threadTracerParamPos = ((this.readMethod.getAccess() & Opcodes.ACC_STATIC) == 0 ? 1 : 0);
        for (final Type t : oldMethodArguments)
            threadTracerParamPos += t.getSize();

        final Map<LabelNode, LabelNode> newMethodLabels = LazyMap.decorate(new HashMap<LabelNode, LabelNode>(),
                new Factory() {
                    @Override
                    public Object create() {
                        return new LabelNode();
                    }
                });

        // copy the local variables information to the new method
        for (final Object o : this.methodNode.localVariables) {
            final LocalVariableNode lv = (LocalVariableNode) o;
            newMethod.localVariables.add(new LocalVariableNode(lv.name, lv.desc, lv.signature,
                    newMethodLabels.get(lv.start), newMethodLabels.get(lv.end), lv.index));
        }

        newMethod.maxLocals = this.methodNode.maxLocals;
        newMethod.maxStack = this.methodNode.maxStack;

        // copy the try-catch-blocks
        for (final Object o : this.methodNode.tryCatchBlocks) {
            final TryCatchBlockNode tcb = (TryCatchBlockNode) o;
            newMethod.tryCatchBlocks.add(new TryCatchBlockNode(newMethodLabels.get(tcb.start),
                    newMethodLabels.get(tcb.end), newMethodLabels.get(tcb.handler), tcb.type));
        }

        // skip the first 6 instructions, replace them with these:
        newMethod.instructions.add(new VarInsnNode(ALOAD, threadTracerParamPos));
        newMethod.instructions.add(new InsnNode(DUP));
        newMethod.instructions.add(new VarInsnNode(ASTORE, this.tracerLocalVarIndex));
        newMethod.instructions.add(new JumpInsnNode(IFNULL, newMethodLabels.get(noTracingLabel)));
        final Iterator<AbstractInsnNode> oldInsnIt = this.methodNode.instructions.iterator(6);
        // and add all the other instructions
        while (oldInsnIt.hasNext()) {
            final AbstractInsnNode insn = oldInsnIt.next();
            newMethod.instructions.add(insn.clone(newMethodLabels));
        }
    }

    ready();
}

From source file:pl.asie.foamfix.coremod.patches.ReturnIfBooleanTruePatch.java

License:Open Source License

public ReturnIfBooleanTruePatch(String optionName, String... methods) {
    this.optionName = optionName;
    this.methods = ImmutableSet.copyOf(methods);

    list = new InsnList();
    Label l = new Label();
    LabelNode ln = new LabelNode(l);
    list.add(new FieldInsnNode(Opcodes.GETSTATIC, "pl/asie/foamfix/shared/FoamFixShared", "config",
            "Lpl/asie/foamfix/shared/FoamFixConfig;"));
    list.add(new FieldInsnNode(Opcodes.GETFIELD, "pl/asie/foamfix/shared/FoamFixConfig", optionName, "Z"));
    list.add(new JumpInsnNode(Opcodes.IFEQ, ln));
    list.add(new InsnNode(Opcodes.RETURN));
    list.add(ln);//from  w w w .j  ava 2 s .  c o  m
    list.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null));
}

From source file:pl.clareo.coroutines.core.ClassTransformer.java

License:Apache License

@SuppressWarnings("unchecked")
private static InsnList createDebugFrame(MethodNode coroutine) {
    InsnList insn = new InsnList();
    int nLocals = coroutine.maxLocals;
    String[] names = new String[nLocals];
    List<LocalVariableNode> locals = coroutine.localVariables;
    fillVariableNames(names, locals);/*from w w  w . ja v  a  2s  .  c o m*/
    insn.add(new TypeInsnNode(Opcodes.NEW, FRAME_NAME));
    insn.add(new InsnNode(Opcodes.DUP));
    insn.add(makeInt(nLocals));
    insn.add(makeInt(nLocals));
    insn.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/String"));
    for (LocalVariableNode local : locals) {
        int i = local.index;
        String name = names[i];
        insn.add(new InsnNode(Opcodes.DUP));
        insn.add(makeInt(i));
        if (name != null) {
            insn.add(new LdcInsnNode(name));
        } else {
            insn.add(new InsnNode(Opcodes.ACONST_NULL));
        }
        insn.add(new InsnNode(Opcodes.AASTORE));
    }
    insn.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, FRAME_NAME, "<init>", "(I[Ljava/lang/String;)V"));
    return insn;
}

From source file:pl.clareo.coroutines.core.ClassTransformer.java

License:Apache License

private static InsnList createFrame(MethodNode coroutine) {
    InsnList insn = new InsnList();
    insn.add(new TypeInsnNode(Opcodes.NEW, FRAME_NAME));
    insn.add(new InsnNode(Opcodes.DUP));
    insn.add(makeInt(coroutine.maxLocals));
    insn.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, FRAME_NAME, "<init>", "(I)V"));
    return insn;/*  ww w .  j  a  v  a  2s. c  o m*/
}

From source file:pl.clareo.coroutines.core.ClassTransformer.java

License:Apache License

private static InsnList loggingInstructions(String ownerName, String loggerField, Level level,
        Object... messages) {/*from  w  w w.  j a v  a 2s . c o m*/
    InsnList insn = new InsnList();
    insn.add(new FieldInsnNode(Opcodes.GETSTATIC, ownerName, loggerField, "Ljava/util/logging/Logger;"));
    insn.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/util/logging/Level", level.getName(),
            "Ljava/util/logging/Level;"));
    // stack: * *
    insn.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/util/logging/Logger", "isLoggable",
            "(Ljava/util/logging/Level;)Z"));
    // stack: *
    LabelNode exitBranch = new LabelNode();
    insn.add(new JumpInsnNode(Opcodes.IFEQ, exitBranch));
    // stack:
    insn.add(new FieldInsnNode(Opcodes.GETSTATIC, ownerName, loggerField, "Ljava/util/logging/Logger;"));
    insn.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/util/logging/Level", level.getName(),
            "Ljava/util/logging/Level;"));
    // stack: * *
    insn.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuilder"));
    insn.add(new InsnNode(Opcodes.DUP));
    // stack: * * * *
    String message0 = messages[0].toString();
    insn.add(new LdcInsnNode(message0));
    // stack: * * * * *
    insn.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>",
            "(Ljava/lang/String;)V"));
    // stack: * * *
    for (int m = 1; m < messages.length; m++) {
        Object message = messages[m];
        if (message instanceof Number) {
            insn.add(new VarInsnNode(Opcodes.ALOAD, ((Number) message).intValue()));
        } else {
            insn.add(new LdcInsnNode(message.toString()));
        }
        // stack: * * * *
        insn.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"));
        // stack: * * *
    }
    insn.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
            "()Ljava/lang/String;"));
    insn.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/util/logging/Logger", "log",
            "(Ljava/util/logging/Level;Ljava/lang/String;)V"));
    // stack:
    insn.add(exitBranch);
    return insn;
}