// so we don't inline it.
// Note: for code style, this is done at Aspect compilation time.
boolean canSeeProceedPassedToOther = false;
InstructionHandle curr = adviceMethod.getBody().getStart();
InstructionHandle end = adviceMethod.getBody().getEnd();
ConstantPoolGen cpg = adviceMethod.getEnclosingClass().getConstantPoolGen();
while (curr != end) {
InstructionHandle next = curr.getNext();
Instruction inst = curr.getInstruction();
if ((inst instanceof InvokeInstruction)
&& ((InvokeInstruction)inst).getSignature(cpg).indexOf("Lorg/aspectj/lang/ProceedingJoinPoint;") > 0) {
// we may want to refine to exclude stuff returning jp ?
// does code style skip inline if i write dump(thisJoinPoint) ?
canSeeProceedPassedToOther = true;// we see one pjp passed around - dangerous
break;
}
curr = next;
}
if (canSeeProceedPassedToOther) {
// remember this decision to avoid re-analysis
adviceMethod.setCanInline(false);
weaveAroundClosure(munger, hasDynamicTest);
return;
}
}
// We can't inline around methods if they have around advice on them, this
// is because the weaving will extract the body and hence the proceed call.
//??? should consider optimizations to recognize simple cases that don't require body extraction
enclosingMethod.setCanInline(false);
// start by exposing various useful things into the frame
final InstructionFactory fact = getFactory();
// now generate the aroundBody method
LazyMethodGen extractedMethod =
extractMethod(
NameMangler.aroundCallbackMethodName(
getSignature(),
getEnclosingClass()),
Modifier.PRIVATE,
munger
);
// now extract the advice into its own method
String adviceMethodName =
NameMangler.aroundCallbackMethodName(
getSignature(),
getEnclosingClass()) + "$advice";
List argVarList = new ArrayList();
List proceedVarList = new ArrayList();
int extraParamOffset = 0;
// Create the extra parameters that are needed for passing to proceed
// This code is very similar to that found in makeCallToCallback and should
// be rationalized in the future
if (thisVar != null) {
argVarList.add(thisVar);
proceedVarList.add(new BcelVar(thisVar.getType(), extraParamOffset));
extraParamOffset += thisVar.getType().getSize();
}
if (targetVar != null && targetVar != thisVar) {
argVarList.add(targetVar);
proceedVarList.add(new BcelVar(targetVar.getType(), extraParamOffset));
extraParamOffset += targetVar.getType().getSize();
}
for (int i = 0, len = getArgCount(); i < len; i++) {
argVarList.add(argVars[i]);
proceedVarList.add(new BcelVar(argVars[i].getType(), extraParamOffset));
extraParamOffset += argVars[i].getType().getSize();
}
if (thisJoinPointVar != null) {
argVarList.add(thisJoinPointVar);
proceedVarList.add(new BcelVar(thisJoinPointVar.getType(), extraParamOffset));
extraParamOffset += thisJoinPointVar.getType().getSize();
}
Type[] adviceParameterTypes = adviceMethod.getArgumentTypes();
Type[] extractedMethodParameterTypes = extractedMethod.getArgumentTypes();
Type[] parameterTypes =
new Type[extractedMethodParameterTypes.length
+ adviceParameterTypes.length
+ 1];
int parameterIndex = 0;
System.arraycopy(
extractedMethodParameterTypes,
0,
parameterTypes,
parameterIndex,
extractedMethodParameterTypes.length);
parameterIndex += extractedMethodParameterTypes.length;
parameterTypes[parameterIndex++] =
BcelWorld.makeBcelType(adviceMethod.getEnclosingClass().getType());
System.arraycopy(
adviceParameterTypes,
0,
parameterTypes,
parameterIndex,
adviceParameterTypes.length);
LazyMethodGen localAdviceMethod =
new LazyMethodGen(
Modifier.PRIVATE | Modifier.FINAL | Modifier.STATIC,
BcelWorld.makeBcelType(mungerSig.getReturnType()),
adviceMethodName,
parameterTypes,
new String[0],
getEnclosingClass());
String donorFileName = adviceMethod.getEnclosingClass().getInternalFileName();
String recipientFileName = getEnclosingClass().getInternalFileName();
// System.err.println("donor " + donorFileName);
// System.err.println("recip " + recipientFileName);
if (! donorFileName.equals(recipientFileName)) {
localAdviceMethod.fromFilename = donorFileName;
getEnclosingClass().addInlinedSourceFileInfo(
donorFileName,
adviceMethod.highestLineNumber);
}
getEnclosingClass().addMethodGen(localAdviceMethod);
// create a map that will move all slots in advice method forward by extraParamOffset
// in order to make room for the new proceed-required arguments that are added at
// the beginning of the parameter list
int nVars = adviceMethod.getMaxLocals() + extraParamOffset;
IntMap varMap = IntMap.idMap(nVars);
for (int i=extraParamOffset; i < nVars; i++) {
varMap.put(i-extraParamOffset, i);
}
localAdviceMethod.getBody().insert(
BcelClassWeaver.genInlineInstructions(adviceMethod,
localAdviceMethod, varMap, fact, true));
localAdviceMethod.setMaxLocals(nVars);
//System.err.println(localAdviceMethod);
// the shadow is now empty. First, create a correct call
// to the around advice. This includes both the call (which may involve
// value conversion of the advice arguments) and the return
// (which may involve value conversion of the return value). Right now
// we push a null for the unused closure. It's sad, but there it is.
InstructionList advice = new InstructionList();
// InstructionHandle adviceMethodInvocation;
{
for (Iterator i = argVarList.iterator(); i.hasNext(); ) {
BcelVar var = (BcelVar)i.next();
var.appendLoad(advice, fact);
}
// ??? we don't actually need to push NULL for the closure if we take care
advice.append(
munger.getAdviceArgSetup(
this,
null,
(munger.getConcreteAspect().isAnnotationStyleAspect())?
this.loadThisJoinPoint():
new InstructionList(InstructionConstants.ACONST_NULL)));
// adviceMethodInvocation =
advice.append(
Utility.createInvoke(fact, localAdviceMethod)); //(fact, getWorld(), munger.getSignature()));
advice.append(
Utility.createConversion(
getFactory(),
BcelWorld.makeBcelType(mungerSig.getReturnType()),
extractedMethod.getReturnType(),world.isInJava5Mode()));
if (! isFallsThrough()) {
advice.append(InstructionFactory.createReturn(extractedMethod.getReturnType()));
}
}
// now, situate the call inside the possible dynamic tests,
// and actually add the whole mess to the shadow
if (! hasDynamicTest) {
range.append(advice);
} else {
InstructionList afterThingie = new InstructionList(InstructionConstants.NOP);
InstructionList callback = makeCallToCallback(extractedMethod);
if (terminatesWithReturn()) {
callback.append(
InstructionFactory.createReturn(extractedMethod.getReturnType()));
} else {
//InstructionHandle endNop = range.insert(fact.NOP, Range.InsideAfter);
advice.append(
InstructionFactory.createBranchInstruction(
Constants.GOTO,
afterThingie.getStart()));
}
range.append(
munger.getTestInstructions(
this,
advice.getStart(),
callback.getStart(),
advice.getStart()));
range.append(advice);
range.append(callback);
range.append(afterThingie);
}
// now search through the advice, looking for a call to PROCEED.
// Then we replace the call to proceed with some argument setup, and a
// call to the extracted method.
// inlining support for code style aspects
if (!munger.getConcreteAspect().isAnnotationStyleAspect()) {
String proceedName =
NameMangler.proceedMethodName(munger.getSignature().getName());
InstructionHandle curr = localAdviceMethod.getBody().getStart();
InstructionHandle end = localAdviceMethod.getBody().getEnd();
ConstantPoolGen cpg = localAdviceMethod.getEnclosingClass().getConstantPoolGen();
while (curr != end) {
InstructionHandle next = curr.getNext();
Instruction inst = curr.getInstruction();
if ((inst instanceof INVOKESTATIC)
&& proceedName.equals(((INVOKESTATIC) inst).getMethodName(cpg))) {
localAdviceMethod.getBody().append(
curr,
getRedoneProceedCall(
fact,
extractedMethod,
munger,
localAdviceMethod,
proceedVarList));
Utility.deleteInstruction(curr, localAdviceMethod);
}
curr = next;
}
// and that's it.
} else {
//ATAJ inlining support for @AJ aspects
// [TODO document @AJ code rule: don't manipulate 2 jps proceed at the same time.. in an advice body]
InstructionHandle curr = localAdviceMethod.getBody().getStart();
InstructionHandle end = localAdviceMethod.getBody().getEnd();
ConstantPoolGen cpg = localAdviceMethod.getEnclosingClass().getConstantPoolGen();
while (curr != end) {
InstructionHandle next = curr.getNext();
Instruction inst = curr.getInstruction();
if ((inst instanceof INVOKEINTERFACE)
&& "proceed".equals(((INVOKEINTERFACE) inst).getMethodName(cpg))) {