use of org.apache.tapestry5.internal.plastic.asm.tree.InsnNode 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.InsnNode in project tapestry-5 by apache.
the class Analyzer method computeMaxLocals.
/**
* Computes and returns the maximum number of local variables used in the given method.
*
* @param method a method.
* @return the maximum number of local variables used in the given method.
*/
private static int computeMaxLocals(final MethodNode method) {
int maxLocals = Type.getArgumentsAndReturnSizes(method.desc) >> 2;
for (AbstractInsnNode insnNode : method.instructions) {
if (insnNode instanceof VarInsnNode) {
int local = ((VarInsnNode) insnNode).var;
int size = (insnNode.getOpcode() == Opcodes.LLOAD || insnNode.getOpcode() == Opcodes.DLOAD || insnNode.getOpcode() == Opcodes.LSTORE || insnNode.getOpcode() == Opcodes.DSTORE) ? 2 : 1;
maxLocals = Math.max(maxLocals, local + size);
} else if (insnNode instanceof IincInsnNode) {
int local = ((IincInsnNode) insnNode).var;
maxLocals = Math.max(maxLocals, local + 1);
}
}
return maxLocals;
}
use of org.apache.tapestry5.internal.plastic.asm.tree.InsnNode in project tapestry-5 by apache.
the class Analyzer method analyze.
/**
* Analyzes the given method.
*
* @param owner the internal name of the class to which 'method' belongs.
* @param method the method to be analyzed. The maxStack and maxLocals fields must have correct
* values.
* @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 {@literal null} 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 method) throws AnalyzerException {
if ((method.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
frames = (Frame<V>[]) new Frame<?>[0];
return frames;
}
insnList = method.instructions;
insnListSize = insnList.size();
handlers = (List<TryCatchBlockNode>[]) new List<?>[insnListSize];
frames = (Frame<V>[]) new Frame<?>[insnListSize];
subroutines = new Subroutine[insnListSize];
inInstructionsToProcess = new boolean[insnListSize];
instructionsToProcess = new int[insnListSize];
numInstructionsToProcess = 0;
// fact that execution can flow from this instruction to the exception handler.
for (int i = 0; i < method.tryCatchBlocks.size(); ++i) {
TryCatchBlockNode tryCatchBlock = method.tryCatchBlocks.get(i);
int startIndex = insnList.indexOf(tryCatchBlock.start);
int endIndex = insnList.indexOf(tryCatchBlock.end);
for (int j = startIndex; j < endIndex; ++j) {
List<TryCatchBlockNode> insnHandlers = handlers[j];
if (insnHandlers == null) {
insnHandlers = new ArrayList<>();
handlers[j] = insnHandlers;
}
insnHandlers.add(tryCatchBlock);
}
}
// For each instruction, compute the subroutine to which it belongs.
// Follow the main 'subroutine', and collect the jsr instructions to nested subroutines.
Subroutine main = new Subroutine(null, method.maxLocals, null);
List<AbstractInsnNode> jsrInsns = new ArrayList<>();
findSubroutine(0, main, jsrInsns);
// Follow the nested subroutines, and collect their own nested subroutines, until all
// subroutines are found.
Map<LabelNode, Subroutine> jsrSubroutines = new HashMap<>();
while (!jsrInsns.isEmpty()) {
JumpInsnNode jsrInsn = (JumpInsnNode) jsrInsns.remove(0);
Subroutine subroutine = jsrSubroutines.get(jsrInsn.label);
if (subroutine == null) {
subroutine = new Subroutine(jsrInsn.label, method.maxLocals, jsrInsn);
jsrSubroutines.put(jsrInsn.label, subroutine);
findSubroutine(insnList.indexOf(jsrInsn.label), subroutine, jsrInsns);
} else {
subroutine.callers.add(jsrInsn);
}
}
// intermediate step above to find the real ones).
for (int i = 0; i < insnListSize; ++i) {
if (subroutines[i] != null && subroutines[i].start == null) {
subroutines[i] = null;
}
}
// Initializes the data structures for the control flow analysis.
Frame<V> currentFrame = computeInitialFrame(owner, method);
merge(0, currentFrame, null);
init(owner, method);
// Control flow analysis.
while (numInstructionsToProcess > 0) {
// Get and remove one instruction from the list of instructions to process.
int insnIndex = instructionsToProcess[--numInstructionsToProcess];
Frame<V> oldFrame = frames[insnIndex];
Subroutine subroutine = subroutines[insnIndex];
inInstructionsToProcess[insnIndex] = false;
// Simulate the execution of this instruction.
AbstractInsnNode insnNode = null;
try {
insnNode = method.instructions.get(insnIndex);
int insnOpcode = insnNode.getOpcode();
int insnType = insnNode.getType();
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
merge(insnIndex + 1, oldFrame, subroutine);
newControlFlowEdge(insnIndex, insnIndex + 1);
} else {
currentFrame.init(oldFrame).execute(insnNode, interpreter);
subroutine = subroutine == null ? null : new Subroutine(subroutine);
if (insnNode instanceof JumpInsnNode) {
JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;
if (insnOpcode != GOTO && insnOpcode != JSR) {
currentFrame.initJumpTarget(insnOpcode, /* target = */
null);
merge(insnIndex + 1, currentFrame, subroutine);
newControlFlowEdge(insnIndex, insnIndex + 1);
}
int jumpInsnIndex = insnList.indexOf(jumpInsn.label);
currentFrame.initJumpTarget(insnOpcode, jumpInsn.label);
if (insnOpcode == JSR) {
merge(jumpInsnIndex, currentFrame, new Subroutine(jumpInsn.label, method.maxLocals, jumpInsn));
} else {
merge(jumpInsnIndex, currentFrame, subroutine);
}
newControlFlowEdge(insnIndex, jumpInsnIndex);
} else if (insnNode instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;
int targetInsnIndex = insnList.indexOf(lookupSwitchInsn.dflt);
currentFrame.initJumpTarget(insnOpcode, lookupSwitchInsn.dflt);
merge(targetInsnIndex, currentFrame, subroutine);
newControlFlowEdge(insnIndex, targetInsnIndex);
for (int i = 0; i < lookupSwitchInsn.labels.size(); ++i) {
LabelNode label = lookupSwitchInsn.labels.get(i);
targetInsnIndex = insnList.indexOf(label);
currentFrame.initJumpTarget(insnOpcode, label);
merge(targetInsnIndex, currentFrame, subroutine);
newControlFlowEdge(insnIndex, targetInsnIndex);
}
} else if (insnNode instanceof TableSwitchInsnNode) {
TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnNode;
int targetInsnIndex = insnList.indexOf(tableSwitchInsn.dflt);
currentFrame.initJumpTarget(insnOpcode, tableSwitchInsn.dflt);
merge(targetInsnIndex, currentFrame, subroutine);
newControlFlowEdge(insnIndex, targetInsnIndex);
for (int i = 0; i < tableSwitchInsn.labels.size(); ++i) {
LabelNode label = tableSwitchInsn.labels.get(i);
currentFrame.initJumpTarget(insnOpcode, label);
targetInsnIndex = insnList.indexOf(label);
merge(targetInsnIndex, currentFrame, subroutine);
newControlFlowEdge(insnIndex, targetInsnIndex);
}
} else if (insnOpcode == RET) {
if (subroutine == null) {
throw new AnalyzerException(insnNode, "RET instruction outside of a subroutine");
}
for (int i = 0; i < subroutine.callers.size(); ++i) {
JumpInsnNode caller = subroutine.callers.get(i);
int jsrInsnIndex = insnList.indexOf(caller);
if (frames[jsrInsnIndex] != null) {
merge(jsrInsnIndex + 1, frames[jsrInsnIndex], currentFrame, subroutines[jsrInsnIndex], subroutine.localsUsed);
newControlFlowEdge(insnIndex, jsrInsnIndex + 1);
}
}
} else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
if (subroutine != null) {
if (insnNode instanceof VarInsnNode) {
int var = ((VarInsnNode) insnNode).var;
subroutine.localsUsed[var] = true;
if (insnOpcode == LLOAD || insnOpcode == DLOAD || insnOpcode == LSTORE || insnOpcode == DSTORE) {
subroutine.localsUsed[var + 1] = true;
}
} else if (insnNode instanceof IincInsnNode) {
int var = ((IincInsnNode) insnNode).var;
subroutine.localsUsed[var] = true;
}
}
merge(insnIndex + 1, currentFrame, subroutine);
newControlFlowEdge(insnIndex, insnIndex + 1);
}
}
List<TryCatchBlockNode> insnHandlers = handlers[insnIndex];
if (insnHandlers != null) {
for (TryCatchBlockNode tryCatchBlock : insnHandlers) {
Type catchType;
if (tryCatchBlock.type == null) {
catchType = Type.getObjectType("java/lang/Throwable");
} else {
catchType = Type.getObjectType(tryCatchBlock.type);
}
if (newControlFlowExceptionEdge(insnIndex, tryCatchBlock)) {
Frame<V> handler = newFrame(oldFrame);
handler.clearStack();
handler.push(interpreter.newExceptionValue(tryCatchBlock, handler, catchType));
merge(insnList.indexOf(tryCatchBlock.handler), handler, subroutine);
}
}
}
} catch (AnalyzerException e) {
throw new AnalyzerException(e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
} catch (RuntimeException e) {
// DontCheck(IllegalCatch): can't be fixed, for backward compatibility.
throw new AnalyzerException(insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
}
}
return frames;
}
Aggregations