Skip to content

Commit 8819fad

Browse files
committed
333274: more tests and fixes: nested @around advice with proceed
1 parent b858c78 commit 8819fad

File tree

13 files changed

+236
-42
lines changed

13 files changed

+236
-42
lines changed

org.aspectj.matcher/src/main/java/org/aspectj/weaver/Advice.java

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ public static Advice makePerTypeWithinEntry(World world, Pointcut p, ResolvedTyp
8383
ret.concreteAspect = inAspect;
8484
return ret;
8585
}
86+
87+
public boolean isAroundAdvice() {
88+
return attribute.getKind() == AdviceKind.Around;
89+
}
8690

8791
public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType, ResolvedType inAspect,
8892
IHasSourceLocation loc) {

org.aspectj.matcher/src/main/java/org/aspectj/weaver/Shadow.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ public abstract class Shadow {
4646
private ResolvedMember resolvedSignature;
4747
protected final Shadow enclosingShadow;
4848
protected List<ShadowMunger> mungers = Collections.emptyList();
49-
49+
protected boolean needAroundClosureStacking = false;
50+
5051
public int shadowId = nextShadowID++; // every time we build a shadow, it gets a new id
5152

5253
// ----
@@ -628,6 +629,20 @@ protected void prepareForMungers() {
628629
/** Actually implement the (non-empty) mungers associated with this shadow */
629630
private void implementMungers() {
630631
World world = getIWorld();
632+
needAroundClosureStacking = false;
633+
int annotationStyleWithAroundAndProceedCount = 0;
634+
for (ShadowMunger munger: mungers) {
635+
if (munger.getDeclaringType()!= null &&
636+
munger.getDeclaringType().isAnnotationStyleAspect() &&
637+
munger.isAroundAdvice() &&
638+
munger.bindsProceedingJoinPoint()) {
639+
annotationStyleWithAroundAndProceedCount++;
640+
if (annotationStyleWithAroundAndProceedCount>1) {
641+
needAroundClosureStacking = true;
642+
break;
643+
}
644+
}
645+
}
631646
for (ShadowMunger munger : mungers) {
632647
if (munger.implementOn(this)) {
633648
world.reportMatch(munger, this);

org.aspectj.matcher/src/main/java/org/aspectj/weaver/ShadowMunger.java

+8
Original file line numberDiff line numberDiff line change
@@ -303,5 +303,13 @@ public void write(CompressingDataOutputStream stream) throws IOException {
303303
// }
304304
// newShadowMunger.binaryFile = null;
305305
// }
306+
307+
public boolean bindsProceedingJoinPoint() {
308+
return false;
309+
}
310+
311+
public boolean isAroundAdvice() {
312+
return false;
313+
}
306314

307315
}

runtime/src/main/java/org/aspectj/lang/ProceedingJoinPoint.java

+13
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ public interface ProceedingJoinPoint extends JoinPoint {
2929
*/
3030
void set$AroundClosure(AroundClosure arc);
3131

32+
/**
33+
* The joinpoint needs to know about its closure so that proceed can delegate to closure.run().
34+
* This internal method should not be called directly, and won't be visible to the end-user when
35+
* packed in a jar (synthetic method). This should maintain a stack of closures as multiple around
36+
* advice with proceed are targeting a joinpoint and the stack will need to be unwound when
37+
* exiting nested advice. Passing a non null arc indicates a push, passing null indicates a pop.
38+
*
39+
* @param arc the around closure to associate with this joinpoint
40+
*/
41+
default void stack$AroundClosure(AroundClosure arc) {
42+
throw new UnsupportedOperationException();
43+
}
44+
3245
/**
3346
* Proceed with the next advice or target method invocation
3447
*

runtime/src/main/java/org/aspectj/runtime/internal/AroundClosure.java

+21
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,21 @@ public ProceedingJoinPoint linkClosureAndJoinPoint() {
6565
return jp;
6666
}
6767

68+
/**
69+
* This method is called to implicitly associate the closure with the joinpoint
70+
* as required for @AJ aspect proceed()
71+
*
72+
* @param flags indicating whether this/target found at joinpoint and bound
73+
* @return the associated ProceedingJoinPoint
74+
*/
75+
public ProceedingJoinPoint linkStackClosureAndJoinPoint(int flags) {
76+
//TODO is this cast safe ?
77+
ProceedingJoinPoint jp = (ProceedingJoinPoint)state[state.length-1];
78+
jp.stack$AroundClosure(this);
79+
this.bitflags = flags;
80+
return jp;
81+
}
82+
6883
/**
6984
* This method is called to implicitly associate the closure with the joinpoint
7085
* as required for @AJ aspect proceed()
@@ -79,4 +94,10 @@ public ProceedingJoinPoint linkClosureAndJoinPoint(int flags) {
7994
this.bitflags = flags;
8095
return jp;
8196
}
97+
98+
public void unlink() {
99+
ProceedingJoinPoint jp = (ProceedingJoinPoint)state[state.length-1];
100+
jp.stack$AroundClosure(null);
101+
}
102+
82103
}

runtime/src/main/java/org/aspectj/runtime/reflect/JoinPointImpl.java

+48-20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package org.aspectj.runtime.reflect;
1515

16+
import java.util.Stack;
17+
1618
import org.aspectj.lang.JoinPoint;
1719
import org.aspectj.lang.ProceedingJoinPoint;
1820
import org.aspectj.lang.Signature;
@@ -134,47 +136,73 @@ public final String toLongString() {
134136
return staticPart.toLongString();
135137
}
136138

137-
// To proceed we need a closure to proceed on
138-
private AroundClosure arc;
139+
// To proceed we need a closure to proceed on. Generated code
140+
// will either be using arc or arcs but not both. arcs being non-null
141+
// indicates it is in use (even if an empty stack)
142+
private AroundClosure arc = null;
143+
private Stack<AroundClosure> arcs = null;
139144

140145
public void set$AroundClosure(AroundClosure arc) {
141146
this.arc = arc;
142147
}
143148

149+
public void stack$AroundClosure(AroundClosure arc) {
150+
// If input parameter arc is null this is the 'unlink' call from AroundClosure
151+
if (arcs == null) {
152+
arcs = new Stack<AroundClosure>();
153+
}
154+
if (arc==null) {
155+
this.arcs.pop();
156+
} else {
157+
this.arcs.push(arc);
158+
}
159+
}
160+
144161
public Object proceed() throws Throwable {
145162
// when called from a before advice, but be a no-op
146-
if (arc == null)
147-
return null;
148-
else
149-
return arc.run(arc.getState());
163+
if (arcs == null) {
164+
if (arc == null) {
165+
return null;
166+
} else {
167+
return arc.run(arc.getState());
168+
}
169+
} else {
170+
return arcs.peek().run(arcs.peek().getState());
171+
}
150172
}
151173

152174
public Object proceed(Object[] adviceBindings) throws Throwable {
153175
// when called from a before advice, but be a no-op
154-
if (arc == null)
176+
AroundClosure ac = null;
177+
if (arcs == null) {
178+
ac = arc;
179+
} else {
180+
ac = arcs.peek();
181+
}
182+
183+
if (ac == null) {
155184
return null;
156-
else {
157-
185+
} else {
158186
// Based on the bit flags in the AroundClosure we can determine what to
159187
// expect in the adviceBindings array. We may or may not be expecting
160188
// the first value to be a new this or a new target... (see pr126167)
161-
int flags = arc.getFlags();
189+
int flags = ac.getFlags();
162190
boolean unset = (flags & 0x100000) != 0;
163191
boolean thisTargetTheSame = (flags & 0x010000) != 0;
164192
boolean hasThis = (flags & 0x001000) != 0;
165193
boolean bindsThis = (flags & 0x000100) != 0;
166194
boolean hasTarget = (flags & 0x000010) != 0;
167195
boolean bindsTarget = (flags & 0x000001) != 0;
168-
196+
169197
// state is always consistent with caller?,callee?,formals...,jp
170-
Object[] state = arc.getState();
171-
198+
Object[] state = ac.getState();
199+
172200
// these next two numbers can differ because some join points have a this and
173201
// target that are the same (eg. call) - and yet you can bind this and target
174202
// separately.
175-
203+
176204
// In the state array, [0] may be this, [1] may be target
177-
205+
178206
int firstArgumentIndexIntoAdviceBindings = 0;
179207
int firstArgumentIndexIntoState = 0;
180208
firstArgumentIndexIntoState += (hasThis ? 1 : 0);
@@ -202,8 +230,8 @@ public Object proceed(Object[] adviceBindings) throws Throwable {
202230
// This previous variant doesn't seem to cope with only binding target at a joinpoint
203231
// which has both this and target. It forces you to supply this even if you didn't bind
204232
// it.
205-
// firstArgumentIndexIntoAdviceBindings = (hasThis ? 1 : 0) + 1;
206-
// state[hasThis ? 1 : 0] = adviceBindings[hasThis ? 1 : 0];
233+
// firstArgumentIndexIntoAdviceBindings = (hasThis ? 1 : 0) + 1;
234+
// state[hasThis ? 1 : 0] = adviceBindings[hasThis ? 1 : 0];
207235

208236
int targetPositionInAdviceBindings = (hasThis && bindsThis) ? 1 : 0;
209237
firstArgumentIndexIntoAdviceBindings = ((hasThis&&bindsThis)?1:0)+((hasTarget&&bindsTarget&&!thisTargetTheSame)?1:0);
@@ -213,20 +241,20 @@ public Object proceed(Object[] adviceBindings) throws Throwable {
213241
// leave state[0]/state[1] alone, they are OK
214242
}
215243
}
216-
244+
217245
// copy the rest across
218246
for (int i = firstArgumentIndexIntoAdviceBindings; i < adviceBindings.length; i++) {
219247
state[firstArgumentIndexIntoState + (i - firstArgumentIndexIntoAdviceBindings)] = adviceBindings[i];
220248
}
221-
249+
222250
// old code that did this, didnt allow this/target overriding
223251
// for (int i = state.length-2; i >= 0; i--) {
224252
// int formalIndex = (adviceBindings.length - 1) - (state.length-2) + i;
225253
// if (formalIndex >= 0 && formalIndex < adviceBindings.length) {
226254
// state[i] = adviceBindings[formalIndex];
227255
// }
228256
// }
229-
return arc.run(state);
257+
return ac.run(state);
230258
}
231259
}
232260

tests/src/test/java/org/aspectj/systemtest/ajc1611/Ajc1611Tests.java

+11-11
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,17 @@ public void testDeclareField_328840() {
103103
runTest("pr328840");
104104
}
105105

106-
// public void testAnnoStyleAdviceChain_333274() {
107-
// runTest("anno style advice chain");
108-
// }
109-
//
110-
// public void testAnnoStyleAdviceChain_333274_2() {
111-
// runTest("code style advice chain");
112-
// }
113-
//
114-
// public void testAnnoStyleAdviceChain_333274_3() {
115-
// runTest("code style advice chain - no inline");
116-
// }
106+
public void testAnnoStyleAdviceChain_333274() {
107+
runTest("anno style advice chain");
108+
}
109+
110+
public void testAnnoStyleAdviceChain_333274_2() {
111+
runTest("code style advice chain");
112+
}
113+
114+
public void testAnnoStyleAdviceChain_333274_3() {
115+
runTest("code style advice chain - no inline");
116+
}
117117

118118
// ---
119119

tests/src/test/java/org/aspectj/systemtest/ajc193/Ajc193Tests.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* which accompanies this distribution, and is available at
66
* http://www.eclipse.org/legal/epl-v10.html
77
*******************************************************************************/
8-
package org.aspectj.systemtest.ajc193;
8+
package org.aspectj.systemtest.ajc193;
99

1010
import java.io.File;
1111

@@ -18,9 +18,13 @@
1818

1919
/**
2020
* @author Andy Clement
21-
*/
21+
*/
2222
public class Ajc193Tests extends XMLBasedAjcTestCaseForJava10OrLater {
2323

24+
public void testNestedAroundProceed() {
25+
runTest("nested around proceed");
26+
}
27+
2428
public void testDeclareMixinOverweavingControl() {
2529
runTest("overweaving decm - control");
2630
}

tests/src/test/resources/org/aspectj/systemtest/ajc1611/ajc1611.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
</ajc-test>
118118

119119
<ajc-test dir="bugs1611/pr333274" title="code style advice chain">
120-
<compile files="ma2/Annotation1.java ma2/aspect1/Aspect1.java ma2/aspect3/Aspect3.java ma2/Main.java ma2/Precedence.java" options="-1.5 "/>
120+
<compile files="ma2/Annotation1.java ma2/aspect1/Aspect1.java ma2/aspect3/Aspect3.java ma2/Main.java ma2/Precedence.java" options="-1.5 -XnoInline"/>
121121
<run class="ma2.Main">
122122
<stdout>
123123
<line text="&gt;In Aspect1"/>

tests/src/test/resources/org/aspectj/systemtest/ajc190/ajc190.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@
7777
</ajc-test>
7878

7979
<ajc-test dir="bugs190/modules/fff" title="compile module including aspects">
80-
<compile files="module-info.java pkg/Demo.java otherpkg/Azpect.java" modulepath="$runtime" outjar="demomodule.jar" options="-1.9"/>
81-
<run modulepath="$runtime:demomodule.jar" module="demo/pkg.Demo">
80+
<compile files="module-info.java pkg/Demo.java otherpkg/Azpect.java" modulepath="$runtimemodule" outjar="demomodule.jar" options="-1.9"/>
81+
<run modulepath="$runtimemodule:demomodule.jar" module="demo/pkg.Demo">
8282
<stdout>
8383
<line text="Azpect running"/>
8484
<line text="Demo running"/>

tests/src/test/resources/org/aspectj/systemtest/ajc193/ajc193.xml

+36
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,42 @@
22

33
<suite>
44

5+
<ajc-test dir="bugs193/333274" vm="1.8" title="nested around proceed">
6+
<compile files="ma/aspect2/Aspect2.java,ma/aspect2/Annotation2.java,ma/aspect3/Aspect3.java,ma/aspect3/Annotation3.java,ma/Precedence.java,ma/Main.java,ma/aspect1/Aspect1.java,ma/aspect1/Annotation1.java" options="-showWeaveInfo -1.8 -XnoInline">
7+
<message kind="weave" text="Join point 'method-execution(int ma.Main$Dummy.retryTranslateAndTimeLimited())' in Type 'ma.Main$Dummy' (Main.java:16) advised by around advice from 'ma.aspect3.Aspect3' (Aspect3.java:11)"/>
8+
<message kind="weave" text="Join point 'method-execution(int ma.Main$Dummy.retryTranslateAndTimeLimited())' in Type 'ma.Main$Dummy' (Main.java:16) advised by around advice from 'ma.aspect2.Aspect2' (Aspect2.java:11)"/>
9+
<message kind="weave" text="Join point 'method-execution(int ma.Main$Dummy.retryTranslateAndTimeLimited())' in Type 'ma.Main$Dummy' (Main.java:16) advised by around advice from 'ma.aspect1.Aspect1' (Aspect1.java:12)"/>
10+
</compile>
11+
<!--
12+
>In Aspect1
13+
>In Aspect2
14+
>In Aspect3
15+
Method call
16+
<In Aspect3
17+
<In Aspect2
18+
=In Aspect1
19+
Method call
20+
<In Aspect1
21+
-->
22+
<run class="ma.Main">
23+
<stdout>
24+
<line text="&gt;In Aspect1"/>
25+
<line text="&gt;In Aspect2"/>
26+
<line text="&gt;In Aspect3"/>
27+
<line text="Method call"/>
28+
<line text="&lt;In Aspect3"/>
29+
<line text="&lt;In Aspect2"/>
30+
<line text="=In Aspect1"/>
31+
<line text="&gt;In Aspect2"/>
32+
<line text="&gt;In Aspect3"/>
33+
<line text="Method call"/>
34+
<line text="&lt;In Aspect3"/>
35+
<line text="&lt;In Aspect2"/>
36+
<line text="&lt;In Aspect1"/>
37+
</stdout>
38+
</run>
39+
</ajc-test>
40+
541
<ajc-test dir="bugs193/543657" vm="1.8" title="overweaving decm - control">
642
<compile files="MoodIndicator.java,Code1.java" options="-showWeaveInfo -1.8">
743
<message kind="weave" text="Mixing interface 'MoodIndicator$Moody' (MoodIndicator.java) into type 'Code1' (Code1.java)"/>

weaver/src/main/java/org/aspectj/weaver/bcel/BcelAdvice.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,17 @@ public BcelAdvice(AjAttribute.AdviceAttribute attribute, Pointcut pointcut, Memb
7676
super(attribute, pointcut, simplify(attribute.getKind(), adviceSignature));
7777
this.concreteAspect = concreteAspect;
7878
}
79-
79+
80+
public boolean bindsProceedingJoinPoint() {
81+
UnresolvedType[] parameterTypes = signature.getParameterTypes();
82+
for (int i=0;i<parameterTypes.length;i++) {
83+
if (parameterTypes[i].equals(UnresolvedType.PROCEEDING_JOINPOINT)) {
84+
return true;
85+
}
86+
}
87+
return false;
88+
}
89+
8090
/**
8191
* A heavyweight BcelMethod object is only required for around advice that will be inlined. For other kinds of advice it is
8292
* possible to save some space.
@@ -603,6 +613,7 @@ public InstructionList getAdviceArgSetup(BcelShadow shadow, BcelVar extraVar, In
603613
} else {
604614
previousIsClosure = true;
605615
il.append(closureInstantiation.copy());
616+
shadow.closureVarInitialized = true;
606617
}
607618
}
608619
} else if ("Lorg/aspectj/lang/JoinPoint$StaticPart;".equals(getSignature().getParameterTypes()[i]

0 commit comments

Comments
 (0)