|
|
@@ -1725,105 +1725,175 @@ public class BcelShadow extends Shadow { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* We guarantee that the return value is on the top of the stack when |
|
|
|
* munger.getAdviceInstructions() will be run |
|
|
|
* (Unless we have a void return type in which case there's nothing) |
|
|
|
* The basic strategy here is to add a set of instructions at the end of |
|
|
|
* the shadow range that dispatch the advice, and then return whatever the |
|
|
|
* shadow was going to return anyway. |
|
|
|
* |
|
|
|
* To achieve this, we note all the return statements in the advice, and |
|
|
|
* replace them with code that: |
|
|
|
* 1) stores the return value on top of the stack in a temp var |
|
|
|
* 2) jumps to the start of our advice block |
|
|
|
* 3) restores the return value at the end of the advice block before |
|
|
|
* ultimately returning |
|
|
|
* |
|
|
|
* We also need to bind the return value into a returning parameter, if the |
|
|
|
* advice specified one. |
|
|
|
*/ |
|
|
|
public void weaveAfterReturning(BcelAdvice munger) { |
|
|
|
// InstructionFactory fact = getFactory(); |
|
|
|
List returns = new ArrayList(); |
|
|
|
Instruction ret = null; |
|
|
|
for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { |
|
|
|
if (ih.getInstruction() instanceof ReturnInstruction) { |
|
|
|
returns.add(ih); |
|
|
|
ret = Utility.copyInstruction(ih.getInstruction()); |
|
|
|
} |
|
|
|
} |
|
|
|
InstructionList retList; |
|
|
|
InstructionHandle afterAdvice; |
|
|
|
List returns = findReturnInstructions(); |
|
|
|
boolean hasReturnInstructions = !returns.isEmpty(); |
|
|
|
|
|
|
|
// list of instructions that handle the actual return from the join point |
|
|
|
InstructionList retList = new InstructionList(); |
|
|
|
|
|
|
|
// variable that holds the return value |
|
|
|
BcelVar returnValueVar = null; |
|
|
|
|
|
|
|
if (ret != null) { |
|
|
|
if (this.getReturnType() != ResolvedType.VOID) { |
|
|
|
returnValueVar = genTempVar(this.getReturnType()); |
|
|
|
retList = new InstructionList(); |
|
|
|
returnValueVar.appendLoad(retList,getFactory()); |
|
|
|
} else { |
|
|
|
retList = new InstructionList(ret); |
|
|
|
} |
|
|
|
retList.append(ret); |
|
|
|
afterAdvice = retList.getStart(); |
|
|
|
} else /* if (munger.hasDynamicTests()) */ { |
|
|
|
/* |
|
|
|
* |
|
|
|
27: getstatic #72; //Field ajc$cflowCounter$0:Lorg/aspectj/runtime/internal/CFlowCounter; |
|
|
|
30: invokevirtual #87; //Method org/aspectj/runtime/internal/CFlowCounter.dec:()V |
|
|
|
33: aload 6 |
|
|
|
35: athrow |
|
|
|
36: nop |
|
|
|
37: getstatic #72; //Field ajc$cflowCounter$0:Lorg/aspectj/runtime/internal/CFlowCounter; |
|
|
|
40: invokevirtual #87; //Method org/aspectj/runtime/internal/CFlowCounter.dec:()V |
|
|
|
43: d2i |
|
|
|
44: invokespecial #23; //Method java/lang/Object."<init>":()V |
|
|
|
*/ |
|
|
|
retList = new InstructionList(InstructionConstants.NOP); |
|
|
|
afterAdvice = retList.getStart(); |
|
|
|
// } else { |
|
|
|
// retList = new InstructionList(); |
|
|
|
// afterAdvice = null; |
|
|
|
if (hasReturnInstructions) { |
|
|
|
returnValueVar = generateReturnInstructions(returns,retList); |
|
|
|
} else { |
|
|
|
// we need at least one instruction, as the target for jumps |
|
|
|
retList.append(InstructionConstants.NOP); |
|
|
|
} |
|
|
|
|
|
|
|
InstructionList advice = new InstructionList(); |
|
|
|
|
|
|
|
BcelVar tempVar = null; |
|
|
|
if (munger.hasExtraParameter()) { |
|
|
|
UnresolvedType tempVarType = getReturnType(); |
|
|
|
if (tempVarType.equals(ResolvedType.VOID)) { |
|
|
|
tempVar = genTempVar(UnresolvedType.OBJECT); |
|
|
|
advice.append(InstructionConstants.ACONST_NULL); |
|
|
|
tempVar.appendStore(advice, getFactory()); |
|
|
|
} else { |
|
|
|
tempVar = genTempVar(tempVarType); |
|
|
|
advice.append(InstructionFactory.createDup(tempVarType.getSize())); |
|
|
|
tempVar.appendStore(advice, getFactory()); |
|
|
|
} |
|
|
|
} |
|
|
|
advice.append(munger.getAdviceInstructions(this, tempVar, afterAdvice)); |
|
|
|
// list of instructions for dispatching to the advice itself |
|
|
|
InstructionList advice = getAfterReturningAdviceDispatchInstructions( |
|
|
|
munger, retList.getStart()); |
|
|
|
|
|
|
|
if (ret != null) { |
|
|
|
if (hasReturnInstructions) { |
|
|
|
InstructionHandle gotoTarget = advice.getStart(); |
|
|
|
for (Iterator i = returns.iterator(); i.hasNext();) { |
|
|
|
InstructionHandle ih = (InstructionHandle) i.next(); |
|
|
|
// pr148007, work around JRockit bug |
|
|
|
// replace ret with store into returnValueVar, followed by goto if not |
|
|
|
// at the end of the instruction list... |
|
|
|
InstructionList newInstructions = new InstructionList(); |
|
|
|
if (returnValueVar != null) { |
|
|
|
if (munger.hasExtraParameter()) { |
|
|
|
// we have to dup the return val before consuming it... |
|
|
|
newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize())); |
|
|
|
} |
|
|
|
// store the return value into this var |
|
|
|
returnValueVar.appendStore(newInstructions,getFactory()); |
|
|
|
} |
|
|
|
if (!isLastInstructionInRange(ih,range)) { |
|
|
|
newInstructions.append(InstructionFactory.createBranchInstruction( |
|
|
|
Constants.GOTO, |
|
|
|
gotoTarget)); |
|
|
|
} |
|
|
|
if (newInstructions.isEmpty()) { |
|
|
|
newInstructions.append(InstructionConstants.NOP); |
|
|
|
} |
|
|
|
Utility.replaceInstruction(ih,newInstructions,enclosingMethod); |
|
|
|
retargetReturnInstruction(munger.hasExtraParameter(), returnValueVar, gotoTarget, ih); |
|
|
|
} |
|
|
|
range.append(advice); |
|
|
|
range.append(retList); |
|
|
|
} else { |
|
|
|
range.append(advice); |
|
|
|
range.append(retList); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
range.append(advice); |
|
|
|
range.append(retList); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @return a list of all the return instructions in the range of this shadow |
|
|
|
*/ |
|
|
|
private List findReturnInstructions() { |
|
|
|
List returns = new ArrayList(); |
|
|
|
for (InstructionHandle ih = range.getStart(); ih != range.getEnd(); ih = ih.getNext()) { |
|
|
|
if (ih.getInstruction() instanceof ReturnInstruction) { |
|
|
|
returns.add(ih); |
|
|
|
} |
|
|
|
} |
|
|
|
return returns; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Given a list containing all the return instruction handles for this shadow, |
|
|
|
* finds the last return instruction and copies it, making this the ultimate |
|
|
|
* return. If the shadow has a non-void return type, we also create a temporary |
|
|
|
* variable to hold the return value, and load the value from this var before |
|
|
|
* returning (see pr148007 for why we do this - it works around a JRockit bug, |
|
|
|
* and is also closer to what javac generates) |
|
|
|
* @param returns list of all the return instructions in the shadow |
|
|
|
* @param returnInstructions instruction list into which the return instructions should |
|
|
|
* be generated |
|
|
|
* @return the variable holding the return value, if needed |
|
|
|
*/ |
|
|
|
private BcelVar generateReturnInstructions(List returns, InstructionList returnInstructions) { |
|
|
|
BcelVar returnValueVar = null; |
|
|
|
InstructionHandle lastReturnHandle = (InstructionHandle)returns.get(returns.size() - 1); |
|
|
|
Instruction newReturnInstruction = Utility.copyInstruction(lastReturnHandle.getInstruction()); |
|
|
|
if (this.hasANonVoidReturnType()) { |
|
|
|
returnValueVar = genTempVar(this.getReturnType()); |
|
|
|
returnValueVar.appendLoad(returnInstructions,getFactory()); |
|
|
|
} else { |
|
|
|
returnInstructions.append(newReturnInstruction); |
|
|
|
} |
|
|
|
returnInstructions.append(newReturnInstruction); |
|
|
|
return returnValueVar; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @return true, iff this shadow returns a value |
|
|
|
*/ |
|
|
|
private boolean hasANonVoidReturnType() { |
|
|
|
return this.getReturnType() != ResolvedType.VOID; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the list of instructions used to dispatch to the after advice |
|
|
|
* @param munger |
|
|
|
* @param firstInstructionInReturnSequence |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
private InstructionList getAfterReturningAdviceDispatchInstructions(BcelAdvice munger, InstructionHandle firstInstructionInReturnSequence) { |
|
|
|
InstructionList advice = new InstructionList(); |
|
|
|
|
|
|
|
BcelVar tempVar = null; |
|
|
|
if (munger.hasExtraParameter()) { |
|
|
|
tempVar = insertAdviceInstructionsForBindingReturningParameter(advice); |
|
|
|
} |
|
|
|
advice.append(munger.getAdviceInstructions(this, tempVar, firstInstructionInReturnSequence)); |
|
|
|
return advice; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* If the after() returning(Foo f) form is used, bind the return value to the parameter. |
|
|
|
* If the shadow returns void, bind null. |
|
|
|
* @param advice |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
private BcelVar insertAdviceInstructionsForBindingReturningParameter(InstructionList advice) { |
|
|
|
BcelVar tempVar; |
|
|
|
UnresolvedType tempVarType = getReturnType(); |
|
|
|
if (tempVarType.equals(ResolvedType.VOID)) { |
|
|
|
tempVar = genTempVar(UnresolvedType.OBJECT); |
|
|
|
advice.append(InstructionConstants.ACONST_NULL); |
|
|
|
tempVar.appendStore(advice, getFactory()); |
|
|
|
} else { |
|
|
|
tempVar = genTempVar(tempVarType); |
|
|
|
advice.append(InstructionFactory.createDup(tempVarType.getSize())); |
|
|
|
tempVar.appendStore(advice, getFactory()); |
|
|
|
} |
|
|
|
return tempVar; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Helper method for weaveAfterReturning |
|
|
|
* |
|
|
|
* Each return instruction in the method body is retargeted by calling this method. |
|
|
|
* The return instruction is replaced by up to three instructions: |
|
|
|
* 1) if the shadow returns a value, and that value is bound to an after returning |
|
|
|
* parameter, then we DUP the return value on the top of the stack |
|
|
|
* 2) if the shadow returns a value, we store it in the returnValueVar (it will |
|
|
|
* be retrieved from here when we ultimately return after the advice dispatch) |
|
|
|
* 3) if the return was the last instruction, we add a NOP (it will fall through |
|
|
|
* to the advice dispatch), otherwise we add a GOTO that branches to the |
|
|
|
* supplied gotoTarget (start of the advice dispatch) |
|
|
|
*/ |
|
|
|
private void retargetReturnInstruction(boolean hasReturningParameter, BcelVar returnValueVar, InstructionHandle gotoTarget, InstructionHandle returnHandle) { |
|
|
|
// pr148007, work around JRockit bug |
|
|
|
// replace ret with store into returnValueVar, followed by goto if not |
|
|
|
// at the end of the instruction list... |
|
|
|
InstructionList newInstructions = new InstructionList(); |
|
|
|
if (returnValueVar != null) { |
|
|
|
if (hasReturningParameter) { |
|
|
|
// we have to dup the return val before consuming it... |
|
|
|
newInstructions.append(InstructionFactory.createDup(this.getReturnType().getSize())); |
|
|
|
} |
|
|
|
// store the return value into this var |
|
|
|
returnValueVar.appendStore(newInstructions,getFactory()); |
|
|
|
} |
|
|
|
if (!isLastInstructionInRange(returnHandle,range)) { |
|
|
|
newInstructions.append(InstructionFactory.createBranchInstruction( |
|
|
|
Constants.GOTO, |
|
|
|
gotoTarget)); |
|
|
|
} |
|
|
|
if (newInstructions.isEmpty()) { |
|
|
|
newInstructions.append(InstructionConstants.NOP); |
|
|
|
} |
|
|
|
Utility.replaceInstruction(returnHandle,newInstructions,enclosingMethod); |
|
|
|
} |
|
|
|
|
|
|
|
private boolean isLastInstructionInRange(InstructionHandle ih, ShadowRange aRange) { |
|
|
|
return ih.getNext() == aRange.getEnd(); |
|
|
|
} |