Bug 266552 - @DeclareMixin feature
Summary: @DeclareMixin feature
Status: NEW
Alias: None
Product: AspectJ
Classification: Tools
Component: Compiler (show other bugs)
Version: DEVELOPMENT   Edit
Hardware: PC Windows NT
: P3 enhancement (vote)
Target Milestone: 1.6.4   Edit
Assignee: aspectj inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-02-27 15:07 EST by Andrew Clement CLA
Modified: 2009-03-09 20:12 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 Andrew Clement CLA 2009-02-27 15:07:16 EST
We currently have @DeclareParents, which is a poor imitation of code style 'declare parents' and really implements a mixin strategy rather than what 'declare parents' does.  I've read through the posts on this and the bugs related to it and taken on board a suggestion that the JBoss mixin syntax looks neat.  Based on all that I'm proposing this kind of design for @DeclareMixin - I may then deprecate @DeclareParents.

What is a mixin?  Well the definition I'm using here is that some new type is 'mixed in' to an existing type by means of a delegate instance of the new type being maintained and forwarding methods added to the target type that forward calls on the original object to the delegate.  This is basically what @DeclareParents does now but we'll tweak the syntax and make it more friendly.

General form - the [] show where users plug details in.  Basically @DeclareMixin is attached to a factory method in an aspect that can create instances of the delegate class.

@DeclareMixin(value="[typePattern]",interfaces={[interfaceList]})
public static [interfaceName | className] create[Something](Object object) {
  return new [ImplementationType](object);
}

By allowing the user to specify the factory method, we don't have to rely on a default no-arg constructor in the Implementation class and instances of the Implementation class can know which object they are the delegate for (if they want to).  With smart defaults, it can be as simple as follows:

The interface to mixin is deduced from the return type of the factory method.

@DeclareMixin("@Foo *")
public static SomeInterface createInstance(Object object) {
  return new SomeImplementation(object);
}

or if the user doesn't care about the instance for which they are delegating:

@DeclareMixin("@Foo *")
public static SomeInterface createInstance() {
  return new SomeImplementation();
}

Here the public methods (and probably fields) are mixed in from the SomeImplementation class:

@DeclareMixin("@Foo *")
public static SomeImplementation createInstance(Object object) {
  return new SomeImplementation(object);
}

If SomeImplementation implemented interfaces we could limit (by interface) the mixed in methods, so here only A and B are mixed in - no fields would be mixed.

class MyClass implements A,B,C,D {}

@DeclareMixin("@Foo *",interfaces={A.class,B.class})
public static MyClass createInstance(Object object) {
  return new MyClass(object);
}

In terms of annotation 'copying' - any annotations on the interface methods will be copied to the delegate methods added to the target type (just as @DeclareParents does today).

Let's finish with a translation from @DeclareParents to @DeclareMixin:

@DeclareParents(value="org.xyz..*",defaultImpl=MoodyImpl.class)
private Moody implementedInterface;

@DeclareMixin("org.xyz..*")
private Moody createMoodyInstance() {
  return new MoodyImpl();
}

The declaration will work in either an annotation style or code style aspect.

(Thanks to Ramnivas Laddad for already helping me get this far)

I definetly see a phase 1 and phase 2 since field mixins need a bit more thought.
Comment 1 Andrew Clement CLA 2009-03-03 20:24:48 EST
First testcases are in and are passing.  An example is attached below.  It already supports the optional inclusion of an argument to the factory method - and if there is an argument specified (either Object or the type of the mixin target - which would be CaseA below) then the instance for which a delegate is being created will be passed in.  Also the factory method can optionally be static or an instance method.  For an instance method X.aspectOf() would be called to get the right instance for which to invoke the factory method.

Although the basics are working, it is easy to make it fail as it is not policing much at the moment...

---
import org.aspectj.lang.annotation.*;

public class CaseA {
  public static void main(String[]argv) {
    CaseA ca = new CaseA();
    ((I)ca).methodOne(); // will only succeed if mixin applied
  }
}

aspect X {
  @DeclareMixin("CaseA")
  public static I createImplementation() {
    System.out.println("Delegate factory invoked");
    return new Implementation();
  }
}

interface I {
  void methodOne();
}

class Implementation implements I {
  public void methodOne() {
    System.out.println("methodOne running");
  }
}
Comment 2 Andrew Clement CLA 2009-03-04 17:07:41 EST
The code so far is in the AJDT 1.6.4 release candidate.

There appears to be a problem with incremental compilation.

The mixin maps to the creation under the covers of a declare parents, multiple method type mungers, and a method field munger.  This is the same as for @DeclareParents.

On the secondary compile it appears the class is still marked as implementing the interface but doesn't provide the implementation - so there is an error.  I imagine @DeclareParents fails in the same way.

Not currently sure of the fix.  It seems that we are in trouble when we have this mixture of constructs that modify the type system (declare parents) and constructs that just weave (delegate mungers). 

Hmm, maybe the appropriate fix here is a new kind of type munger that doesn't affect the type system.  It would be similar to declare parents but not applied until weaving.  I imagine @DeclareParents, if it is kept, would need changing to use this new munger too.
Comment 3 Andrew Clement CLA 2009-03-05 16:40:20 EST
Incremental analysis modified in 2 ways:

- created a subtype of DeclareParents called DeclareParentsMixin - this is created when @DeclareMixin is processed and only affects weaving, NOT compilation.
- marked the MethodDelegateTypeMunger and associated delegate field host munger as being 'harmless' for compilation.
Comment 4 Andrew Clement CLA 2009-03-05 17:54:59 EST
incremental support committed for @DeclareMixin - only basic testing though.

ought to move fixes into place for @DeclareParents too..
Comment 5 Andrew Clement CLA 2009-03-05 19:05:14 EST
basic documentation written at : http://www.eclipse.org/aspectj/doc/released/adk15notebook/ataspectj-itds.html

needs another review before marking complete though, the Engleesh is dreadful
Comment 6 Andrew Clement CLA 2009-03-09 20:12:23 EDT
it now polices that the instance target type for a mixin is compatible with the parameter type of the factory method (although it might put out an error once per method on the interface rather than just once for the declaration...).

New weaveinfo message for a mixin.