Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] execution join points and overridden methods

Hi Doug -

I agree that the semantics section is incomplete on this point.
How about something like this:

   {When supplied with a method signature, the execution pointcut will 
   pick out join points whose signatures match [as described elsewhere...].}
   
   If the pointcut signature specifies a declaring type, the pointcut will
   only match methods declared in that type, or methods that override 
   methods declared in or inherited by that type.  

   {It will not match methods with 
   the same signature in a super class of that type.  This enables the
   pointcut to match overriding methods as it does implementations, for
   consistency in behavior when subclassing.  To instead pick out only
   methods on a specific type, use the lexical PCD's "within" or
   "withincode()".}

The text is {braces} is transitional or elaboration and subject to edits,
but I believe the middle paragraph is correct (both as a description of what 
the semantics should be and what the implementation does).  I'll put it in 
the semantics guide unless there are objections. 

Otherwise, replies inline...

> ------------Original Message------------
> From: Doug Orleans <dougo@xxxxxxxxx>
> To: aspectj-users@xxxxxxxxxxx
> Date: Tue, Aug-17-2004 4:06 PM
> Subject: Re: [aspectj-users] execution join points and overridden methods
>
> Wes Isberg writes:
>  > The programming guide, semantics appendix is typically laconic:
>  > 
>  >   At a method execution join point, the signature [is] a method 
>  >   signature whose qualifying type is the declaring type of 
>  >   the method.
> 
> I saw this sentence, but I decided it was irrelevant, or at least
> didn't tell the whole story: it specifies the signature of a
> method execution join point, but it doesn't specify how a method
> execution pointcut designator signature pattern matches it.

Good point.  It only says what's being matched.

>  > So in our example, the declaring type is A1.
> 
> I think this is wrong: thisJoinPoint.getSignature().getDeclaringType()
> returns A2.  Even if it were A1, it wouldn't explain why both
> pointcuts (with different qualifying types) contain the same join
> point.

Again, good point.  The reflective access to the join point provides
the type of the join point.  If, or since, the join point can be 
picked out by many pointcuts, some of which specify the supertype, 
the type specified in the pointcut might not be identical to the join
point type (though the join point type will be a subtype of the type 
specified in the pointcut).  [See also P.S. below.]

>  > (I should have said "declared in A1" rather than "defined in A1";
>  > though both are true, declare is what matters.)  Same result if A1
>  > were an interface.
> 
> As far as I know, the whole declaration/definition distinction is just
> left over from C and doesn't exist in the Java language spec.  A
> class declaration contains member declarations; a class type contains
> declared and inherited members.

Good point. You convinced me to abandon this analogy in favor of 
"overriding" and "inheriting", which the JLS does use and which 
together cover Liskov substitution for methods.  The slightly tricky
part is realizing that it's not just overriding.  E.g., 

  declare warning: execution(* Super.*(..))

will emit a warning for any method declared in (any subtype of) Super,
if the method is declared in, or inherited by, Super.  For example,

   class SuperSuper { void m() {} }
   class Super {}        // inherits SuperSuper.m()
   class Sub { 
     void m() {}         // overrides SuperSuper.m() <-- DW
     void p() {}
   }
   aspect A {
     declare warning: execution(* Super.*(..)): "Hello, World!";
   }

The implementation Sub.m() (at DW) is flagged by the warning.  
We normally would not say that Sub.p() "overrides" any method 
in Super; it overrides SuperSuper.m().  (Of course, p() is not
picked out because it is not declared or inherited in Super.)
So "overrides" is correct for subtypes, but the overridden 
method may be inherited by the specified type.

Thanks for bringing this up and pushing it to a better conclusion.

Wes

P.S. - Is this wise?  No one has objected to the rationale of Liskov 
substitution; but is that what programmers expect or want here?
And what should happen when the programmer specifies a subtype of the
declaring type of the method?  Now, we don't pick out the supertype(s) 
implementation(s), which would seem to violate the rationale.  It
seems to be the programmers' fault, but certainly could come about
via a refactoring that hoisted a method to a supertype.  Personally,
I've come to rely on these semantics, but I wonder if others do.

P.P.S. - For completeness' sake, since in Java a method can be declared
in two unrelated interfaces but extended in one interface and/or
implemented in one class, it means that either of two unrelated types
can be used to specify that particular implementation of the method.
So for the types:

   interface I { void m(); }
   interface J { void m(); }
   interface K extends I, J { }
   class D implements K { public void m() {} } // match

The declaration D.m() would be flagged by any of

   declare warning: execution(* I.*(..)): "Hello";
   declare warning: execution(* J.*(..)): "Hello";
   declare warning: execution(* K.*(..)): "Hello";
   declare warning: execution(* D.*(..)): "Hello";

Again thisJoinPoint.getSignature().getDeclaringType() would return
D, but the type specified could be any of D, K, J, or I.




Back to the top