Bug 113447 - VerifyError overriding pointcut with context used in around advice
Summary: VerifyError overriding pointcut with context used in around advice
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.0M4   Edit
Hardware: PC Windows XP
: P2 major (vote)
Target Milestone: 1.5.0RC1   Edit
Assignee: Andrew Clement CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 113448 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-10-22 04:03 EDT by Wes Isberg CLA
Modified: 2012-04-03 16:01 EDT (History)
0 users

See Also:


Attachments
patch containing simplified testcase (2.25 KB, patch)
2005-10-26 03:53 EDT, Helen Beeken CLA
no flags Details | Diff
zip file containing updated testcase patch and patch containing fix (1.69 KB, application/zip)
2005-10-27 06:55 EDT, Helen Beeken CLA
no flags Details
patch containing testcases (11.01 KB, patch)
2005-10-28 10:43 EDT, 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-10-22 04:03:06 EDT
Get 

----
java.lang.VerifyError: (class: bugs/DoubleAround, method: one signature: ()V)
Unable to pop operand off an empty stack
----

running 

----

public class DoubleAround {

    public static void main(String[] args) {
        DoubleAround me = new DoubleAround();
        me.one();
      //  me.two();
    }
    void one() {}
    //void two() {}

    static abstract aspect Super {
        int count = 0;
        pointcut pc(DoubleAround s) : this(s) && execution(void one());
        void around(DoubleAround s) : pc(s) {
            proceed(s);
            System.out.println(thisJoinPoint 
                    + " count: " + count++ 
                    + " by " + this
                    + " of " + s);
        }
    }
    static aspect Sub extends Super {        
        // no VerifyError if this is commented out
        pointcut pc(DoubleAround s) : Super.pc(s) 
        || (this(s) && execution(void two()));
    }
}
----
Comment 1 Wes Isberg CLA 2005-10-24 13:39:16 EDT
*** Bug 113448 has been marked as a duplicate of this bug. ***
Comment 2 Helen Beeken CLA 2005-10-26 03:53:29 EDT
Created attachment 28781 [details]
patch containing simplified testcase

Apply this patch to the tests project.

Running this test with the PointcutRewriter.WATCH_PROGRESS flag set to true
gives the following output:

Initial pointcut is	   ==> (persingleton(Super) &&
((this(BindingTypePattern(PR113447, 0)) && execution(void method1())) ||
(this(BindingTypePattern(PR113447, 0)) && execution(void method2()))))
Distributing NOT gives	   ==> (persingleton(Super) &&
((this(BindingTypePattern(PR113447, 0)) && execution(void method1())) ||
(this(BindingTypePattern(PR113447, 0)) && execution(void method2()))))
Pull up disjunctions gives ==> ((persingleton(Super) &&
(this(BindingTypePattern(PR113447, 0)) && execution(void method1()))) ||
(persingleton(Super) && (this(BindingTypePattern(PR113447, 0)) &&
execution(void method2()))))
Simplifying ANDs gives	   ==> (((execution(void method1()) &&
this(BindingTypePattern(PR113447, 0))) && persingleton(Super)) ||
((execution(void method2()) && this(BindingTypePattern(PR113447, 0))) &&
persingleton(Super)))
Sorting ORs gives	   ==> (((execution(void method1()) &&
this(BindingTypePattern(PR113447, 0))) && persingleton(Super)) ||
((execution(void method2()) && this(BindingTypePattern(PR113447, 0))) &&
persingleton(Super)))
Comment 3 Helen Beeken CLA 2005-10-26 04:44:09 EDT
Comparing the decompiled output of two methods where one is matched by the pointcut:

pointcut pc2(PR113447 s): this(s) && execution(void method3());

(which runs fine) and the other is matched by the pointcut:

pointcut pc(PR113447 s) : 
		(this(s) && execution(void method1()))
		|| (this(s) && execution(void method2()));

(which causes the verify error) the output for method3 is

0:   invokestatic    #34; //Method Super.aspectOf:()LSuper;
3:   aload_0                                              <<--- extra line
4:   invokevirtual   #41; //Method Super.ajc$before$Super$1$c4a93b3f:(LPR113447 ;)V
7:   return
LocalVariableTable:
Start  Length  Slot  Name   Signature
7      0      0    this       LPR113447;

whereas the output for method1() is:

0:   invokestatic    #34; //Method Super.aspectOf:()LSuper;
3:   invokevirtual   #38; //Method Super.ajc$before$Super$2$657ed6b:(LPR11344
7;)V
6:   return
LocalVariableTable:
Start  Length  Slot  Name   Signature
6      0      0    this       LPR113447;

It therefore looks like in the case where there are two this pcd's, "this" isn't
being put on the stack.
Comment 4 Helen Beeken CLA 2005-10-26 05:55:45 EDT
The problem is that in the case of method1 (see comment #3), the first time we
enter ThisOrTargetPointcut.findResidueInternal(..) the test

(state.get(btp.getFormalIndex())!=null) && (lastMatchedShadowId != shadow.shadowId)

is false. However, the second time we enter (for the second "this" pcd) the test
is true and we record that an error has been marked against it (although no
error is printed or logged). Then, in BcelAdvice.getAdviceArgSetup(..) we check
whether an error has been marked against it and continue without doing anything
if it has. Consequently, "this" is not put on the stack.
Comment 5 Helen Beeken CLA 2005-10-27 06:55:43 EDT
Created attachment 28840 [details]
zip file containing updated testcase patch and patch containing fix

The attached zip contains two patches:

- pr113447-tests-patch2.txt - apply this to the tests project
- pr113447-weaver-patch.txt - apply this to the weaver project

The fix involved changing the logic mentioned in comment #4 to first check
whether the formal has been bound and that we're looking at a different object
to that which did the binding. If this is true then we further check whether
this pointcut was involved in the matching. If it wasn't then we return "no
residue", otherwise we mark this pointcut as having reported an error against
it.
Comment 6 Helen Beeken CLA 2005-10-27 09:01:33 EDT
The following program:

@Retention(RetentionPolicy.RUNTIME)
@interface Annotation{};

@Annotation
public class PR113447a {

	public static void main(String[] args) {
		PR113447a me = new PR113447a();
		me.method1();
		me.method3();
	}
	
	public void method1(){}

	public void method3(){}
}

aspect Super {

	pointcut pc1(Annotation a) : 
		(@this(a) && execution(void method1()))
		|| (@this(a) && execution(void method2()));

	before(Annotation a) : pc1(a) {
	}
}

also fails with the same VerifyError. The fix therefore needs to also be applied
to the ThisOrTargetAnnotationPointcut class.
Comment 7 Andrew Clement CLA 2005-10-27 09:50:35 EDT
first patch integrated.
Comment 8 Helen Beeken CLA 2005-10-28 10:36:26 EDT
A summary of progress/investigation so far:

The verifyErrors are coming from calls to ExposedState.setErroneousVar(Var) and
there are 6 places where this called:

- ThisOrTargetPointcut.findResidueInternal(Shadow, ExposedState)
- ThisOrTargetAnnotationPointcut.findResidueInternal(Shadow, ExposedState)
- WithinAnnotationPointcut.findResidueInternal(Shadow, ExposedState)
- WithinCodeAnnotationPointcut.findResidueInternal(Shadow, ExposedState)
- AnnotationPointcut.findResidueInternal(Shadow, ExposedState)
- ArgsPointcut.findResidueInternal(Shadow, ExposedState)

Looking at each of these separately:

ThisOrTargetPointcut
--------------------

The call to setErroneousVar(..) is reached when you have a pointcut which
contains two this statements, both bound to the same thing i.e.

(this(s) && call(void myMethod1()) || (this(s) && call(void myMethod2())

Here, when we enter findResidueInternal for the myMethod1 shadow the LHS has
matched and we haven't been here before, so everything's ok. Then when we enter
again for myMethod1 but with the "this" from the RHS, we have previously found a
"this", however, with a different object. By checking whether the field
"lastMatchedShadowId" is equal to the id of the myMethod1 shadow we can tell if
the current "this" was involved in the matching of myMethod1. In the example
above it isn't so we return without doing anything. The only way we would mark
the current "this" (or "target") to be "erroneous" is if we're not looking at
the same object (as in the case when a pointcut is used in multiple advice) and
it was used in the matching of the given shadow.

This fix has been checked in in comment #7.

ArgsPointcut
------------

This is similar to ThisOrTargetPointcut in that the call to setErroneousVar(..)
can be reached through pointcuts like

(args(i) && call(void method1(int))) || (args(i) && call(void method2(int)))

However, I think the fix should be slightly different because the information
which is available at this point is slightly different than the
ThisOrTargetPointcut case. I haven't had a chance to look into this in much
detail yet.


ThisOrTargetAnnotationPointcut
------------------------------

The call to setErroneousVar(..) is reached when you have a pointcut which
contains two @this statements i.e.

(@this(a) && execution(void myMethod1()))  || (@this(a) && execution(void
myMethod2())).

The reasoning here is similar to the ThisOrTargetPointcut case, apart from the
fact that we should return if the "lastMatchedShadowId" field is equal to the id
of the myMethod1 shadow id (rather than vice versa). The reason for this being
that we match this pointcut again at the beginning of the findResidueInternal
method and as expected, it does match.


WithinAnnotationPointcut
------------------------

The call to setErroneousVar(..) is currently reached if you have two @within
pointcuts which both have the "lastMatchedShadowId" equal to the current shadow
id. The following pointcut doesn't cause any problems

(@within(a) && execution(void myMethod1()))  || (@within(a) && execution(void
myMethod2())).

because on the second time round the "lastMatchedShadowId" doesn't equal the
current shadow id. However, a pointcut of the form

@within(a) && (call(void method4(int)) || call(void method5(int)));

does cause a verify error (different to the one reported in this bug - (class:
PR113447b, method: main signature: ([Ljava/lang/String;)V) Expecting to find
object/array on stack). In this case the two objects are the same and so, if the
fix for ThisOrTargetPointcut is applied to WithinAnnotationPointcut the object
equality check fixes this problem. I'm not sure how much sense it is to check
whether "lastMatchedShadowId" equals the current shadow id though.......


WithincodeAnnotationPointcut
----------------------------

This is the same as WithinAnnotationPointcut - the call to setErroneousVar(..)
is reached if you have a pointcut of the form:

@withincode(a) && (call(void method4(int)) || call(void method5(int)));

when again the two objects are the same. Also, a pointcut of the form:

(@withincode(a) && execution(void myMethod1()))  || (@withincode(a) &&
execution(void myMethod2())).

doesn't cause a verify error because in this case the lastMatchedShadowId isn't
equal to the current shadow id.

AnnotationPointcut
------------------

This is the same as WithinAnnotationPointcut and WithincodeAnnotationPointcut.
The call to setErroneousVar(..) is reached if you have a pointcut of the form:

@annotation(a) && (call(void method4(int)) || call(void method5(int)));

when the two objects are the same.Again, pointcuts of the form:

(@annotation(a) && call(void method4(int))) || (@annotation(a) && call(void
method5(int)))

do not enter this code because the lastMatchedShadowId isn't equal to the
current shadow id.

Comment 9 Helen Beeken CLA 2005-10-28 10:43:16 EDT
Created attachment 28922 [details]
patch containing testcases

Apply the patch to the tests project.

This contains testcases for all 6 situations detailed in the above comment
(note that because there are now more tests for this bug, the patch also moves
the checked in test to the same directory to keep all the test classes
together)
Comment 10 Andrew Clement CLA 2005-11-11 06:24:56 EST
I've fixed this based on Helens investigations and also applied her testcases. 
The problem is that when the rewriting was put in, we added code to check for
ambiguous bindings across a pointcut and since this was added there was no
longer a need to check it at every single shadow.  As discussed, checking it at
every shadow is problematic.  So, I removed all the messy code to check it at
each shadow.  All the tests pass - even those that verify ambiguous binding
messages come out correctly.

waiting on build before closing.
Comment 11 Andrew Clement CLA 2005-11-11 08:47:40 EST
full fix available.
Comment 12 Adrian Colyer CLA 2005-11-14 15:19:20 EST
hooray - that's great news...