Search in sources :

Example 1 with AbstractInsnNode

use of org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode in project tapestry-5 by apache.

the class JSRInlinerAdapter method emitInstantiation.

/**
 * Emits an instantiation of a subroutine, specified by <code>instantiation</code>. May add new
 * instantiations that are invoked by this one to the <code>worklist</code>, and new try/catch
 * blocks to <code>newTryCatchBlocks</code>.
 *
 * @param instantiation 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.
 * @param newLocalVariables the local variables list to which the instantiated local variables
 *     must be appended.
 */
private void emitInstantiation(final Instantiation instantiation, final List<Instantiation> worklist, final InsnList newInstructions, final List<TryCatchBlockNode> newTryCatchBlocks, final List<LocalVariableNode> newLocalVariables) {
    LabelNode previousLabelNode = null;
    for (int i = 0; i < instructions.size(); ++i) {
        AbstractInsnNode insnNode = instructions.get(i);
        if (insnNode.getType() == AbstractInsnNode.LABEL) {
            // Always clone all labels, while avoiding to add the same label more than once.
            LabelNode labelNode = (LabelNode) insnNode;
            LabelNode clonedLabelNode = instantiation.getClonedLabel(labelNode);
            if (clonedLabelNode != previousLabelNode) {
                newInstructions.add(clonedLabelNode);
                previousLabelNode = clonedLabelNode;
            }
        } else if (instantiation.findOwner(i) == instantiation) {
            if (insnNode.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 oldest
                // instantiation that claims to own this instruction.
                LabelNode retLabel = null;
                for (Instantiation retLabelOwner = instantiation; retLabelOwner != null; retLabelOwner = retLabelOwner.parent) {
                    if (retLabelOwner.subroutineInsns.get(i)) {
                        retLabel = retLabelOwner.returnLabel;
                    }
                }
                if (retLabel == null) {
                    // never happen for verifiable code.
                    throw new IllegalArgumentException("Instruction #" + i + " is a RET not owned by any subroutine");
                }
                newInstructions.add(new JumpInsnNode(GOTO, retLabel));
            } else if (insnNode.getOpcode() == JSR) {
                LabelNode jsrLabelNode = ((JumpInsnNode) insnNode).label;
                BitSet subroutineInsns = subroutinesInsns.get(jsrLabelNode);
                Instantiation newInstantiation = new Instantiation(instantiation, subroutineInsns);
                LabelNode clonedJsrLabelNode = newInstantiation.getClonedLabelForJumpInsn(jsrLabelNode);
                // Replace the JSR instruction with a GOTO to the instantiated subroutine, and push NULL
                // for what was once the return address value. This hack allows us to avoid doing any sort
                // of data flow analysis to figure out which instructions manipulate the old return
                // address value pointer which is now known to be unneeded.
                newInstructions.add(new InsnNode(ACONST_NULL));
                newInstructions.add(new JumpInsnNode(GOTO, clonedJsrLabelNode));
                newInstructions.add(newInstantiation.returnLabel);
                // Insert this new instantiation into the queue to be emitted later.
                worklist.add(newInstantiation);
            } else {
                newInstructions.add(insnNode.clone(instantiation));
            }
        }
    }
    // Emit the try/catch blocks that are relevant for this instantiation.
    for (TryCatchBlockNode tryCatchBlockNode : tryCatchBlocks) {
        final LabelNode start = instantiation.getClonedLabel(tryCatchBlockNode.start);
        final LabelNode end = instantiation.getClonedLabel(tryCatchBlockNode.end);
        if (start != end) {
            final LabelNode handler = instantiation.getClonedLabelForJumpInsn(tryCatchBlockNode.handler);
            if (start == null || end == null || handler == null) {
                throw new AssertionError("Internal error!");
            }
            newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, tryCatchBlockNode.type));
        }
    }
    // Emit the local variable nodes that are relevant for this instantiation.
    for (LocalVariableNode localVariableNode : localVariables) {
        final LabelNode start = instantiation.getClonedLabel(localVariableNode.start);
        final LabelNode end = instantiation.getClonedLabel(localVariableNode.end);
        if (start != end) {
            newLocalVariables.add(new LocalVariableNode(localVariableNode.name, localVariableNode.desc, localVariableNode.signature, start, end, localVariableNode.index));
        }
    }
}
Also used : LabelNode(org.apache.tapestry5.internal.plastic.asm.tree.LabelNode) LookupSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.LookupSwitchInsnNode) JumpInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.JumpInsnNode) AbstractInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode) TableSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.TableSwitchInsnNode) InsnNode(org.apache.tapestry5.internal.plastic.asm.tree.InsnNode) TryCatchBlockNode(org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode) BitSet(java.util.BitSet) JumpInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.JumpInsnNode) AbstractInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode) LocalVariableNode(org.apache.tapestry5.internal.plastic.asm.tree.LocalVariableNode)

Example 2 with AbstractInsnNode

use of org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode in project tapestry-5 by apache.

the class Analyzer method findSubroutine.

/**
 * Follows the control flow graph of the currently analyzed method, starting at the given
 * instruction index, and stores a copy of the given subroutine in {@link #subroutines} for each
 * encountered instruction. Jumps to nested subroutines are <i>not</i> followed: instead, the
 * corresponding instructions are put in the given list.
 *
 * @param insnIndex an instruction index.
 * @param subroutine a subroutine.
 * @param jsrInsns where the jsr instructions for nested subroutines must be put.
 * @throws AnalyzerException if the control flow graph can fall off the end of the code.
 */
private void findSubroutine(final int insnIndex, final Subroutine subroutine, final List<AbstractInsnNode> jsrInsns) throws AnalyzerException {
    ArrayList<Integer> instructionIndicesToProcess = new ArrayList<>();
    instructionIndicesToProcess.add(insnIndex);
    while (!instructionIndicesToProcess.isEmpty()) {
        int currentInsnIndex = instructionIndicesToProcess.remove(instructionIndicesToProcess.size() - 1);
        if (currentInsnIndex < 0 || currentInsnIndex >= insnListSize) {
            throw new AnalyzerException(null, "Execution can fall off the end of the code");
        }
        if (subroutines[currentInsnIndex] != null) {
            continue;
        }
        subroutines[currentInsnIndex] = new Subroutine(subroutine);
        AbstractInsnNode currentInsn = insnList.get(currentInsnIndex);
        // Push the normal successors of currentInsn onto instructionIndicesToProcess.
        if (currentInsn instanceof JumpInsnNode) {
            if (currentInsn.getOpcode() == JSR) {
                // Do not follow a jsr, it leads to another subroutine!
                jsrInsns.add(currentInsn);
            } else {
                JumpInsnNode jumpInsn = (JumpInsnNode) currentInsn;
                instructionIndicesToProcess.add(insnList.indexOf(jumpInsn.label));
            }
        } else if (currentInsn instanceof TableSwitchInsnNode) {
            TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) currentInsn;
            findSubroutine(insnList.indexOf(tableSwitchInsn.dflt), subroutine, jsrInsns);
            for (int i = tableSwitchInsn.labels.size() - 1; i >= 0; --i) {
                LabelNode labelNode = tableSwitchInsn.labels.get(i);
                instructionIndicesToProcess.add(insnList.indexOf(labelNode));
            }
        } else if (currentInsn instanceof LookupSwitchInsnNode) {
            LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) currentInsn;
            findSubroutine(insnList.indexOf(lookupSwitchInsn.dflt), subroutine, jsrInsns);
            for (int i = lookupSwitchInsn.labels.size() - 1; i >= 0; --i) {
                LabelNode labelNode = lookupSwitchInsn.labels.get(i);
                instructionIndicesToProcess.add(insnList.indexOf(labelNode));
            }
        }
        // Push the exception handler successors of currentInsn onto instructionIndicesToProcess.
        List<TryCatchBlockNode> insnHandlers = handlers[currentInsnIndex];
        if (insnHandlers != null) {
            for (TryCatchBlockNode tryCatchBlock : insnHandlers) {
                instructionIndicesToProcess.add(insnList.indexOf(tryCatchBlock.handler));
            }
        }
        // Push the next instruction, if the control flow can go from currentInsn to the next.
        switch(currentInsn.getOpcode()) {
            case GOTO:
            case RET:
            case TABLESWITCH:
            case LOOKUPSWITCH:
            case IRETURN:
            case LRETURN:
            case FRETURN:
            case DRETURN:
            case ARETURN:
            case RETURN:
            case ATHROW:
                break;
            default:
                instructionIndicesToProcess.add(currentInsnIndex + 1);
                break;
        }
    }
}
Also used : LabelNode(org.apache.tapestry5.internal.plastic.asm.tree.LabelNode) TryCatchBlockNode(org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode) TableSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.TableSwitchInsnNode) ArrayList(java.util.ArrayList) AbstractInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode) JumpInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.JumpInsnNode) LookupSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.LookupSwitchInsnNode)

Example 3 with AbstractInsnNode

use of org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode in project tapestry-5 by apache.

the class BasicVerifier method naryOperation.

@Override
public BasicValue naryOperation(final AbstractInsnNode insn, final List<? extends BasicValue> values) throws AnalyzerException {
    int opcode = insn.getOpcode();
    if (opcode == MULTIANEWARRAY) {
        for (BasicValue value : values) {
            if (!BasicValue.INT_VALUE.equals(value)) {
                throw new AnalyzerException(insn, null, BasicValue.INT_VALUE, value);
            }
        }
    } else {
        int i = 0;
        int j = 0;
        if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) {
            Type owner = Type.getObjectType(((MethodInsnNode) insn).owner);
            if (!isSubTypeOf(values.get(i++), newValue(owner))) {
                throw new AnalyzerException(insn, "Method owner", newValue(owner), values.get(0));
            }
        }
        String methodDescriptor = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc : ((MethodInsnNode) insn).desc;
        Type[] args = Type.getArgumentTypes(methodDescriptor);
        while (i < values.size()) {
            BasicValue expected = newValue(args[j++]);
            BasicValue actual = values.get(i++);
            if (!isSubTypeOf(actual, expected)) {
                throw new AnalyzerException(insn, "Argument " + j, expected, actual);
            }
        }
    }
    return super.naryOperation(insn, values);
}
Also used : Type(org.apache.tapestry5.internal.plastic.asm.Type)

Example 4 with AbstractInsnNode

use of org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode in project tapestry-5 by apache.

the class BasicVerifier method binaryOperation.

@Override
public BasicValue binaryOperation(final AbstractInsnNode insn, final BasicValue value1, final BasicValue value2) throws AnalyzerException {
    BasicValue expected1;
    BasicValue expected2;
    switch(insn.getOpcode()) {
        case IALOAD:
            expected1 = newValue(Type.getType("[I"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case BALOAD:
            if (isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
                expected1 = newValue(Type.getType("[Z"));
            } else {
                expected1 = newValue(Type.getType("[B"));
            }
            expected2 = BasicValue.INT_VALUE;
            break;
        case CALOAD:
            expected1 = newValue(Type.getType("[C"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case SALOAD:
            expected1 = newValue(Type.getType("[S"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case LALOAD:
            expected1 = newValue(Type.getType("[J"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case FALOAD:
            expected1 = newValue(Type.getType("[F"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case DALOAD:
            expected1 = newValue(Type.getType("[D"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case AALOAD:
            expected1 = newValue(Type.getType("[Ljava/lang/Object;"));
            expected2 = BasicValue.INT_VALUE;
            break;
        case IADD:
        case ISUB:
        case IMUL:
        case IDIV:
        case IREM:
        case ISHL:
        case ISHR:
        case IUSHR:
        case IAND:
        case IOR:
        case IXOR:
        case IF_ICMPEQ:
        case IF_ICMPNE:
        case IF_ICMPLT:
        case IF_ICMPGE:
        case IF_ICMPGT:
        case IF_ICMPLE:
            expected1 = BasicValue.INT_VALUE;
            expected2 = BasicValue.INT_VALUE;
            break;
        case FADD:
        case FSUB:
        case FMUL:
        case FDIV:
        case FREM:
        case FCMPL:
        case FCMPG:
            expected1 = BasicValue.FLOAT_VALUE;
            expected2 = BasicValue.FLOAT_VALUE;
            break;
        case LADD:
        case LSUB:
        case LMUL:
        case LDIV:
        case LREM:
        case LAND:
        case LOR:
        case LXOR:
        case LCMP:
            expected1 = BasicValue.LONG_VALUE;
            expected2 = BasicValue.LONG_VALUE;
            break;
        case LSHL:
        case LSHR:
        case LUSHR:
            expected1 = BasicValue.LONG_VALUE;
            expected2 = BasicValue.INT_VALUE;
            break;
        case DADD:
        case DSUB:
        case DMUL:
        case DDIV:
        case DREM:
        case DCMPL:
        case DCMPG:
            expected1 = BasicValue.DOUBLE_VALUE;
            expected2 = BasicValue.DOUBLE_VALUE;
            break;
        case IF_ACMPEQ:
        case IF_ACMPNE:
            expected1 = BasicValue.REFERENCE_VALUE;
            expected2 = BasicValue.REFERENCE_VALUE;
            break;
        case PUTFIELD:
            FieldInsnNode fieldInsn = (FieldInsnNode) insn;
            expected1 = newValue(Type.getObjectType(fieldInsn.owner));
            expected2 = newValue(Type.getType(fieldInsn.desc));
            break;
        default:
            throw new AssertionError();
    }
    if (!isSubTypeOf(value1, expected1)) {
        throw new AnalyzerException(insn, "First argument", expected1, value1);
    } else if (!isSubTypeOf(value2, expected2)) {
        throw new AnalyzerException(insn, "Second argument", expected2, value2);
    }
    if (insn.getOpcode() == AALOAD) {
        return getElementValue(value1);
    } else {
        return super.binaryOperation(insn, value1, value2);
    }
}
Also used : FieldInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.FieldInsnNode)

Example 5 with AbstractInsnNode

use of org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode in project tapestry-5 by apache.

the class JSRInlinerAdapter method findReachableInsns.

/**
 * Finds the instructions that are reachable from the given instruction, without following any JSR
 * instruction nor any exception handler. For this the control flow graph is visited with a depth
 * first search.
 *
 * @param insnIndex the index of an instruction of the subroutine.
 * @param subroutineInsns where the indices of the instructions of the subroutine must be stored.
 * @param visitedInsns the indices of the instructions that have been visited so far (including in
 *     previous calls to this method). This bitset is updated by this method each time a new
 *     instruction is visited. It is used to make sure each instruction is visited at most once.
 */
private void findReachableInsns(final int insnIndex, final BitSet subroutineInsns, final BitSet visitedInsns) {
    int currentInsnIndex = insnIndex;
    // return or not from a JSR, but this is more complicated).
    while (currentInsnIndex < instructions.size()) {
        // Visit each instruction at most once.
        if (subroutineInsns.get(currentInsnIndex)) {
            return;
        }
        subroutineInsns.set(currentInsnIndex);
        // Check if this instruction has already been visited by another subroutine.
        if (visitedInsns.get(currentInsnIndex)) {
            sharedSubroutineInsns.set(currentInsnIndex);
        }
        visitedInsns.set(currentInsnIndex);
        AbstractInsnNode currentInsnNode = instructions.get(currentInsnIndex);
        if (currentInsnNode.getType() == AbstractInsnNode.JUMP_INSN && currentInsnNode.getOpcode() != JSR) {
            // Don't follow JSR instructions in the control flow graph.
            JumpInsnNode jumpInsnNode = (JumpInsnNode) currentInsnNode;
            findReachableInsns(instructions.indexOf(jumpInsnNode.label), subroutineInsns, visitedInsns);
        } else if (currentInsnNode.getType() == AbstractInsnNode.TABLESWITCH_INSN) {
            TableSwitchInsnNode tableSwitchInsnNode = (TableSwitchInsnNode) currentInsnNode;
            findReachableInsns(instructions.indexOf(tableSwitchInsnNode.dflt), subroutineInsns, visitedInsns);
            for (LabelNode labelNode : tableSwitchInsnNode.labels) {
                findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns);
            }
        } else if (currentInsnNode.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
            LookupSwitchInsnNode lookupSwitchInsnNode = (LookupSwitchInsnNode) currentInsnNode;
            findReachableInsns(instructions.indexOf(lookupSwitchInsnNode.dflt), subroutineInsns, visitedInsns);
            for (LabelNode labelNode : lookupSwitchInsnNode.labels) {
                findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns);
            }
        }
        // Check if this instruction falls through to the next instruction; if not, return.
        switch(instructions.get(currentInsnIndex).getOpcode()) {
            case GOTO:
            case RET:
            case TABLESWITCH:
            case LOOKUPSWITCH:
            case IRETURN:
            case LRETURN:
            case FRETURN:
            case DRETURN:
            case ARETURN:
            case RETURN:
            case ATHROW:
                // Note: this either returns from this subroutine, or from a parent subroutine.
                return;
            default:
                // Go to the next instruction.
                currentInsnIndex++;
                break;
        }
    }
}
Also used : LabelNode(org.apache.tapestry5.internal.plastic.asm.tree.LabelNode) TableSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.TableSwitchInsnNode) JumpInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.JumpInsnNode) LookupSwitchInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.LookupSwitchInsnNode) AbstractInsnNode(org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode)

Aggregations

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