Bug 136026 - cflow VerifyError
Summary: cflow VerifyError
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.5.1   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: 1.5.2   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-04-10 21:41 EDT by Wes Isberg CLA
Modified: 2006-05-12 08:36 EDT (History)
0 users

See Also:


Attachments
.class files produced by ajc 1.5.1 (7.68 KB, application/octet-stream)
2006-04-10 21:42 EDT, Wes Isberg CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Wes Isberg CLA 2006-04-10 21:41:44 EDT
While following up a mailing list question, I blundered into a bug I don't understand.  Same result in 1.5.1 (command-line) and 1.5.1a (via AJDT 1.3.1).  

---------------- output
Starting CflowOrder.main(..)
Exception in thread "main" java.lang.VerifyError: (class: bugs/CflowOrder$A, method: foo signature: ()V) Incompatible object argument for function call
	at bugs.CflowOrder.main(CflowOrder.java:20)

---------------- bugs/CflowOrder.java

package bugs;

import java.io.PrintStream;
import java.lang.annotation.*;

import org.aspectj.lang.JoinPoint;

public class CflowOrder {

    public static void main(String[] args) {
        Log.print("Starting CflowOrder.main(..)");
        A.main(null);
        Log.print("Ending CflowOrder.main(..)");
    }

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

    static class A {
        @Annotation("A.foo")
        void foo() {
            new B().foo();
            Log.print("A.foo()");
        }

        public static void main(String[] args) {
            new A().foo();
            Log.print("A.main(..)");
        }
    }

    static class B {
        @Annotation("B.foo")
        void foo() {
            Log.print("B.foo()");
        }
    }

    static class Log implements IAspect {
        static final PrintStream out = System.err;

        static void print(String label) {
            out.println(label);
        }

        static void print(String label, JoinPoint tjp, JoinPoint.StaticPart sp,
                Object a) {
            out.println(label);
            out.println("\tJoin point: " + tjp);
            out.println("\tEnclosing join point: " + sp);
            out.println("\tAnnotation: " + a);
        }
    }
    static aspect Logger implements IAspect {

        //declare error: execution(* *(..)) && !within(Log) : "er";

//        before() : cflow(execution(void CflowOrder.main(String[]))) 
//            && !call(* IAspect+.*(..)) && ! within(IAspect+) {
//            Log.print("cflow(..main(..))", thisJoinPoint,
//                    thisEnclosingJoinPointStaticPart, null);
//        }
    }

    interface IAspect {}
    static aspect MyAspect  implements IAspect {

        pointcut annotated(Annotation a) :
            call(@Annotation * *(..)) && @annotation(a);

        pointcut belowAnnotated() :
            cflowbelow(annotated(Annotation));
      pointcut topAnnotated(Annotation a) : annotated(a) 
            && !belowAnnotated();

      pointcut notTopAnnotated(Annotation a, Annotation aTop) : annotated(a) 
      && cflowbelow(annotated(aTop));
//        pointcut topAnnotated(Annotation a) : annotated(a) 
//            && !cflowbelow(annotated(Annotation));
//
//        pointcut notTopAnnotated(Annotation a, Annotation aTop) : annotated(a) 
//            &&  cflowbelow(topAnnotated(aTop));

        // if this first, then no nonTopAnnotated advice
        before(Annotation a) : topAnnotated(a) {
            Log.print("topAnnotated", thisJoinPoint,
                    thisEnclosingJoinPointStaticPart, a);
        }
        // if topAnnotated is first, this does not run
        before(Annotation a, Annotation aTop) : notTopAnnotated(a, aTop) {
            Log.print("nonTopAnnotated", thisJoinPoint,
                    thisEnclosingJoinPointStaticPart, a);
        }
    }
}
Comment 1 Wes Isberg CLA 2006-04-10 21:42:47 EDT
Created attachment 38239 [details]
.class files produced by ajc 1.5.1
Comment 2 Andrew Clement CLA 2006-05-12 08:36:35 EDT
Yikes, that was hard to diagnose.  Offending code looks like:

46:  getstatic       #36; //Field CflowOrder$MyAspect.ajc$cflowCounter$0:Lorg/aspectj/runtime/internal/CFlowCounter;
49:  bipush  0
51:  invokevirtual   #42; //Method org/aspectj/runtime/internal/CFlowStack.get:(I)Ljava/lang/Object;

You can't call CflowStack.get() on a CflowCounter instance!

This is occurring because of the logic that shares cflow fields when working with the same pointcut.  Unfortunately in the situation in this program the pointcut behaves like a counter in one place and a stack in the other (ie. the consumer of the pointcut is using the context it exposes in one case but not the other).  Our cache that is indexed by "pointcutString:declaringAspect" to find a cflow field doesn't take into account the kind of context in which it is being used.  if we extend the cache key to include this information (by appending 'stack' or 'counter' to the key) then it works.

fix committed - there are possibly other bugzilla cases that are this same problem, but Wes' testcase was the best ;)