/*
* Some fun implementation stuff:
- *
+ *
* * expressionKind advice is non-execution advice
- * * may have a target.
- * * if the body is extracted, it will be extracted into
- * a static method. The first argument to the static
+ * * may have a target.
+ * * if the body is extracted, it will be extracted into
+ * a static method. The first argument to the static
* method is the target
* * advice may expose a this object, but that's the advice's
* consideration, not ours. This object will NOT be cached in another
* local, but will always come from frame zero.
- *
- * * non-expressionKind advice is execution advice
+ *
+ * * non-expressionKind advice is execution advice
* * may have a this.
* * target is same as this, and is exposed that way to advice
* (i.e., target will not be cached, will always come from frame zero)
* * if the body is extracted, it will be extracted into a method
- * with same static/dynamic modifier as enclosing method. If non-static,
- * target of callback call will be this.
+ * with same static/dynamic modifier as enclosing method. If non-static,
+ * target of callback call will be this.
*
- * * because of these two facts, the setup of the actual arguments (including
+ * * because of these two facts, the setup of the actual arguments (including
* possible target) callback method is the same for both kinds of advice:
* push the targetVar, if it exists (it will not exist for advice on static
- * things), then push all the argVars.
- *
+ * things), then push all the argVars.
+ *
* Protected things:
*
* * the above is sufficient for non-expressionKind advice for protected things,
* since the target will always be this.
- *
+ *
* * For expressionKind things, we have to modify the signature of the callback
* method slightly. For non-static expressionKind things, we modify
* the first argument of the callback method NOT to be the type specified
* by the method/field signature (the owner), but rather we type it to
* the currentlyEnclosing type. We are guaranteed this will be fine,
* since the verifier verifies that the target is a subtype of the currently
- * enclosingType.
- *
+ * enclosingType.
+ *
* Worries:
- *
+ *
* * ConstructorCalls will be weirder than all of these, since they
* supposedly don't have a target (according to AspectJ), but they clearly
* do have a target of sorts, just one that needs to be pushed on the stack,
* dupped, and not touched otherwise until the constructor runs.
- *
+ *
* @author Jim Hugunin
* @author Erik Hilsdale
- *
+ *
*/
public class BcelShadow extends Shadow {
* The new/dup (or new/dup_x1/swap) are removed and will be readded later (after the advice call) by the caller of this method.
* The groovy compiler produces unusual code where the new/dup isn't visible (when making a this() call from an existing ctor),
* an aload_0 is used to load the uninitialized object (as an example see the ctors in grails.util.BuildSettings).
- *
+ *
* @return true if managed to remove them
*/
private boolean deleteNewAndDup() {
/**
* Create an initialization join point associated with a constructor, but not with any body of code yet. If this is actually
* matched, it's range will be set when we inline self constructors.
- *
+ *
* @param constructor The constructor starting this initialization.
*/
public static BcelShadow makeUnfinishedInitialization(BcelWorld world, LazyMethodGen constructor) {
private Map<ResolvedType, AnnotationAccessVar> withinAnnotationVars = null;
private Map<ResolvedType, AnnotationAccessVar> withincodeAnnotationVars = null;
private boolean allArgVarsInitialized = false;
-
- // If in annotation style and the relevant advice is using PJP then this will
+
+ // If in annotation style and the relevant advice is using PJP then this will
// be set to true when the closure variable is initialized - if it gets set
// (which means link() has been called) then we will need to call unlink()
// after the code has been run.
/**
* Get the Var for the xxxxJpStaticPart, xxx = this or enclosing
- *
+ *
* @param isEnclosingJp true to have the enclosingJpStaticPart
* @return
*/
/**
* Get the Var for the enclosingJpStaticPart
- *
+ *
* @return
*/
public BcelVar getThisEnclosingJoinPointStaticPartBcelVar() {
/**
* 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) {
* 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)
- *
+ *
* Sometimes the 'last return' isnt the right one - some rogue code can include the real return from the body of a subroutine
* that exists at the end of the method. In this case the last return is RETURN but that may not be correct for a method with a
* non-void return type... pr151673
- *
+ *
* @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
/**
* Get the list of instructions used to dispatch to the after advice
- *
+ *
* @param munger
* @param firstInstructionInReturnSequence
* @return
/**
* 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
*/
/**
* 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
}
ResolvedType aspectRT = munger.getConcreteAspect();
BcelWorld.getBcelObjectType(aspectRT);
-
+
// Although matched, if the visibility rules prevent the aspect from seeing this type, don't
// insert any code (easier to do it here than try to affect the matching logic, unfortunately)
if (!(tResolved.canBeSeenBy(aspectRT) || aspectRT.isPrivilegedAspect())) {
return;
}
-
+
final InstructionFactory fact = getFactory();
InstructionList entryInstructions = new InstructionList();
/*
* Implementation notes:
- *
+ *
* AroundInline still extracts the instructions of the original shadow into an extracted method. This allows inlining of even
* that advice that doesn't call proceed or calls proceed more than once.
- *
+ *
* It extracts the instructions of the original shadow into a method.
- *
+ *
* Then it extracts the instructions of the advice into a new method defined on this enclosing class. This new method can then
* be specialized as below.
- *
+ *
* Then it searches in the instructions of the advice for any call to the proceed method.
- *
+ *
* At such a call, there is stuff on the stack representing the arguments to proceed. Pop these into the frame.
- *
+ *
* Now build the stack for the call to the extracted method, taking values either from the join point state or from the new
* frame locs from proceed. Now call the extracted method. The right return value should be on the stack, so no cast is
* necessary.
- *
+ *
* If only one call to proceed is made, we can re-inline the original shadow. We are not doing that presently.
- *
+ *
* If the body of the advice can be determined to not alter the stack, or if this shadow doesn't care about the stack, i.e.
* method-execution, then the new method for the advice can also be re-lined. We are not doing that presently.
*/
String extractedShadowMethodName = NameMangler.aroundShadowMethodName(getSignature(), shadowClass.getNewGeneratedNameTag());
List<String> parameterNames = new ArrayList<String>();
boolean shadowClassIsInterface = shadowClass.isInterface();
- LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName,
+ LazyMethodGen extractedShadowMethod = extractShadowInstructionsIntoNewMethod(extractedShadowMethodName,
shadowClassIsInterface?Modifier.PUBLIC:Modifier.PRIVATE,
munger.getSourceLocation(), parameterNames,shadowClassIsInterface);
/**
* Annotation style handling for inlining.
- *
+ *
* Note: The proceedingjoinpoint is already on the stack (since the user was calling pjp.proceed(...)
- *
+ *
* The proceed map is ignored (in terms of argument repositioning) since we have a fixed expected format for annotation style.
* The aim here is to change the proceed() call into a call to the xxx_aroundBody0 method.
- *
- *
+ *
+ *
*/
private InstructionList getRedoneProceedCallForAnnotationStyle(InstructionFactory fact, LazyMethodGen callbackMethod,
BcelAdvice munger, LazyMethodGen localAdviceMethod, List<BcelVar> argVarList, boolean isProceedWithArgs) {
int linenumber = getSourceLine();
// MOVE OUT ALL THE INSTRUCTIONS IN MY SHADOW INTO ANOTHER METHOD!
-
+
// callbackMethod will be something like: "static final void m_aroundBody0(I)"
boolean shadowClassIsInterface = getEnclosingClass().isInterface();
LazyMethodGen callbackMethod = extractShadowInstructionsIntoNewMethod(
}
closureVarInitialized = false;
-
+
// ATAJ for @AJ aspect we need to link the closure with the joinpoint instance
if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect()
&& munger.getDeclaringAspect() != null && munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()) {
aroundClosureInstance = genTempVar(AjcMemberMaker.AROUND_CLOSURE_TYPE);
closureInstantiation.append(fact.createDup(1));
aroundClosureInstance.appendStore(closureInstantiation, fact);
-
+
// stick the bitflags on the stack and call the variant of linkClosureAndJoinPoint that takes an int
closureInstantiation.append(fact.createConstant(Integer.valueOf(bitflags)));
if (needAroundClosureStacking) {
closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(),
new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"),
Modifier.PUBLIC, "linkStackClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));
-
+
} else {
closureInstantiation.append(Utility.createInvoke(getFactory(), getWorld(),
new MemberImpl(Member.METHOD, UnresolvedType.forName("org.aspectj.runtime.internal.AroundClosure"),
- Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));
+ Modifier.PUBLIC, "linkClosureAndJoinPoint", String.format("%s%s", "(I)", "Lorg/aspectj/lang/ProceedingJoinPoint;"))));
}
}
// invoke the advice
InstructionHandle tryUnlinkPosition = advice.append(munger.getNonTestAdviceInstructions(this));
-
+
if (needAroundClosureStacking) {
// Call AroundClosure.unlink() in a 'finally' block
if (munger.getConcreteAspect() != null && munger.getConcreteAspect().isAnnotationStyleAspect()
&& munger.getDeclaringAspect() != null
&& munger.getDeclaringAspect().resolve(world).isAnnotationStyleAspect()
&& closureVarInitialized) {
-
+
// Call unlink when 'normal' flow occurring
aroundClosureInstance.appendLoad(advice, fact);
InstructionHandle unlinkInsn = advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType
.forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink",
"()V")));
-
+
InstructionHandle jumpOverHandler = advice.append(InstructionConstants.NOP);
-
+
// Call unlink in finally block
- InstructionHandle handlerStart = advice.append(InstructionConstants.POP);
- aroundClosureInstance.appendLoad(advice, fact);
+
+ // Do not POP the exception off, we need to rethrow it
+ InstructionHandle handlerStart = advice.append(aroundClosureInstance.createLoad(fact));
advice.append(Utility.createInvoke(getFactory(), getWorld(), new MemberImpl(Member.METHOD, UnresolvedType
.forName("org.aspectj.runtime.internal.AroundClosure"), Modifier.PUBLIC, "unlink",
"()V")));
- advice.append(InstructionConstants.ACONST_NULL);
+ // After that exception is on the top of the stack again
advice.append(InstructionConstants.ATHROW);
InstructionHandle jumpTarget = advice.append(InstructionConstants.NOP);
jumpOverHandler.setInstruction(InstructionFactory.createBranchInstruction(Constants.GOTO, jumpTarget));
enclosingMethod.addExceptionHandler(tryUnlinkPosition, unlinkInsn, handlerStart, null/* ==finally */, false);
- }
+ }
}
-
+
advice.append(returnConversionCode);
if (getKind() == Shadow.MethodExecution && linenumber > 0) {
advice.getStart().addTargeter(new LineNumberTag(linenumber));
}
/**
- *
+ *
* @param callbackMethod the method we will call back to when our run method gets called.
* @param proceedMap A map from state position to proceed argument position. May be non covering on state position.
*/
/**
* Extract the instructions in the shadow to a new method.
- *
+ *
* @param extractedMethodName name for the new method
* @param extractedMethodVisibilityModifier visibility modifiers for the new method
* @param adviceSourceLocation source location of the advice affecting the shadow