Search in sources :

Example 1 with InsnNode

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

the class Analyzer method analyze.

/**
 * Analyzes the given method.
 *
 * @param owner
 *            the internal name of the class to which the method belongs.
 * @param m
 *            the method to be analyzed.
 * @return the symbolic state of the execution stack frame at each bytecode
 *         instruction of the method. The size of the returned array is
 *         equal to the number of instructions (and labels) of the method. A
 *         given frame is <tt>null</tt> if and only if the corresponding
 *         instruction cannot be reached (dead code).
 * @throws AnalyzerException
 *             if a problem occurs during the analysis.
 */
@SuppressWarnings("unchecked")
public Frame<V>[] analyze(final String owner, final MethodNode m) throws AnalyzerException {
    if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
        frames = (Frame<V>[]) new Frame<?>[0];
        return frames;
    }
    n = m.instructions.size();
    insns = m.instructions;
    handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
    frames = (Frame<V>[]) new Frame<?>[n];
    subroutines = new Subroutine[n];
    queued = new boolean[n];
    queue = new int[n];
    top = 0;
    // computes exception handlers for each instruction
    for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
        TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
        int begin = insns.indexOf(tcb.start);
        int end = insns.indexOf(tcb.end);
        for (int j = begin; j < end; ++j) {
            List<TryCatchBlockNode> insnHandlers = handlers[j];
            if (insnHandlers == null) {
                insnHandlers = new ArrayList<TryCatchBlockNode>();
                handlers[j] = insnHandlers;
            }
            insnHandlers.add(tcb);
        }
    }
    // computes the subroutine for each instruction:
    Subroutine main = new Subroutine(null, m.maxLocals, null);
    List<AbstractInsnNode> subroutineCalls = new ArrayList<AbstractInsnNode>();
    Map<LabelNode, Subroutine> subroutineHeads = new HashMap<LabelNode, Subroutine>();
    findSubroutine(0, main, subroutineCalls);
    while (!subroutineCalls.isEmpty()) {
        JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0);
        Subroutine sub = subroutineHeads.get(jsr.label);
        if (sub == null) {
            sub = new Subroutine(jsr.label, m.maxLocals, jsr);
            subroutineHeads.put(jsr.label, sub);
            findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls);
        } else {
            sub.callers.add(jsr);
        }
    }
    for (int i = 0; i < n; ++i) {
        if (subroutines[i] != null && subroutines[i].start == null) {
            subroutines[i] = null;
        }
    }
    // initializes the data structures for the control flow analysis
    Frame<V> current = newFrame(m.maxLocals, m.maxStack);
    Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
    current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
    Type[] args = Type.getArgumentTypes(m.desc);
    int local = 0;
    if ((m.access & ACC_STATIC) == 0) {
        Type ctype = Type.getObjectType(owner);
        current.setLocal(local++, interpreter.newValue(ctype));
    }
    for (int i = 0; i < args.length; ++i) {
        current.setLocal(local++, interpreter.newValue(args[i]));
        if (args[i].getSize() == 2) {
            current.setLocal(local++, interpreter.newValue(null));
        }
    }
    while (local < m.maxLocals) {
        current.setLocal(local++, interpreter.newValue(null));
    }
    merge(0, current, null);
    init(owner, m);
    // control flow analysis
    while (top > 0) {
        int insn = queue[--top];
        Frame<V> f = frames[insn];
        Subroutine subroutine = subroutines[insn];
        queued[insn] = false;
        AbstractInsnNode insnNode = null;
        try {
            insnNode = m.instructions.get(insn);
            int insnOpcode = insnNode.getOpcode();
            int insnType = insnNode.getType();
            if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
                merge(insn + 1, f, subroutine);
                newControlFlowEdge(insn, insn + 1);
            } else {
                current.init(f).execute(insnNode, interpreter);
                subroutine = subroutine == null ? null : subroutine.copy();
                if (insnNode instanceof JumpInsnNode) {
                    JumpInsnNode j = (JumpInsnNode) insnNode;
                    if (insnOpcode != GOTO && insnOpcode != JSR) {
                        merge(insn + 1, current, subroutine);
                        newControlFlowEdge(insn, insn + 1);
                    }
                    int jump = insns.indexOf(j.label);
                    if (insnOpcode == JSR) {
                        merge(jump, current, new Subroutine(j.label, m.maxLocals, j));
                    } else {
                        merge(jump, current, subroutine);
                    }
                    newControlFlowEdge(insn, jump);
                } else if (insnNode instanceof LookupSwitchInsnNode) {
                    LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
                    int jump = insns.indexOf(lsi.dflt);
                    merge(jump, current, subroutine);
                    newControlFlowEdge(insn, jump);
                    for (int j = 0; j < lsi.labels.size(); ++j) {
                        LabelNode label = lsi.labels.get(j);
                        jump = insns.indexOf(label);
                        merge(jump, current, subroutine);
                        newControlFlowEdge(insn, jump);
                    }
                } else if (insnNode instanceof TableSwitchInsnNode) {
                    TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
                    int jump = insns.indexOf(tsi.dflt);
                    merge(jump, current, subroutine);
                    newControlFlowEdge(insn, jump);
                    for (int j = 0; j < tsi.labels.size(); ++j) {
                        LabelNode label = tsi.labels.get(j);
                        jump = insns.indexOf(label);
                        merge(jump, current, subroutine);
                        newControlFlowEdge(insn, jump);
                    }
                } else if (insnOpcode == RET) {
                    if (subroutine == null) {
                        throw new AnalyzerException(insnNode, "RET instruction outside of a sub routine");
                    }
                    for (int i = 0; i < subroutine.callers.size(); ++i) {
                        JumpInsnNode caller = subroutine.callers.get(i);
                        int call = insns.indexOf(caller);
                        if (frames[call] != null) {
                            merge(call + 1, frames[call], current, subroutines[call], subroutine.access);
                            newControlFlowEdge(insn, call + 1);
                        }
                    }
                } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
                    if (subroutine != null) {
                        if (insnNode instanceof VarInsnNode) {
                            int var = ((VarInsnNode) insnNode).var;
                            subroutine.access[var] = true;
                            if (insnOpcode == LLOAD || insnOpcode == DLOAD || insnOpcode == LSTORE || insnOpcode == DSTORE) {
                                subroutine.access[var + 1] = true;
                            }
                        } else if (insnNode instanceof IincInsnNode) {
                            int var = ((IincInsnNode) insnNode).var;
                            subroutine.access[var] = true;
                        }
                    }
                    merge(insn + 1, current, subroutine);
                    newControlFlowEdge(insn, insn + 1);
                }
            }
            List<TryCatchBlockNode> insnHandlers = handlers[insn];
            if (insnHandlers != null) {
                for (int i = 0; i < insnHandlers.size(); ++i) {
                    TryCatchBlockNode tcb = insnHandlers.get(i);
                    Type type;
                    if (tcb.type == null) {
                        type = Type.getObjectType("java/lang/Throwable");
                    } else {
                        type = Type.getObjectType(tcb.type);
                    }
                    int jump = insns.indexOf(tcb.handler);
                    if (newControlFlowExceptionEdge(insn, tcb)) {
                        handler.init(f);
                        handler.clearStack();
                        handler.push(interpreter.newValue(type));
                        merge(jump, handler, subroutine);
                    }
                }
            }
        } catch (AnalyzerException e) {
            throw new AnalyzerException(e.node, "Error at instruction " + insn + ": " + e.getMessage(), e);
        } catch (Exception e) {
            throw new AnalyzerException(insnNode, "Error at instruction " + insn + ": " + e.getMessage(), e);
        }
    }
    return frames;
}
Also used : LabelNode(org.mvel2.asm.tree.LabelNode) TryCatchBlockNode(org.mvel2.asm.tree.TryCatchBlockNode) TableSwitchInsnNode(org.mvel2.asm.tree.TableSwitchInsnNode) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) AbstractInsnNode(org.mvel2.asm.tree.AbstractInsnNode) Type(org.mvel2.asm.Type) JumpInsnNode(org.mvel2.asm.tree.JumpInsnNode) IincInsnNode(org.mvel2.asm.tree.IincInsnNode) ArrayList(java.util.ArrayList) List(java.util.List) InsnList(org.mvel2.asm.tree.InsnList) LookupSwitchInsnNode(org.mvel2.asm.tree.LookupSwitchInsnNode) VarInsnNode(org.mvel2.asm.tree.VarInsnNode)

Example 2 with InsnNode

use of org.mvel2.asm.tree.InsnNode 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)2 JumpInsnNode (org.mvel2.asm.tree.JumpInsnNode)2 LabelNode (org.mvel2.asm.tree.LabelNode)2 LookupSwitchInsnNode (org.mvel2.asm.tree.LookupSwitchInsnNode)2 TableSwitchInsnNode (org.mvel2.asm.tree.TableSwitchInsnNode)2 TryCatchBlockNode (org.mvel2.asm.tree.TryCatchBlockNode)2 ArrayList (java.util.ArrayList)1 BitSet (java.util.BitSet)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Type (org.mvel2.asm.Type)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