Thanks for bringing this up for discussion, Matthew. Some comments
and code
below; I do think the current behavior is correct, i.e., it's what the user
should
expect based on how the programming guide describes initialization and
handling
interface initializers.
First,
> One thing I am sure of: we should not return null [from
getConstructor()]
That seems like the correct behavior to me if there is no constructor for
an interface
(this assumes we have an interface-specific initializer, which I think we
do).
Second, wrt interface initialization, even though Java does not permit
interfaces to have
constructors, AspectJ permits interfaces to have fields that have
initialization statements,
so I think users should be able to expect those to be sequestered in
an interface initializer,
i.e., "initialization(Interface.new(..)) && !
initialization(Instance.new(..))" Indeed, in the
example of an instance with superclass and superinterface, there should be
three
initialization join points: the instance, the superclass, and the
superinterface. This is
what the code below shows. With care, each join point can be picked
out.
Third, wrt declaring type, the programming guide says
"At object initialization and pre-initialization join points, the
signature is the constructor
signature for the constructor that started this initialization: the
first constructor entered
during this type's initialization of this object."
On one hand, I think we'd want AspectJ's reflective access to follow this
model, so the
declaring type of the (reflective) signature indeed should reflect the
specific instance type,
"Test" in your program and "Initializer" in mine below. On the other
hand, if the pointcut
really is only picking out the interface or superclass initialization join
point, it seems
weird to say that it picks up the declaring type of the initiating
constructor. That would mean
the declaring type of the interface-initializer join point enclosed by the
subtype-initializer
join point would be the subtype, and it would thus get as many declaring
types as
there are implementing types, and it becomes not static data associated
with the
join point but dynamic data associated with the enclosing join point.
So perhaps
we should qualify the programming guide to say the declaring type for
purposes
of matching or some such language. In any case, the code below shows
what I think
is the correct declaring type, Interface for the interface-initialization
and Initializer for
the subtype.
Fourth, one might think that initialization of an instance starts
the instance-initialization join
point and then starts the instance-initialization join point of any parent,
since this is what
Java leads us to believe is the order of execution for constructors.
However, AspectJ defines
constructor-execution for the subtype to start when the code for
the constructor starts,
which is after any explicit or implicit call to this(..) or super(..) in
the constructor. That
means the supertype constructor-execution join point is not enclosed by the
subtype
constructor execution. Initialization follows this even though
initialization is intended to
pick out the initiating constructor. So even though the
initialization join point matches based
on the subtype-type, the first initialization join point run is not the
subtype-being-initialized but
the supertype. That's reflected in the code below and should probably
be explained to users.
(There is a faq entry on point, as well as a definition of the order of
initialization for interfaces.)
hth - Wes
------- Initializer.java import
org.aspectj.lang.Signature; import org.aspectj.lang.JoinPoint; import
org.aspectj.lang.reflect.ConstructorSignature;
interface Interface { void m();}
public class Initializer extends Parent implements Interface {
public static void main(String[]
args)
{
new Initializer(); }
}
class Parent { public void m(){}}
aspect Aspect { static int
counting()
{
System.err.println("COUNTING"); return ++COUNT;
} static int COUNT;
public int Interface.count =
counting();
before () :
initialization(Initializer.new())
{
print(thisJoinPoint, "
Interface");
} before () :
initialization(Interface.new())
{
print(thisJoinPoint, "
Interface");
} before () :
initialization(Interface.new())
&& !initialization(Parent.new())
{
print(thisJoinPoint,
"=Interface");
} before() :
initialization(Parent.new())
{
print(thisJoinPoint, "
Parent");
} before() :
initialization(Parent.new())
&& !initialization(Initializer.new())
{
print(thisJoinPoint, "
=Parent");
} before() :
initialization(Parent+.new())
{
print(thisJoinPoint, "
Parent+");
} void print(JoinPoint jp, String
label)
{
Signature signature =
jp.getSignature();
int line =
jp.getStaticPart().getSourceLocation().getLine();
System.err.println(label
+ " [" +
line
+ "] tjp=" +
jp.hashCode()
+ "] this=" +
jp.getThis()
+ ", declaringType=" +
signature.getDeclaringTypeName()
+ ", " +
((ConstructorSignature)signature).getConstructor());
}
}
------------Original Message------------
From: Matthew Webster <matthew_webster@xxxxxxxxxx>
To: aspectj-dev@xxxxxxxxxxx
Date: Tue, Sep-19-2006 6:14 AM
Subject: [aspectj-dev] AspectJ Runtime: Signatures for interface
initialization and type staticinitialization This discussion relates to
https://bugs.eclipse.org/bugs/show_bug.cgi?id=157054. I have also found
https://bugs.eclipse.org/bugs/show_bug.cgi?id=49295 and
https://bugs.eclipse.org/bugs/show_bug.cgi?id=60936 which concern the removal
from the language of the interface constructor execution pcd. Unfortunately
the documentation for this part of the API is terse and the tests almost
non-existent so it is very difficult to determine the intended behaviour. One
thing I am sure of: we should not return null.
There seems to be a discrepancy between classes and
interfaces when it comes to the initialization join point. In the example
below we advise the initialization of a single class both as an extender of
Parent and an implementer of Interface:
public class Test extends Parent implements
Interface {
public static void main(String[] args)
{
new Test();
}
}
public interface Interface
{ }
public class Parent {
}
public
aspect Aspect {
before () : (initialization(Interface.new()) || initialization(Parent+.new()))
&& !within(Parent) {
Signature signature = thisJoinPoint.getSignature();
System.err.println("Aspect.before() this=" +
thisJoinPoint.getThis()
+ ",
signature=" + signature.getClass()
+ ",
declaringType=" + thisJoinPoint.getSignature().getDeclaringTypeName()
+ ",
" +
((ConstructorSignature)signature).getConstructor()); }
}
However the output in each case is different:
Aspect.before() this=Test@1833955,
signature=class org.aspectj.runtime.reflect.ConstructorSignatureImpl,
declaringType=Test, public Test() Aspect.before() this=Test@1833955, signature=class
org.aspectj.runtime.reflect.ConstructorSignatureImpl, declaringType=Interface,
null
Firstly while the declaring
type for a class initializer is the target class in the case of the interface
it is not. Secondly an interface cannot have a constructor. There are two
possible solutions: 1. Make the
implementing type the declaring type and remove the discrepancy between class
and interface. 2. Return an
InitializerSignature instead of a ConstructorSignature. Unfortunately this
interface has getInitializer() which also returns a
java.lang.reflect.Constructor. What is slightly bizarre is that this interface
is also used for staticinitializer join points and in this case the interface
returns the signature for the default constructor of the target class!
Matthew Webster AOSD
Project Java Technology Centre, MP146 IBM Hursley Park, Winchester,
SO21 2JN, England Telephone: +44 196 2816139 (external) 246139
(internal) Email: Matthew Webster/UK/IBM @ IBMGB,
matthew_webster@xxxxxxxxxx http://w3.hursley.ibm.com/~websterm/ _______________________________________________
aspectj-dev mailing list aspectj-dev@xxxxxxxxxxx https://dev.eclipse.org/mailman/listinfo/aspectj-dev
|