Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-dev] Re: after() and around() advice does not work on handler join poi nts.

I'm replying to this on the dev list since I think it's an interesting discussion.  I'll put some summary back into the bug database when the discussion is done.

Ramnivas wrote (to the bug database): 
> This bug (or compiler limitation) is seen in 1.1beta4. The same program
> works fine in 1.0.6.
> 
> Test program:
> public class Test {
>     public static void main(String[] args) {
> 	try {
> 	    foo();
> 	} catch (NullPointerException ex) {
> 
> 	}
>     }
> 
>     public static void foo() {
>     }
> }
> 
> aspect AfterHandlerAspect {
>     after() : handler(NullPointerException) {
> 	System.out.println("Thrown NullPointerException");
>     }
> }
> 
> F:\aspectj\bugs\1.1\b4\after-handler>ajc *.java
> Only before advice is supported on handler join points (compiler
> limitation)
> Only before advice is supported on handler join points (compiler
> limitation)
> 
> 2 warnings
> 
> Indetical error is issued if I use around() advice instead of after().
> 
> Why is this compiler limitation? This prevents implementing crosscutting
> that relies on checking the state of thrown exception by a handler block.

This limitation is a result of weaving into bytecodes instead of source code.  It is either hard or impossible to reliably find the end of an exception handler in bytecode.  We decided that it would be better to make this unimplemented in 1.1 rather than come up with an rushed definition of the end of a handler block that we would be forced to live with forever after.  Here are two quick examples of where this is easy and where it's hard/impossible.

Your Test class above produces the following bytecode (with javac):

Method void main(java.lang.String[])
   0 invokestatic #2 <Method Test.foo()V>
   3 goto 7
   6 astore_1
   7 return
Exception table:
   from   to  target type
     0     3     6   <Class java.lang.NullPointerException>

This is an example of a common pattern that would let us find the end of most exception handlers.  Just before the handler block there is a goto that goes to the instruction just past the end.  Checking for this pattern would let us handle 99% of actual exception handlers.  However, what should ajc do when this pattern isn't present or when it's wrong?

Consider this bytecode:

Method void bar()
   0 invokestatic #2 <Method Test.foo()V>
   3 return
   4 astore_0
   5 invokestatic #2 <Method Test.foo()V>
   8 return
Exception table:
   from   to  target type
     0     4     4   <Class java.lang.NullPointerException>

It could be produced from either of these two programs:
A:  public static void bar() {
      try {
	  foo();
	  return;
	} catch (NullPointerException ex) {
	  foo();
	}
    }
OR
B:  public static void bar() {
      try {
	  foo();
	  return;
	} catch (NullPointerException ex) {
	}
	foo();
    }

So, should we consider the foo() to be inside of the catch block or not?

You might be able to make the argument that in a case like this it's reasonable to treat the call to foo() as effectively part of the catch block since it can only be called if an exception is caught.  However, it's also easy to see how this might be very confusing to a programmer who wrote B.

I suspect that this issue could be resolved with some hard thought and analysis of all the possible edge cases; however, this feature was not deemed important enough to delay the 1.1 release until that analysis could be done.

-Jim


Back to the top