Bug 115235 - StackOverflowError on circular pointcut iff aspect parameterized
Summary: StackOverflowError on circular pointcut iff aspect parameterized
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.0M4   Edit
Hardware: PC Windows XP
: P2 normal (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Helen Beeken CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-11-06 07:26 EST by Wes Isberg CLA
Modified: 2012-04-03 15:53 EDT (History)
0 users

See Also:


Attachments
patch containing fix (1.64 KB, patch)
2005-11-14 04:39 EST, Helen Beeken CLA
aclement: iplog+
Details | Diff
patch containing testcases (4.12 KB, patch)
2005-11-14 04:42 EST, Helen Beeken CLA
aclement: iplog+
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Wes Isberg CLA 2005-11-06 07:26:57 EST
The code below overflows when concretizing a circular pointcut from a
parameterized aspect.  Not true when the aspect is not parameterized.

---------------------------------------------
public class SelfPC {

	public static void main(String[] args) {
		new C().foo();
	}
	static class C { 
		pointcut doit() : C.doit(); // CE expected
		void foo() {} 
	}
	
	// ------------ pertarget<T>
	static abstract aspect PT_PARM<T> pertarget(pc()) {
		abstract protected pointcut pc();
		before() : pc() {}
	}
	static aspect CPT_PARM extends PT_PARM<C> {
		protected pointcut pc() : C.doit();
	}
	
//	// ------------ issingleton<T>
//	static abstract aspect IS_PARM<T> {
//		abstract protected pointcut pc();
//		before() : pc() {}
//	}
//	static aspect CIS_PARM extends IS_PARM<C> {
//		protected pointcut pc() : C.doit();
//	}
//	// ------------ pertarget
//	static abstract aspect PT pertarget(pc()) {
//		abstract protected pointcut pc();
//		before() : pc() {}
//	}
//	static aspect CPT extends PT{
//		protected pointcut pc() : C.doit();
//	}
//	// ------------ issingleton
//	static abstract aspect SIS {
//		abstract protected pointcut pc();
//		before() : pc() {}
//	}
//	static aspect CSIS extends SIS {
//		protected pointcut pc() : C.doit();
//	}

}
---------------------------------------------
java.lang.StackOverflowError
at java.lang.StringBuffer.append(StringBuffer.java:225)
at org.aspectj.weaver.UnresolvedType.nameToSignature(UnresolvedType.java:734)
at org.aspectj.weaver.UnresolvedType.forName(UnresolvedType.java:308)
at
org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory.fromBinding(EclipseFactory.java:302)
at
org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory.fromEclipse(EclipseFactory.java:129)
at
org.aspectj.ajdt.internal.compiler.lookup.EclipseSourceType.getSuperclass(EclipseSourceType.java:119)
at org.aspectj.weaver.ReferenceType.getSuperclass(ReferenceType.java:481)
at org.aspectj.weaver.ResolvedType.getDirectSupertypes(ResolvedType.java:65)
at org.aspectj.weaver.ResolvedType$6.get(ResolvedType.java:447)
at org.aspectj.weaver.Iterators$4.next(Iterators.java:148)
at org.aspectj.weaver.Iterators$3$1.hasNext(Iterators.java:117)
at org.aspectj.weaver.Iterators$3.hasNext(Iterators.java:128)
at org.aspectj.weaver.ResolvedType.findPointcut(ResolvedType.java:466)
at
org.aspectj.weaver.patterns.ReferencePointcut.concretize1(ReferencePointcut.java:269)
at org.aspectj.weaver.patterns.Pointcut.concretize(Pointcut.java:229)
at
org.aspectj.weaver.patterns.ReferencePointcut.concretize1(ReferencePointcut.java:326)
at org.aspectj.weaver.patterns.Pointcut.concretize(Pointcut.java:229)
...
---------------------------------------------
Comment 1 Helen Beeken CLA 2005-11-09 08:41:57 EST
I've managed to recreate the problem with the following simplified testcase:

abstract aspect ParameterizedAbstractAspect<T>{
	abstract protected pointcut pc();
	before() : pc() {}
}

aspect ParameterizedConcrete extends
ParameterizedAbstractAspect<ParameterizedConcrete> {
	protected pointcut pc() : pc();
}

The reason the StackOverflowError is occuring is in
ReferencePointcut.concretize1(...). In this method, there is a flag called
"concretizing" which is initially false but is set to true when concretizing is
taking place. The method begins with a check on whether or not "concretizing" is
true and if it is then the expected error message is produced - 'circular
pointcut declaration involving: pc()' type message. If it's false, then we go
ahead and try to concretize. 

The part of the method which is causing the problems is:

Pointcut ret = pointcutDec.getPointcut();
if (typeVariableMap != null) ret = ret.parameterizeWith(typeVariableMap);
return ret.concretize(searchStart, declaringType, newBindings);

(note that the implementation of concretize calls the concretize1 method).
If the aspect is not parameterized then typeVariableMap == null. However, if it
is parameterized then typeVariableMap is non-null. In this case, ret becomes a
new object (due to the implementation of
ReferencePointcut.parameterizeWith(..)). So, for example, if we're looking at
non-parameterized aspects, we get the following info at the trouble spot in
concretize1:

first pass in concretize1:  this: id = 144 concretizing is set to true, 
                            ret : id = 176 concretizing is still false
second pass in concretize1: this: id = 176 concretizing is set to true
                            ret : id = 176 concretizing is still true
third pass in concretize1:  this: id = 176 concretizing is true so get compiler 
                                           warning as expected

However, if we're looking at parameterized aspects, we get the following:

first pass in concretize1:  
  this                                     : id = 47 concretizing is set to true
  ret (before call to parameterizeWith(..) : id = 92 concretizing is false
  ret (after call to parameterizeWith(..)  : id = 95 concretizing is false
 
second pass in concretize1:  
  this                                     : id = 95 concretizing is set to true
  ret (before call to parameterizeWith(..) : id = 92 concretizing is false
  ret (after call to parameterizeWith(..)  : id = 135 concretizing is false

third pass in concretize1:  
  this                                     : id = 135 concretizing set to true
  ret (before call to parameterizeWith(..) : id = 92 concretizing is false
  ret (after call to parameterizeWith(..)  : id = 147 concretizing is false

this goes on and on..... and hence the StackOverflowError.
Comment 2 Helen Beeken CLA 2005-11-14 04:38:45 EST
The fix is to add a protected boolean field hasBeenParameterized to the abstract
class Pointcut. Then add an extra check in the problem code mentioned in comment
#1, changing it from 

if (typeVariableMap != null) ret = ret.parameterizeWith(typeVariableMap);

to

if (typeVariableMap != null && !hasBeenParameterized) {					
    ret = ret.parameterizeWith(typeVariableMap);
    ret.hasBeenParameterized=true;
}

It is necessary to update the field here rather than in the
ReferencePointcut.parameterizeWith(...) method, because parameterizeWith is
called several times from different places whereas you only concretize once.
Comment 3 Helen Beeken CLA 2005-11-14 04:39:42 EST
Created attachment 29852 [details]
patch containing fix

Apply the patch to the weaver project.

The patch contains the fix described in the previous comment.
Comment 4 Helen Beeken CLA 2005-11-14 04:42:10 EST
Created attachment 29853 [details]
patch containing testcases

Apply this patch to the tests project.

This patch contains the original failing testcase in file pr115325.aj. I've
also included another testcase in file pr115325b.aj which came about from me
investigating difference scenarios. I'm not sure whether it should be included
in the suite, however, I thought I would include it in the patch just in case
:-)
Comment 5 Andrew Clement CLA 2005-11-15 06:39:05 EST
fix available.