Search in sources :

Example 6 with AbstractInsnNode

use of org.mvel2.asm.tree.AbstractInsnNode in project mvel by mvel.

the class JSRInlinerAdapter method emitSubroutine.

/**
 * Emits one instantiation of one subroutine, specified by
 * <code>instant</code>. May add new instantiations that are invoked by this
 * one to the <code>worklist</code> parameter, and new try/catch blocks to
 * <code>newTryCatchBlocks</code>.
 *
 * @param instant
 *            the instantiation that must be performed.
 * @param worklist
 *            list of the instantiations that remain to be done.
 * @param newInstructions
 *            the instruction list to which the instantiated code must be
 *            appended.
 * @param newTryCatchBlocks
 *            the exception handler list to which the instantiated handlers
 *            must be appended.
 */
private void emitSubroutine(final Instantiation instant, final List<Instantiation> worklist, final InsnList newInstructions, final List<TryCatchBlockNode> newTryCatchBlocks, final List<LocalVariableNode> newLocalVariables) {
    LabelNode duplbl = null;
    if (LOGGING) {
        log("--------------------------------------------------------");
        log("Emitting instantiation of subroutine " + instant.subroutine);
    }
    // labels and jump targets as we go:
    for (int i = 0, c = instructions.size(); i < c; i++) {
        AbstractInsnNode insn = instructions.get(i);
        Instantiation owner = instant.findOwner(i);
        // Always remap labels:
        if (insn.getType() == AbstractInsnNode.LABEL) {
            // Translate labels into their renamed equivalents.
            // Avoid adding the same label more than once. Note
            // that because we own this instruction the gotoTable
            // and the rangeTable will always agree.
            LabelNode ilbl = (LabelNode) insn;
            LabelNode remap = instant.rangeLabel(ilbl);
            if (LOGGING) {
                // TODO use of default toString().
                log("Translating lbl #" + i + ':' + ilbl + " to " + remap);
            }
            if (remap != duplbl) {
                newInstructions.add(remap);
                duplbl = remap;
            }
            continue;
        }
        // that do not invoke each other.
        if (owner != instant) {
            continue;
        }
        if (LOGGING) {
            log("Emitting inst #" + i);
        }
        if (insn.getOpcode() == RET) {
            // Translate RET instruction(s) to a jump to the return label
            // for the appropriate instantiation. The problem is that the
            // subroutine may "fall through" to the ret of a parent
            // subroutine; therefore, to find the appropriate ret label we
            // find the lowest subroutine on the stack that claims to own
            // this instruction. See the class javadoc comment for an
            // explanation on why this technique is safe (note: it is only
            // safe if the input is verifiable).
            LabelNode retlabel = null;
            for (Instantiation p = instant; p != null; p = p.previous) {
                if (p.subroutine.get(i)) {
                    retlabel = p.returnLabel;
                }
            }
            if (retlabel == null) {
                // code.
                throw new RuntimeException("Instruction #" + i + " is a RET not owned by any subroutine");
            }
            newInstructions.add(new JumpInsnNode(GOTO, retlabel));
        } else if (insn.getOpcode() == JSR) {
            LabelNode lbl = ((JumpInsnNode) insn).label;
            BitSet sub = subroutineHeads.get(lbl);
            Instantiation newinst = new Instantiation(instant, sub);
            LabelNode startlbl = newinst.gotoLabel(lbl);
            if (LOGGING) {
                log(" Creating instantiation of subr " + sub);
            }
            // Rather than JSRing, we will jump to the inline version and
            // push NULL for what was once the return value. This hack
            // allows us to avoid doing any sort of data flow analysis to
            // figure out which instructions manipulate the old return value
            // pointer which is now known to be unneeded.
            newInstructions.add(new InsnNode(ACONST_NULL));
            newInstructions.add(new JumpInsnNode(GOTO, startlbl));
            newInstructions.add(newinst.returnLabel);
            // Insert this new instantiation into the queue to be emitted
            // later.
            worklist.add(newinst);
        } else {
            newInstructions.add(insn.clone(instant));
        }
    }
    // Emit try/catch blocks that are relevant to this method.
    for (Iterator<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it.hasNext(); ) {
        TryCatchBlockNode trycatch = it.next();
        if (LOGGING) {
            // TODO use of default toString().
            log("try catch block original labels=" + trycatch.start + '-' + trycatch.end + "->" + trycatch.handler);
        }
        final LabelNode start = instant.rangeLabel(trycatch.start);
        final LabelNode end = instant.rangeLabel(trycatch.end);
        // Ignore empty try/catch regions
        if (start == end) {
            if (LOGGING) {
                log(" try catch block empty in this subroutine");
            }
            continue;
        }
        final LabelNode handler = instant.gotoLabel(trycatch.handler);
        if (LOGGING) {
            // TODO use of default toString().
            log(" try catch block new labels=" + start + '-' + end + "->" + handler);
        }
        if (start == null || end == null || handler == null) {
            throw new RuntimeException("Internal error!");
        }
        newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, trycatch.type));
    }
    for (Iterator<LocalVariableNode> it = localVariables.iterator(); it.hasNext(); ) {
        LocalVariableNode lvnode = it.next();
        if (LOGGING) {
            log("local var " + lvnode.name);
        }
        final LabelNode start = instant.rangeLabel(lvnode.start);
        final LabelNode end = instant.rangeLabel(lvnode.end);
        if (start == end) {
            if (LOGGING) {
                log("  local variable empty in this sub");
            }
            continue;
        }
        newLocalVariables.add(new LocalVariableNode(lvnode.name, lvnode.desc, lvnode.signature, start, end, lvnode.index));
    }
}
Also used : LabelNode(org.mvel2.asm.tree.LabelNode) InsnNode(org.mvel2.asm.tree.InsnNode) TableSwitchInsnNode(org.mvel2.asm.tree.TableSwitchInsnNode) AbstractInsnNode(org.mvel2.asm.tree.AbstractInsnNode) JumpInsnNode(org.mvel2.asm.tree.JumpInsnNode) LookupSwitchInsnNode(org.mvel2.asm.tree.LookupSwitchInsnNode) TryCatchBlockNode(org.mvel2.asm.tree.TryCatchBlockNode) BitSet(java.util.BitSet) JumpInsnNode(org.mvel2.asm.tree.JumpInsnNode) AbstractInsnNode(org.mvel2.asm.tree.AbstractInsnNode) LocalVariableNode(org.mvel2.asm.tree.LocalVariableNode)

Aggregations

AbstractInsnNode (org.mvel2.asm.tree.AbstractInsnNode)4 JumpInsnNode (org.mvel2.asm.tree.JumpInsnNode)4 LabelNode (org.mvel2.asm.tree.LabelNode)4 LookupSwitchInsnNode (org.mvel2.asm.tree.LookupSwitchInsnNode)4 TableSwitchInsnNode (org.mvel2.asm.tree.TableSwitchInsnNode)4 TryCatchBlockNode (org.mvel2.asm.tree.TryCatchBlockNode)3 Type (org.mvel2.asm.Type)2 ArrayList (java.util.ArrayList)1 BitSet (java.util.BitSet)1 HashMap (java.util.HashMap)1 List (java.util.List)1 FieldInsnNode (org.mvel2.asm.tree.FieldInsnNode)1 IincInsnNode (org.mvel2.asm.tree.IincInsnNode)1 InsnList (org.mvel2.asm.tree.InsnList)1 InsnNode (org.mvel2.asm.tree.InsnNode)1 LocalVariableNode (org.mvel2.asm.tree.LocalVariableNode)1 VarInsnNode (org.mvel2.asm.tree.VarInsnNode)1