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));
}
}
}
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;
}
}
}
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);
}
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);
}
}
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;
}
}
}
Aggregations