Bug 141735 - Inner Aspects: Compiler complains about exceptions when the join point does not throw an exception
Summary: Inner Aspects: Compiler complains about exceptions when the join point does n...
Status: RESOLVED FIXED
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: 1.2.1   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-05-15 06:30 EDT by Uli Eibauer CLA
Modified: 2006-05-16 03:56 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Uli Eibauer CLA 2006-05-15 06:30:07 EDT
//initial situation:

class Table {

      void foo() throws CheckedException {
	   ..
	   bar(session, mode, data1, data2);
	   check(session,data2);
	   ..
      }

      void bar(Session session, int mode, Object[] data1, Object[] data2) {..}

      void check(Session session, Object[] data2) throws CheckedException {..}

}


//situation after aspect oriented refactoring

class Table {
      
      void foo() throws CheckedException {
	   ..
	   bar(param1, param2);
	   ..
      }

      void bar(Session session, int mode, Object[] data1, Object[] data2) {..}

      void check(Session session, Object[] data2) throws CheckedException {..}

      private static aspect AccessControlAspect {

	      after(Session session, int mode, Object [] data, Table table)
                returning
    		: call(* *..Table.bar(Session, int, Object[], Object[])) 	
    	        && withincode(* *..Table.foo(..))
    		&& args(session, mode, *, data) 
    		&& target (table)
    		{
    			table.check(session, data);	 			
    		}
}

// ==> Realizing this situation as shown above results in a compiler error,
// 'unhandled exception CheckedException'
// when calling table.check(..) within the advice

// ==> Adding 'throws CheckedException' to the advice declaration results in a 
// compiler error,
// 'can't throw checked Exception CheckedException at this join point' 

//==> Realizing the aspect as outer aspect works fine
Comment 1 Adrian Colyer CLA 2006-05-15 07:41:46 EDT
// ==> Realizing this situation as shown above results in a compiler error,
// 'unhandled exception CheckedException'
// when calling table.check(..) within the advice

// ==> Adding 'throws CheckedException' to the advice declaration results in a 
// compiler error,
// 'can't throw checked Exception CheckedException at this join point' 

both of these messages are correct.

As described in the semantics guide, advice can only throw checked exceptions that are declared in its throws clause, and only at join points that are declared to throw the given checked exception.

Since "check" can throw a checked exception, you must either handle that exception  inside the advice body, or declare the checked exception in the advice signature. If a checked exception can be thrown by advice (declared in the signature), then you can only advise join points at which the checked execption is declared (otherwise you would be breaking the contract expected by e.g. the caller of bar in this case, since that caller is not expecting a call to bar to raise a CheckedException). So what matters is that the call to bar doesn't raise an exception bar is not declared to throw - not whether or not this call happens within a method that has the corresponding exception in its own throws clause.

If there is a bug here at all, it is that you didn't get a similar message when declaring the aspect as a top-level type. Was your aspect *identical* to the one shown when you did that?

The following simplified version of your scenario does indeed produce the 
expected error messages with a top-level aspect:

class CheckedException extends Exception {}

class Table {

    public void foo()  throws CheckedException {
        bar();
    }

    public void bar() {}

    public void check() throws CheckedException {}

}

aspect A {

    after(Table t) returning throws CheckedException :
        call(* bar()) && target(t) &&
        withincode(* Table.foo(..))
    {
        t.check();         
    }

}

Compiling with ajc gives:

/Users/adrian/projects/aj-play/Table.java:6 [error] can't throw checked exception 'CheckedException' at this join point 'method-call(void Table.bar())'
bar();
^^^^^
/Users/adrian/projects/aj-play/Table.java:17 [error] can't throw checked exception 'CheckedException' at this join point 'method-call(void Table.bar())'
after(Table t) returning throws CheckedException : 
        call(* bar()) && target(t) &&
        withincode(* Table.foo(..)) 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

2 errors  
Comment 2 Uli Eibauer CLA 2006-05-16 03:04:11 EDT
Thanks for your quick answer!

I re-checked the solution as outer advice and the result is also
as you described. I understand the programming by contract issue
and you are definitely right not to permit throwing checked 
exceptions in such a case.

I am currently doing a refactoring project on an existing software
tool. So I had the "refactoring" point of view and from this point of 
view it definitely means a restriction that you cannot 
translate this situation one by one. Throwing a soft exception
and unwrapping it is really not very comfortable.
Exception handling within the advice is not what the original
program flow meant to be.
Comment 3 Adrian Colyer CLA 2006-05-16 03:56:50 EDT
I agree with you here. When I was working through your scenario I could see exactly why you'd want to be able to throw the checked exception from the advice. It certainly complicates the refactoring flow that it can't be done without some additional mechanism to soften or otherwise handle the exception...