Search in sources :

Example 1 with TryCatchBlockNode

use of org.mvel2.asm.tree.TryCatchBlockNode 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 TryCatchBlockNode

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

the class JSRInlinerAdapter method markSubroutineWalk.

/**
 * Performs a depth first search walking the normal byte code path starting
 * at <code>index</code>, and adding each instruction encountered into the
 * subroutine <code>sub</code>. After this walk is complete, iterates over
 * the exception handlers to ensure that we also include those byte codes
 * which are reachable through an exception that may be thrown during the
 * execution of the subroutine. Invoked from <code>markSubroutines()</code>.
 *
 * @param sub
 *            the subroutine whose instructions must be computed.
 * @param index
 *            an instruction of this subroutine.
 * @param anyvisited
 *            indexes of the already visited instructions, i.e. marked as
 *            part of this subroutine or any previously computed subroutine.
 */
private void markSubroutineWalk(final BitSet sub, final int index, final BitSet anyvisited) {
    if (LOGGING) {
        log("markSubroutineWalk: sub=" + sub + " index=" + index);
    }
    // First find those instructions reachable via normal execution
    markSubroutineWalkDFS(sub, index, anyvisited);
    // Now, make sure we also include any applicable exception handlers
    boolean loop = true;
    while (loop) {
        loop = false;
        for (Iterator<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it.hasNext(); ) {
            TryCatchBlockNode trycatch = it.next();
            if (LOGGING) {
                // TODO use of default toString().
                log("Scanning try/catch " + trycatch);
            }
            // If the handler has already been processed, skip it.
            int handlerindex = instructions.indexOf(trycatch.handler);
            if (sub.get(handlerindex)) {
                continue;
            }
            int startindex = instructions.indexOf(trycatch.start);
            int endindex = instructions.indexOf(trycatch.end);
            int nextbit = sub.nextSetBit(startindex);
            if (nextbit != -1 && nextbit < endindex) {
                if (LOGGING) {
                    log("Adding exception handler: " + startindex + '-' + endindex + " due to " + nextbit + " handler " + handlerindex);
                }
                markSubroutineWalkDFS(sub, handlerindex, anyvisited);
                loop = true;
            }
        }
    }
}
Also used : TryCatchBlockNode(org.mvel2.asm.tree.TryCatchBlockNode)

Example 3 with TryCatchBlockNode

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

the class Analyzer method findSubroutine.

private void findSubroutine(int insn, final Subroutine sub, final List<AbstractInsnNode> calls) throws AnalyzerException {
    while (true) {
        if (insn < 0 || insn >= n) {
            throw new AnalyzerException(null, "Execution can fall off end of the code");
        }
        if (subroutines[insn] != null) {
            return;
        }
        subroutines[insn] = sub.copy();
        AbstractInsnNode node = insns.get(insn);
        // calls findSubroutine recursively on normal successors
        if (node instanceof JumpInsnNode) {
            if (node.getOpcode() == JSR) {
                // do not follow a JSR, it leads to another subroutine!
                calls.add(node);
            } else {
                JumpInsnNode jnode = (JumpInsnNode) node;
                findSubroutine(insns.indexOf(jnode.label), sub, calls);
            }
        } else if (node instanceof TableSwitchInsnNode) {
            TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
            findSubroutine(insns.indexOf(tsnode.dflt), sub, calls);
            for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
                LabelNode l = tsnode.labels.get(i);
                findSubroutine(insns.indexOf(l), sub, calls);
            }
        } else if (node instanceof LookupSwitchInsnNode) {
            LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
            findSubroutine(insns.indexOf(lsnode.dflt), sub, calls);
            for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
                LabelNode l = lsnode.labels.get(i);
                findSubroutine(insns.indexOf(l), sub, calls);
            }
        }
        // calls findSubroutine recursively on exception handler successors
        List<TryCatchBlockNode> insnHandlers = handlers[insn];
        if (insnHandlers != null) {
            for (int i = 0; i < insnHandlers.size(); ++i) {
                TryCatchBlockNode tcb = insnHandlers.get(i);
                findSubroutine(insns.indexOf(tcb.handler), sub, calls);
            }
        }
        // if insn does not falls through to the next instruction, return.
        switch(node.getOpcode()) {
            case GOTO:
            case RET:
            case TABLESWITCH:
            case LOOKUPSWITCH:
            case IRETURN:
            case LRETURN:
            case FRETURN:
            case DRETURN:
            case ARETURN:
            case RETURN:
            case ATHROW:
                return;
        }
        insn++;
    }
}
Also used : LabelNode(org.mvel2.asm.tree.LabelNode) TryCatchBlockNode(org.mvel2.asm.tree.TryCatchBlockNode) TableSwitchInsnNode(org.mvel2.asm.tree.TableSwitchInsnNode) JumpInsnNode(org.mvel2.asm.tree.JumpInsnNode) LookupSwitchInsnNode(org.mvel2.asm.tree.LookupSwitchInsnNode) AbstractInsnNode(org.mvel2.asm.tree.AbstractInsnNode)

Example 4 with TryCatchBlockNode

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

the class JSRInlinerAdapter method emitCode.

/**
 * Creates the new instructions, inlining each instantiation of each
 * subroutine until the code is fully elaborated.
 */
private void emitCode() {
    LinkedList<Instantiation> worklist = new LinkedList<Instantiation>();
    // Create an instantiation of the "root" subroutine, which is just the
    // main routine
    worklist.add(new Instantiation(null, mainSubroutine));
    // Emit instantiations of each subroutine we encounter, including the
    // main subroutine
    InsnList newInstructions = new InsnList();
    List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>();
    List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>();
    while (!worklist.isEmpty()) {
        Instantiation inst = worklist.removeFirst();
        emitSubroutine(inst, worklist, newInstructions, newTryCatchBlocks, newLocalVariables);
    }
    instructions = newInstructions;
    tryCatchBlocks = newTryCatchBlocks;
    localVariables = newLocalVariables;
}
Also used : TryCatchBlockNode(org.mvel2.asm.tree.TryCatchBlockNode) ArrayList(java.util.ArrayList) InsnList(org.mvel2.asm.tree.InsnList) LinkedList(java.util.LinkedList) LocalVariableNode(org.mvel2.asm.tree.LocalVariableNode)

Example 5 with TryCatchBlockNode

use of org.mvel2.asm.tree.TryCatchBlockNode 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

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