//
// The outlined method never needs to be referenced outside of this
// class, and will never be overridden, so we mark it private final.
final InstructionList newIL = new InstructionList();
final XSLTC xsltc = classGen.getParser().getXSLTC();
final String argTypeName = xsltc.getHelperClassName();
final Type[] argTypes =
new Type[] {(new ObjectType(argTypeName)).toJCType()};
final String argName = "copyLocals";
final String[] argNames = new String[] {argName};
int methodAttributes = ACC_PRIVATE | ACC_FINAL;
final boolean isStaticMethod = (getAccessFlags() & ACC_STATIC) != 0;
if (isStaticMethod) {
methodAttributes = methodAttributes | ACC_STATIC;
}
final MethodGenerator outlinedMethodGen =
new MethodGenerator(methodAttributes,
com.sun.org.apache.bcel.internal.generic.Type.VOID,
argTypes, argNames, outlinedMethodName,
getClassName(), newIL, cpg);
// Create class for copying local variables to the outlined method.
// The fields the class will need to contain will be determined as the
// code in the outlineable chunk is examined.
ClassGenerator copyAreaCG
= new ClassGenerator(argTypeName, OBJECT_CLASS, argTypeName+".java",
ACC_FINAL | ACC_PUBLIC | ACC_SUPER, null,
classGen.getStylesheet()) {
public boolean isExternal() {
return true;
}
};
ConstantPoolGen copyAreaCPG = copyAreaCG.getConstantPool();
copyAreaCG.addEmptyConstructor(ACC_PUBLIC);
// Number of fields in the copy class
int copyAreaFieldCount = 0;
// The handle for the instruction after the last one to be outlined.
// Note that this should never end up being null. An outlineable chunk
// won't contain a RETURN instruction or other branch out of the chunk,
// and the JVM specification prohibits code in a method from just
// "falling off the end" so this should always point to a valid handle.
InstructionHandle limit = last.getNext();
// InstructionLists for copying values into and out of an instance of
// CopyLocals:
// oldMethCoypInIL - from locals in old method into an instance
// of the CopyLocals class (oldMethCopyInIL)
// oldMethCopyOutIL - from CopyLocals back into locals in the old
// method
// newMethCopyInIL - from CopyLocals into locals in the new
// method
// newMethCopyOutIL - from locals in new method into the instance
// of the CopyLocals class
InstructionList oldMethCopyInIL = new InstructionList();
InstructionList oldMethCopyOutIL = new InstructionList();
InstructionList newMethCopyInIL = new InstructionList();
InstructionList newMethCopyOutIL = new InstructionList();
// Allocate instance of class in which we'll copy in or copy out locals
// and make two copies: last copy is used to invoke constructor;
// other two are used for references to fields in the CopyLocals object
InstructionHandle outlinedMethodCallSetup =
oldMethCopyInIL.append(new NEW(cpg.addClass(argTypeName)));
oldMethCopyInIL.append(InstructionConstants.DUP);
oldMethCopyInIL.append(InstructionConstants.DUP);
oldMethCopyInIL.append(
new INVOKESPECIAL(cpg.addMethodref(argTypeName, "<init>", "()V")));
// Generate code to invoke the new outlined method, and place the code
// on oldMethCopyOutIL
InstructionHandle outlinedMethodRef;
if (isStaticMethod) {
outlinedMethodRef =
oldMethCopyOutIL.append(
new INVOKESTATIC(cpg.addMethodref(
classGen.getClassName(),
outlinedMethodName,
outlinedMethodGen.getSignature())));
} else {
oldMethCopyOutIL.append(InstructionConstants.THIS);
oldMethCopyOutIL.append(InstructionConstants.SWAP);
outlinedMethodRef =
oldMethCopyOutIL.append(
new INVOKEVIRTUAL(cpg.addMethodref(
classGen.getClassName(),
outlinedMethodName,
outlinedMethodGen.getSignature())));
}
// Used to keep track of the first in a sequence of
// OutlineableChunkStart instructions
boolean chunkStartTargetMappingsPending = false;
InstructionHandle pendingTargetMappingHandle = null;
// Used to keep track of the last instruction that was copied
InstructionHandle lastCopyHandle = null;
// Keeps track of the mapping from instruction handles in the old
// method to instruction handles in the outlined method. Only need
// to track instructions that are targeted by something else in the
// generated BCEL
HashMap targetMap = new HashMap();
// Keeps track of the mapping from local variables in the old method
// to local variables in the outlined method.
HashMap localVarMap = new HashMap();
HashMap revisedLocalVarStart = new HashMap();
HashMap revisedLocalVarEnd = new HashMap();
// Pass 1: Make copies of all instructions, append them to the new list
// and associate old instruction references with the new ones, i.e.,
// a 1:1 mapping. The special marker instructions are not copied.
// Also, identify local variables whose values need to be copied into or
// out of the new outlined method, and builds up targetMap and
// localVarMap as described above. The code identifies those local
// variables first so that they can have fixed slots in the stack
// frame for the outlined method assigned them ahead of all those
// variables that don't need to exist for the entirety of the outlined
// method invocation.
for (InstructionHandle ih = first; ih != limit; ih = ih.getNext()) {
Instruction inst = ih.getInstruction();
// MarkerInstructions are not copied, so if something else targets
// one, the targetMap will point to the nearest copied sibling
// InstructionHandle: for an OutlineableChunkEnd, the nearest
// preceding sibling; for an OutlineableChunkStart, the nearest
// following sibling.
if (inst instanceof MarkerInstruction) {
if (ih.hasTargeters()) {
if (inst instanceof OutlineableChunkEnd) {
targetMap.put(ih, lastCopyHandle);
} else {
if (!chunkStartTargetMappingsPending) {
chunkStartTargetMappingsPending = true;
pendingTargetMappingHandle = ih;
}
}
}
} else {
// Copy the instruction and append it to the outlined method's
// InstructionList.
Instruction c = inst.copy(); // Use clone for shallow copy
if (c instanceof BranchInstruction) {
lastCopyHandle = newIL.append((BranchInstruction)c);
} else {
lastCopyHandle = newIL.append(c);
}
if (c instanceof LocalVariableInstruction
|| c instanceof RET) {
// For any instruction that touches a local variable,
// check whether the local variable's value needs to be
// copied into or out of the outlined method. If so,
// generate the code to perform the necessary copying, and
// use localVarMap to map the variable in the original
// method to the variable in the new method.
IndexedInstruction lvi = (IndexedInstruction)c;
int oldLocalVarIndex = lvi.getIndex();
LocalVariableGen oldLVG =
getLocalVariableRegistry()
.lookupRegisteredLocalVariable(oldLocalVarIndex,
ih.getPosition());
LocalVariableGen newLVG =
(LocalVariableGen)localVarMap.get(oldLVG);
// Has the code already mapped this local variable to a
// local in the new method?
if (localVarMap.get(oldLVG) == null) {
// Determine whether the local variable needs to be
// copied into or out of the outlined by checking
// whether the range of instructions in which the
// variable is accessible is outside the range of
// instructions in the outlineable chunk.
// Special case a chunk start offset of zero: a local
// variable live at that position must be a method
// parameter, so the code doesn't need to check whether
// the variable is live before that point; being live
// at offset zero is sufficient to know that the value
// must be copied in to the outlined method.
boolean copyInLocalValue =
offsetInLocalVariableGenRange(oldLVG,
(outlineChunkStartOffset != 0)
? outlineChunkStartOffset-1
: 0);
boolean copyOutLocalValue =
offsetInLocalVariableGenRange(oldLVG,
outlineChunkEndOffset+1);
// For any variable that needs to be copied into or out
// of the outlined method, create a field in the
// CopyLocals class, and generate the necessary code for
// copying the value.
if (copyInLocalValue || copyOutLocalValue) {
String varName = oldLVG.getName();
Type varType = oldLVG.getType();
newLVG = outlinedMethodGen.addLocalVariable(varName,
varType,
null,
null);
int newLocalVarIndex = newLVG.getIndex();
String varSignature = varType.getSignature();
// Record the mapping from the old local to the new
localVarMap.put(oldLVG, newLVG);
copyAreaFieldCount++;
String copyAreaFieldName =
"field" + copyAreaFieldCount;
copyAreaCG.addField(
new Field(ACC_PUBLIC,
copyAreaCPG.addUtf8(copyAreaFieldName),
copyAreaCPG.addUtf8(varSignature),
null, copyAreaCPG.getConstantPool()));
int fieldRef = cpg.addFieldref(argTypeName,
copyAreaFieldName,
varSignature);
if (copyInLocalValue) {
// Generate code for the old method to store the
// value of the local into the correct field in
// CopyLocals prior to invocation of the
// outlined method.
oldMethCopyInIL.append(
InstructionConstants.DUP);
InstructionHandle copyInLoad =
oldMethCopyInIL.append(
loadLocal(oldLocalVarIndex, varType));
oldMethCopyInIL.append(new PUTFIELD(fieldRef));
// If the end of the live range of the old
// variable was in the middle of the outlined
// chunk. Make the load of its value the new
// end of its range.
if (!copyOutLocalValue) {
revisedLocalVarEnd.put(oldLVG, copyInLoad);
}
// Generate code for start of the outlined
// method to copy the value from a field in
// CopyLocals to the new local in the outlined
// method
newMethCopyInIL.append(
InstructionConstants.ALOAD_1);
newMethCopyInIL.append(new GETFIELD(fieldRef));
newMethCopyInIL.append(
storeLocal(newLocalVarIndex, varType));
}
if (copyOutLocalValue) {
// Generate code for the end of the outlined
// method to copy the value from the new local
// variable into a field in CopyLocals
// method
newMethCopyOutIL.append(
InstructionConstants.ALOAD_1);
newMethCopyOutIL.append(
loadLocal(newLocalVarIndex, varType));
newMethCopyOutIL.append(new PUTFIELD(fieldRef));
// Generate code to copy the value from a field
// in CopyLocals into a local in the original
// method following invocation of the outlined
// method.
oldMethCopyOutIL.append(
InstructionConstants.DUP);
oldMethCopyOutIL.append(new GETFIELD(fieldRef));
InstructionHandle copyOutStore =
oldMethCopyOutIL.append(
storeLocal(oldLocalVarIndex, varType));
// If the start of the live range of the old
// variable was in the middle of the outlined
// chunk. Make this store into it the new start
// of its range.
if (!copyInLocalValue) {
revisedLocalVarStart.put(oldLVG,
copyOutStore);
}
}
}
}
}
if (ih.hasTargeters()) {
targetMap.put(ih, lastCopyHandle);
}
// If this is the first instruction copied following a sequence
// of OutlineableChunkStart instructions, indicate that the
// sequence of old instruction all map to this newly created
// instruction
if (chunkStartTargetMappingsPending) {
do {
targetMap.put(pendingTargetMappingHandle,
lastCopyHandle);
pendingTargetMappingHandle =
pendingTargetMappingHandle.getNext();
} while(pendingTargetMappingHandle != ih);
chunkStartTargetMappingsPending = false;
}
}
}
// Pass 2: Walk old and new instruction lists, updating branch targets
// and local variable references in the new list
InstructionHandle ih = first;
InstructionHandle ch = newIL.getStart();
while (ch != null) {
// i == old instruction; c == copied instruction
Instruction i = ih.getInstruction();
Instruction c = ch.getInstruction();
if (i instanceof BranchInstruction) {
BranchInstruction bc = (BranchInstruction)c;
BranchInstruction bi = (BranchInstruction)i;
InstructionHandle itarget = bi.getTarget(); // old target
// New target must be in targetMap
InstructionHandle newTarget =
(InstructionHandle)targetMap.get(itarget);
bc.setTarget(newTarget);
// Handle LOOKUPSWITCH or TABLESWITCH which may have many
// target instructions
if (bi instanceof Select) {
InstructionHandle[] itargets = ((Select)bi).getTargets();
InstructionHandle[] ctargets = ((Select)bc).getTargets();
// Update all targets
for (int j=0; j < itargets.length; j++) {
ctargets[j] =
(InstructionHandle)targetMap.get(itargets[j]);
}
}
} else if (i instanceof LocalVariableInstruction
|| i instanceof RET) {
// For any instruction that touches a local variable,
// map the location of the variable in the original
// method to its location in the new method.
IndexedInstruction lvi = (IndexedInstruction)c;
int oldLocalVarIndex = lvi.getIndex();
LocalVariableGen oldLVG =
getLocalVariableRegistry()
.lookupRegisteredLocalVariable(oldLocalVarIndex,
ih.getPosition());
LocalVariableGen newLVG =
(LocalVariableGen)localVarMap.get(oldLVG);
int newLocalVarIndex;
if (newLVG == null) {
// Create new variable based on old variable - use same
// name and type, but we will let the variable be active
// for the entire outlined method.
// LocalVariableGen oldLocal = oldLocals[oldLocalVarIndex];
String varName = oldLVG.getName();
Type varType = oldLVG.getType();
newLVG = outlinedMethodGen.addLocalVariable(varName,
varType,
null,
null);
newLocalVarIndex = newLVG.getIndex();
localVarMap.put(oldLVG, newLVG);
// The old variable's live range was wholly contained in
// the outlined chunk. There should no longer be stores
// of values into it or loads of its value, so we can just
// mark its live range as the reference to the outlined
// method.
revisedLocalVarStart.put(oldLVG, outlinedMethodRef);
revisedLocalVarEnd.put(oldLVG, outlinedMethodRef);
} else {
newLocalVarIndex = newLVG.getIndex();
}
lvi.setIndex(newLocalVarIndex);
}
// If the old instruction marks the end of the range of a local
// variable, make sure that any slots on the stack reserved for
// local variables are made available for reuse by calling
// MethodGenerator.removeLocalVariable
if (ih.hasTargeters()) {
InstructionTargeter[] targeters = ih.getTargeters();
for (int idx = 0; idx < targeters.length; idx++) {
InstructionTargeter targeter = targeters[idx];
if (targeter instanceof LocalVariableGen
&& ((LocalVariableGen)targeter).getEnd()==ih) {
Object newLVG = localVarMap.get(targeter);
if (newLVG != null) {
outlinedMethodGen.removeLocalVariable(
(LocalVariableGen)newLVG);
}
}
}
}
// If the current instruction in the original list was a marker,
// it wasn't copied, so don't advance through the list of copied
// instructions yet.
if (!(i instanceof MarkerInstruction)) {
ch = ch.getNext();
}
ih = ih.getNext();
}
// POP the reference to the CopyLocals object from the stack
oldMethCopyOutIL.append(InstructionConstants.POP);
// Now that the generation of the outlined code is complete, update
// the old local variables with new start and end ranges, as required.
Iterator revisedLocalVarStartPairIter = revisedLocalVarStart.entrySet()
.iterator();
while (revisedLocalVarStartPairIter.hasNext()) {
Map.Entry lvgRangeStartPair =
(Map.Entry)revisedLocalVarStartPairIter.next();
LocalVariableGen lvg = (LocalVariableGen)lvgRangeStartPair.getKey();
InstructionHandle startInst =
(InstructionHandle)lvgRangeStartPair.getValue();
lvg.setStart(startInst);
}
Iterator revisedLocalVarEndPairIter = revisedLocalVarEnd.entrySet()
.iterator();
while (revisedLocalVarEndPairIter.hasNext()) {
Map.Entry lvgRangeEndPair =
(Map.Entry)revisedLocalVarEndPairIter.next();
LocalVariableGen lvg = (LocalVariableGen)lvgRangeEndPair.getKey();
InstructionHandle endInst =
(InstructionHandle)lvgRangeEndPair.getValue();
lvg.setEnd(endInst);
}
xsltc.dumpClass(copyAreaCG.getJavaClass());
// Assemble the instruction lists so that the old method invokes the
// new outlined method
InstructionList oldMethodIL = getInstructionList();