Community
Participate
Working Groups
I am also trying to decompose some of our code such, that we can easily supply two different versions of our code, one that supports "persistence", another one that does not provide that capability (but should thus also not be burdened with the involved code and memory overhead). So I tried to advise the original class: ------------------------------------ ... public class Correlator { ... private IEngine engine = null; // private IEngine engine; private IRuleParser parser = null; public Correlator(IEngine engine, IRuleParser parser) { this.engine = engine; this.parser = parser; } ... ------------------------------------ with this advice: ------------------------------------ ... // ------------------------------------------------------ // we "extend" Correlator so that it also implements the // PersistentCorrelator-interface: // ------------------------------------------------------ declare parents: Correlator implements PersistentCorrelator; /** we define an additional attribute to hold the persistence-store: */ private IPersistentStore Correlator.persistence = null; /** * a new constructor for Correlator that accepts and memorizes * the persistence store passed in: */ public Correlator.new(IEngine engine, IRuleParser parser, IPersistentStore persistentStore) { this(engine, parser); // calling the original constructor... this.persistence = persistentStore; } // we also declare it an error if one uses the old constructor: declare error: execution(Correlator.new(IEngine, IRuleParser)) && !adviceexecution() // except THIS aspect which must use it! : "use \"new Correlator(IEngine, IRuleParser, IPersistentStore) \" instead!"; ... <more code that implements methods defined by the PersistentCorrelator interface here> ... ------------------------------------ For some strange reason the declare error statement in the aspect yields me an error in the 1st private IEngine engine = null; assignment. If I remove the "= null" assignment (since it's superfluous - the VM will initialize all fields to null anyway) as indicated by the // private IEngine engine; line I get the error instead in the this.engine = engine; assignment within the original constructor. This can't be working as designed or is it? Why would any of these two assignment-statements trigger the declare error? They don't match the pointcut declared at all, or do they? Is there some hidden constructor-call in the code created by AspectJ somewhere??? The environment used was Eclipse 3.0RC1 using JSDK Sun 1.4.2-03. Michael
Hi Michael, I think several things are confusing the situation here. Let me tackle them one at a time. 1) When execution join points are 'reported' by the compiler - either by showing advice against them or reporting declare error matches - the compiler reports the first line of the method. For example: aspect A { public void foo() { System.err.println(); } declare warning: execution(* foo(..)): "foo executing"; } compiled, results in: C:\ajbugs>ajc A.java C:\ajbugs\A.java:3 warning foo executing System.err.println(); ^^^^^^^^^^^^^^^^^^^^^ method-execution(void A.foo()) see also: C:\ajbugs\A.java:6 1 warning C:\ajbugs> You can see the first line of the method has been reported. This is due to the fact that the first line of the method contains the first bytecode that we can 'get hold of' to report things against. ------ 2) initializer statements are inlined in constructors. If you write "private IEngine engine = null"; then that is implemented by inlining the code in each ctor. (making it the first line!) ------ 3) adviceexecution() matches the join point that represents execution of a piece of advice, it does not match join points within that piece of advice (also ITDs are not advice: only before(),after(),around() are advice). If you want to talk about all join points that occur whilst advice is executing, you typically use cflow(adviceexecution()) which means execution of the advice and all join points that occur whilst it is executing. ------ 4) Finally, of course, inter type declaring your new constructor isnt removing the original constructor, so Correlator.new(IEngine,IRuleParser) still exists in the program and execution() of it still exists. ------ All this means that in the statement: declare error: execution(Correlator.new(IEngine, IRuleParser)) && !adviceexecution() // except THIS aspect which must use it! : "use \"new Correlator(IEngine, IRuleParser, IPersistentStore) execution(Correlator.new(IEngine,IRuleParser)) matches and since adviceexecution() would match only the execution join point for any defined advice, !adviceexecution() matches everything else. Hence you get an error - and when it is reported, you see the first line of the constructor reported, which happens to be the initializer that has been inlined into it. The usual way to deal with this is: declare error: call(Correlator.new(IEngine, IRuleParser)) && !within(MyPersistenceAspect) : "use new Correlator(IEngine, IRuleParser, IPersistentStore)"; let me know what you think.
Hi Michael, I'm going to close this bug - Adrian and I are trying to make a dent in the 150 bugs and enhancements we have against AspectJ at the moment. If you aren't happy with my explanation of how it is working then feel free to reopen it :)
Fix released as part of AspectJ 1.2.1