Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
RE: [aspectj-dev] FW: [aspectj-users] Using AOP to implement doub le dispatching technique

I want to explain ajc-1.1's implementation of introduction so that the reasons behind the two "unneeded" static methods will be more clear.  The short answer is that these methods improve the modularity and separate-compilation of the ajc compiler and its generated code.

When implementing things this way, I believed that the performance over-head in any real application running on a reasonable JIT would be so small as to not be noticeable -- static methods should be very easy for a JIT to inline.  I hope that Adrian can get some help on understanding the details of this from some real JVM performance experts to confirm or deny this assumption -- or possibly suggest some tweak to the implementation to make the JIT's happier.  I also think that Wes is on the right track by pointing out the advantages of the simpler solutions possible in AspectJ.

<details>
Here's a sketch of how ajc-1.0 and ajc-1.1 implement an introduced method (actual mangled names are longer with much better hygiene guarantees):

aspect A {
  void C.m() { <body of C.m> }
}

class Main {
  public static void main(String[] args) {
    new C().m();
  }
}

==(ajc-1.0)==>

class A { }

class C {
  void m$from$A() { <body of C.m properly moved to class C> }
}

class Main {
  public static void main(String[] args) {
    new C().m$from$A();
  }
}
==(ajc-1.1)==>

class A {
  static void C$m(C self) { self.m$from$A(); }

  static void C$m$body(C self) { <body of C.m> }
}

class C {
   void m$from$A() { A.C$m$body(this); }
}

class Main {
  public static void main(String[] args) {
    A.C$m(new C());
  }
}
------------------------------------------

The two extra static methods were added for different reasons.

A.C$m(C self): This method was added to improve the error messages in the case of separate compilation or weaving where the build process is broken.  If A and Main are compiled without the ability to weave into C, then ajc will display an Xlint warning message and continue to generate the partially correct code.  This makes it possible to postpone the weaving into C possibly until class load time.  With this method, if the programmer forgets to weave into C before running the program, she will see some sort of JVM error coming from A.C$m that will give some context to the problem.  Without this method, the error will come from Main.main and will typically not include enough information for the issue to even make sense.  I can easily see removing this method with some very minor performance evidence coupled with some work understanding the separate compilation use-cases.

A.C$m$body(C self): This method keeps the body of the introduced method inside of A rather than moving it to C.  This keeps the body of the method in the same Java lexical scope that the compiler analyzes it in.  This means that stack-traces and debuggers can both understand the correct files and line numbers inside this code without any AspectJ awareness.  It also removes the need to expose A's private members and package-visible members from A's package to the world so they can be accessed from C.  Finally, it improves the incremental compilation properties by allowing changes to the body of the method to only force a recompile of A and not C.  I think you'd need to find some extremely strong performance problems to argue for moving the body of this method back into C.

-Jim


Back to the top