From cce24d74634779801e999fb09f9031687eaa3d31 Mon Sep 17 00:00:00 2001
From: acolyer This is the initial release of AspectJ 1.1. It includes a small
+number of new language features as well as major improvements to the
+functionality of the tools.
+This document describes the differences between
+AspectJ versions 1.1 and 1.0.6.
+Users new to AspectJ need only read
+the AspectJ Programming Guide
+since it describes the 1.1 language.
+Users familiar with AspectJ 1.0 may find this document
+a quicker way to learn what changed in the language
+and tools, and should use it as a guide for porting
+programs from 1.0 to 1.1.
+ This document first summarizes changes from the 1.0 release in
+ then details some of the language
+ and compiler changes,
+ and finally points readers to the bug database for any
+ known limitations.
+ AspectJ 1.1 is a slightly different language than AspectJ 1.0.
+ In all but a few cases, programs written in AspectJ 1.0 should
+ compile correctly in AspectJ 1.1. In many cases, there are
+ new or preferred forms in AspectJ 1.1. However, some AspectJ 1.0
+ features have changed in 1.1, so some 1.0 programs
+ will not compile or will run differently in 1.1.
+ The corresponding features are marked below as compile-time
+ or run-time incompatible (CTI or RTI, respectively).
+ When the language change involves a move in the static shadow effective
+ at run-time but also apparent at compile-time (e.g., in declare
+ error or warning statements), it is marked CRTI.
+ Programs using run-time incompatible forms should be verified that
+ they are behaving as expected in 1.1.
+
+ Most changes to the language are additions to expressibility
+ requested by our users:
+ Some are have different behavior in edge cases but offer
+ improved power and clarity: But in order to support weaving into bytecode effectively,
+ several incompatible changes had to be made to the language: There are a couple of language
+ limitations for things that are rarely used that make the
+ implementation simpler, so we have restricted the language accordingly.
+ We did not implement the long-awaited new
+ pertype aspect specifier in this release, but it may well
+ be in a future release. The compiler for AspectJ 1.1 is different than the compiler for
+ AspectJ 1.0. While this document describes the differences in the
+ compiler, it's worthwhile noting that much effort has been made to
+ make sure that the interface to ajc 1.1 is, as much as possible, the
+ same as the interface to ajc 1.0. There are two important changes
+ under the hood, however. First, the 1.1 compiler is implemented on top of the
+ open-source Eclipse compiler. This has two benefits: It allows us
+ to concentrate on the AspectJ extensions to Java and let the Eclipse
+ team worry about making sure the Java edge cases work, and it allows
+ us to piggyback on Eclipse's already mature incremental compilation
+ facilities. Second, ajc now cleanly delineates compilation of source code
+ from assembly (or "weaving") of bytecode. The compiler still
+ accepts source code, but internally it transforms it into bytecode
+ format before weaving. This new architecture, and other changes to the compiler, allows
+ us to implement some features that were defined in the AspectJ 1.0
+ language but not implementable in the 1.1 compiler. It also makes
+ some new features available: Some other features we wanted to support for 1.1, but did not make
+ it into this release: But some features of the 1.0 compiler are not supported in the
+ 1.1 compiler: A short description of the options ajc accepts is available with
+ " Some changes to the implementation are almost entirely
+ internal:
+ Also, it is worth noting that because AspectJ now works on bytecode,
+ it is somewhat sensitive to how different compilers generate
+ bytecode, especially when compiling with and without the -1.4 flag. This release includes an Ant task for old-style 1.0 build
+ scripts, a new task for all the new compiler options, and a
+ CompilerAdapter to support running This release does not include AspectJ 1.1 will not include ajdb, the AspectJ
+ stand-alone debugger. It is no longer necessary for two reasons.
+ First, the -XnoInline flag will tell the compiler to generate
+ code without inlining that should work correctly with any Java
+ debugger. For code generated with inlining enabled, more
+ third-party debuggers are starting to work according to JSR 45,
+ "Debugging support for other languages," which is supported by
+ AspectJ 1.0. We aim to support JSR-45 in AspectJ 1.1, but
+ support will not be in the initial release. Consider using
+ the -XnoInline flag until support is available. This release has minor additions to the runtime library classes.
+ As with any release, you should compile and run with the runtime
+ library that came with your compiler, and you may run with
+ a later version of the library without recompiling your code. In one instance, however, runtime classes behave differently this release.
+ Because the AspectJ 1.1 compiler does its weaving through
+ bytecode, column numbers of source locations are not available.
+ Therefore, The AspectJ Browser supports incremental compilation and running
+ programs. AJDE for JBuilder, AJDE for NetBeans, and AJDE for Emacs
+are now independent SourceForge projects (to keep their licenses).
+ They use the batch-build mode of the new compiler.
+ The AspectJ tools sources are available under the
+ Common Public
+ License in the CVS repository
+ at http://eclipse.org/aspectj.
+ For more information, see the FAQ entry on
+ building sources.
+ AspectJ 1.0 had many distributions - for the tools,
+ the documentation, each IDE support package,
+ their respective sources, and the Ant tasks -
+ because they came under different licenses.
+ All of AspectJ 1.1 is licensed under the CPL 1.0,
+ so the tools, Ant tasks, and documentation are all
+ in one distribution available from
+
+ http://eclipse.org/aspectj.
+To retain their MPL 1.1 license,
+Ajde for
+Emacs,
+NetBeans and
+JBuilder
+are now independent SourceForge projects. AspectJ 1.2 Readme
+
+ content to be replaced
+
+
+
+
+
+
+The Language
+
+
+
+
+
+
+
+ declare
+ precedence
, that replaces the "dominates"
+ clause on aspects. (CTI)
+
+
+
+
+
+ void
return type. This will require
+ porting of code that uses the set
PCD in conjunction
+ with after-returning or around advice. (CTI) ..
wildcard in args PCD's (rarely encountered in the
+ wild) because we didn't have the time. This might be available
+ in later releases if there is significant outcry. (CTI)
+The Compiler
+
+
+
+
+
+
+
+
+
+
+ ajc -help
".
+ Longer descriptions are available in the
+ Development Environment Guide
+ section on ajc.
+
+
+
+Support Tools
+
+ ajc
with the Javac
+ task by setting the build.compiler
property.
+ The new task can automatically copy input resources to output
+ and work in incremental mode using a "tag" file.
+ ajdoc
, the
+ documentation tool for AspectJ sources.
+ Ajdoc is deeply dependent on the
+ abstract syntax tree classes from the old compiler, so it needs a
+ bottom-up rewrite. We think it best to use this opportunity to
+ implement more general API's for publishing and rendering static
+ structure. Because those API's are last to settle in the new
+ architecture, and because the compiler itself is a higher priority,
+ we are delaying work on ajdoc until after the 1.1 release.
+The Runtime Library
+
+ thisJoinPoint.getSourceLocation().getColumn()
+ is deprecated and will always return 0.
+The AJDE Tools
+
+
+The Sources and the Licenses
+
+
+The AspectJ distribution
+
+
In AspectJ 1.0.6, we made an effort to hide some complications + with Aspect instantiation from the user. In particular, the + following code compiled and ran: +
+ ++ public class Client + { + public static void main(String[] args) { + Client c = new Client(); + } + } + + aspect Watchcall { + pointcut myConstructor(): execution(new(..)); + + before(): myConstructor() { + System.err.println("Entering Constructor"); + } + } ++ +
But there's a conceptual problem with this code: The before + advice should run before the execution of all constructors in the + system. It must run in the context of an instance of the + Watchcall aspect. The only way to get such an instance is to have + Watchcall's default constructor execute. But before that + executes, we need to run the before advice...
+ +AspectJ 1.0.6 hid this circularity through the ad-hoc + mechanism of preventing an aspect's advice from matching join + points that were within the aspect's definition, and occurred + before the aspect was initialized. But even in AspectJ 1.0.6, + this circularity could be exposed: +
+ ++ public class Client + { + public static int foo() { return 3; } + public static void main(String[] args) { + Client c = new Client(); + } + } + + aspect Watchcall { + int i = Client.foo(); + pointcut myConstructor(): + execution(new(..)) || execution(int foo()); + + before(): myConstructor() { + System.err.println("Entering Constructor"); + } + } ++ +
This program would throw a NullPointerException when run, since + Client.foo() was called before the Watchcall instance could be + instantiated.
+ +In AspectJ 1.1, we have decided that half-hiding the problem + just leads to trouble, and so we are no longer silently hiding + some join points before aspect initialization. However, we have + provided a better exception than a NullPointerException for this + case. In AspectJ 1.1, both of the above programs will throw + org.aspectj.lang.NoAspectBoundException. +
+ +Type patterns may now be used to pick out methods and + constructors based on their throws clauses. This allows the + following two kinds of extremely wildcarded pointcuts:
+ +pointcut throwsMathlike(): + // each call to a method with a throws clause containing at least + // one exception with "Math" in its name. + call(* *(..) throws *..*Math*); + + pointcut doesNotThrowMathlike(): + // each call to a method with a throws clause containing no + // exceptions with "Math" in its name. + call(* *(..) throws !*..*Math*); ++ +
The longwinded rules are that a method or constructor pattern + can have a "throws clause pattern". Throws clause patterns look + like:
+ +ThrowsClausePattern: + ThrowsClausePatternItem ("," ThrowsClausePatternItem)* + + ThrowsClausePatternItem: + ["!"] TypeNamePattern ++ +
A ThrowsClausePattern matches the ThrowsClause of any code + member signature. To match, each ThrowsClausePatternItem must + match the throws clause of the member in question. If any item + doesn't match, then the whole pattern doesn't match. This rule is + unchanged from AspectJ 1.0.
+ +If a ThrowsClausePatternItem begins with "!", then it matches + a particular throws clause if and only if none of the + types named in the throws clause is matched by the + TypeNamePattern.
+ +If a ThrowsClausePatternItem does not begin with "!", then it + matches a throws clause if and only if any of the types + named in the throws clause is matched by the TypeNamePattern.
+ +These rules are completely backwards compatible with + AspectJ 1.0. The rule for "!" matching has one potentially + surprising property, in that the two PCD's shown below will have + different matching rules.
+ +[1] call(* *(..) throws !IOException) + [2] call(* *(..) throws (!IOException)) + + void m() throws RuntimeException, IOException {} ++ +
[1] will NOT match the method m(), because method m's throws + clause declares that it throws IOException. [2] WILL match the + method m(), because method m's throws clause declares the it + throws some exception which does not match IOException, + i.e. RuntimeException.
+ +AspectJ 1.0 does not provide kinded pointcut designators for + two (rarely used) join points: preinitialization (the code that + runs before a super constructor call is made) and advice + execution. AspectJ 1.1 does not change the meaning of the join + points, but provides two new pointcut designators to pick out + these join points, thus making join points and pointcut + designators more parallel.
+ + adviceexectuion()
will pick out advice execution
+ join points. You will usually want to use adviceexecution()
+ && within(Aspect)
to restrict it to only those pieces of
+ advice defined in a particular aspect.
+ preinitialization(ConstructorPattern)
will
+ pick out pre-initialization join points where the initialization
+ process is entered through
+ ConstructorPattern
.
We strongly considered adding a pertype aspect kind to 1.1. + This is somewhat motivated by the new + restrictions on inter-type + declarations. This is also motivated by many previous request + to support a common logging idiom. Here's what pertype would look + like:
+ +/** One instance of this aspect will be created for each class, + * interface or aspect in the com.bigboxco packages. + */ + aspect Logger pertype(com.bigboxco..*) { + /* This field holds a logger for the class. */ + Log log; + + /* This advice will run for every public execution defined by + * a type for which a Logger aspect has been created, i.e. + * any type in com.bigboxco..* + */ + before(): execution(public * *(..)) { + log.enterMethod(thisJoinPoint.getSignature().getName()); + } + + /* We can use a special constructor to initialize the log field */ + public Logger(Class myType) { + this.log = new Log(myType); + } + } + + /** External code could use aspectOf to get at the log, i.e. */ + Log l = Logger.aspectOf(com.bigboxco.Foo.class).log; ++ +
The one open question that we see is how this should interact + with inner types. If a pertype aspect is created for an outer + type should advice in that aspect run for join points in inner + types? That is the behavior of the most common uses of this + idiom.
+ +In any case, this feature will not be in AspectJ 1.1. +
+ +Intertype declarations (once called "introductions") in + AspectJ 1.1 can only have one target type. So the following code + intended to declare that there is a void doStuff() method on all + subtypes of Target is not legal AspectJ 1.1 code. +
+ +aspect A { + public void Target+.doStuff() { ... } + } ++ +
The functionality of "multi-intertype declarations" can be + recovered by using a helper interface. +
+ +aspect A { + private interface MyTarget {} + declare parents: Target+ implements MyTarget; + public void MyTarget.doStuff() { ... } + } ++ +
We believe this is better style in AspectJ 1.0 as well, as it + makes clear the static type of "this" inside the method body. +
+ +The one piece of functionality that can not be easily + recovered is the ability to add static fields to many classes. We + believe that the pertype proposal provides + this functionality in a much more usable form.
+ +AspectJ 1.1 does not consider initializer execution a + principled join point. The collection of initializer code (the + code that sets fields with initializers and the code in non-static + initializer blocks) is something that makes sense only in Java + source code, not in Java bytecode.
+ +The end of an exception handler is underdetermined in bytecode, + so ajc will not implement after or around advice on handler join + points, instead signaling a compile-time error.
+ +The code generated by the initializers in Java source code now + runs inside of constructor execution join points. This changes + how before advice runs on constructor execution join points. + Consider:
+ +class C { + C() { } + String id = "identifier"; // this assignment + // has to happen sometime + } + aspect A { + before(C c) this(c) && execution(C.new()) { + System.out.println(c.id.length()); + } + } ++ +
In AspectJ 1.0, this will print "10", since id is assigned its + initial value prior to the before advice's execution. However, in + AspectJ 1.1, this will throw a NullPointerExcception, since "id" + does not have a value prior to the before advice's execution. +
+ +Note that the various flavors of after returning advice are + unchanged in this respect in AspectJ 1.1. Also note that this + only matters for the execution of constructors that call a + super-constructor. Execution of constructors that call a + this-constructor are the same in AspectJ 1.1 as in AspectJ 1.0. +
+ +We believe this difference should be minimal to real programs, + since programmers using before advice on constructor execution + must always assume incomplete object initialization, since the + constructor has not yet run.
+ +The initializer, if any, of an inter-type field definition runs + before the class-local initializers of its target class.
+ +In AspectJ 1.0.6, such an initializer would run after the + initializers of a class but before the execution of any of its + constructor bodies. As already discussed in the sections about + initializer execution join + points and constructor + execution, the point in code between the initializers of a class + and its constructor body is not principled in bytecode. So we had a + choice of running the initializer of an inter-type field definition at + the beginning of initialization (i.e., before initializers from + the target class) or at the end (i.e., just before its called + constructor exits). We chose the former, having this pattern in mind: +
+ ++ int C.methodCount = 0; + before(C c): this(c) && execution(* *(..)) { c.methodCount++; } ++ +
We felt there would be too much surprise if a constructor called a + method (thus incrementing the method count) and then the field was + reset to zero after the constructor was done. +
+ +Because of the guarantees made (and not made) by the Java + classfile format, there are cases where AspectJ 1.1 cannot + guarantee that the within pointcut designator will pick out all + code that was originally within the source code of a certain + type. +
+ +The non-guarantee applies to code inside of anonymous and + local types inside member types. While the within pointcut + designator behaves exactly as it did in AspectJ 1.0 when given a + package-level type (like C, below), if given a member-type (like + C.InsideC, below), it is not guaranteed to capture code in + contained local and anonymous types. For example:
+ +class C { + Thread t; + class InsideC { + void setupOuterThread() { + t = new Thread( + new Runnable() { + public void run() { + // join points with code here + // might not be captured by + // within(C.InsideC), but are + // captured by within(C) + System.out.println("hi"); + } + }); + } + } + } ++ +
We believe the non-guarantee is small, and we haven't verified + that it is a problem in practice.
+ +The withincode pointcut has similar issues to those described + above for within. +
+ +The pointcut designators this, target and args specify a + dynamic test on their argument. These tests can not be performed + on type patterns with wildcards in them. The following code that + compiled under 1.0 will be an error in AspectJ-1.1:
+ +pointcut oneOfMine(): this(com.bigboxco..*); ++ +
The only way to implement this kind of matching in a modular + way would be to use the reflection API at runtime on the Class of + the object. This would have a very high performance cost and + possible security issues. There are two good work-arounds. If + you control the source or bytecode to the type you want to match + then you can use declare parents, i.e.:
+ +private interface OneOfMine {} + declare parents: com.bigboxco..* implements OneOfMine; + pointcut oneOfMine(): this(OneOfMine); ++ +
If you want the more dynamic matching and are willing to pay + for the performance, then you should use the Java reflection API + combined with if. That would look something like:
+ +pointcut oneOfMine(): this(Object) && + if(classMatches("com.bigboxco..*", + thisJoinPoint.getTarget().getClass())); + + static boolean classMatches(String pattern, Class _class) { + if (patternMatches(pattern, _class.getName())) return true; + ... + } ++ +
Note: wildcard type matching still works in all other PCD's that + match based on static types. So, you can use + 'within(com.bigboxco..*+)' to match any code lexically within one + of your classes or a subtype thereof. This is often a good + choice.
+ + + +The Java .class file format contains information about the + source file and line numbers of its contents; however, it has no + information about source columns. As a result, we can not + effectively support the access of column information in the + reflection API. So, any calls to + thisJoinPoint.getSourceLocation().getColumn() will be marked as + deprecated by the compiler, and will always return 0.
+ +AspectJ 1.1 has a new declare form: +
+ +declare precedence ":" TypePatternList ";" ++ +
This is used to declare advice ordering constraints on join + points. For example, the constraints that (1) aspects that have + Security as part of their name should dominate all other aspects, and + (2) the Logging aspect (and any aspect that extends it) should + dominate all non-security aspects, can be expressed by:
+ +declare precedence: *..*Security*, Logging+, *; ++ +
In the TypePatternList, the wildcard * means "any type not matched + by another type in the declare precedence".
+ +It is an error for any aspect to be matched by more than one + TypePattern in a single declare precedence, so:
+ +declare precedence: A, B, A ; // error ++ +
However, multiple declare precedence forms may legally have this + kind of circularity. For example, each of these declare precedence is + perfectly legal: +
+ +declare precedence: B, A; + declare precedence: A, B; ++ +
And a system in which both constraints are active may also be + legal, so long as advice from A and B don't share a join point. So + this is an idiom that can be used to enforce that A and B are strongly + independent.
+ +Consider the following library aspects: +
+ +abstract aspect Logging { + abstract pointcut logged(); + + before(): logged() { + System.err.println("thisJoinPoint: " + thisJoinPoint); + } + } + + aspect MyProfiling { + abstract pointcut profiled(); + + Object around(): profiled() { + long beforeTime = System.currentTimeMillis(); + try { + return proceed(); + } finally { + long afterTime = System.currentTimeMillis(); + addToProfile(thisJoinPointStaticPart, + afterTime - beforeTime); + } + } + abstract void addToProfile( + org.aspectj.JoinPoint.StaticPart jp, + long elapsed); + } ++ +
In order to use either aspect, they must be extended with + concrete aspects, say, MyLogging and MyProfiling. In AspectJ + 1.0, it was not possible to express that Logging's advice (when + concerned with the concrete aspect MyLogging) dominated + Profiling's advice (when concerned with the concrete aspect + MyProfiling) without adding a dominates clause to Logging + itself. In AspectJ 1.1, we can express that constraint with a + simple:
+ +declare precedence: MyLogging, MyProfiling; ++ +
By default, advice in a sub-aspect has more precedence than + advice in a super-aspect. One use of the AspectJ 1.0 dominates + form was to change this precedence: +
+ +abstract aspect SuperA dominates SubA { + pointcut foo(): ... ; + + before(): foo() { + // in AspectJ 1.0, runs before the advice in SubA + // because of the dominates clause + } + } + + aspect SubA extends SuperA { + before(): foo() { + // in AspectJ 1.0, runs after the advice in SuperA + // because of the dominates clause + } + } ++ +
This no longer works in AspectJ 1.1, since declare precedence only + matters for concrete aspects. Thus, if you want to regain this kind + of precedence change, you will need to refactor your aspects. +
+ +The AspectJ 1.1 compiler now accepts a -sourceroots option used to + pass all .java files in particular directories to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows).
+ +So, if you have your project separated into a gui module and a + base module, each of which is stored in a directory tree, you might + use one of +
+ +ajc -sourceroots /myProject/gui:/myProject/base + ajc -sourceroots d:\myProject\gui;d:\myProject\base ++ +
This option may be used in conjunction with lst files, listing + .java files on the command line, and the -injars option. +
+ +The AspectJ 1.1 compiler now accepts an -injars option used to + pass all .class files in a particular jar file to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows).
+ +So, if MyTracing.java defines a trace aspect that you want to + apply to all the classes in myBase.jar and myGui.jar, you would use + one of:
+ +ajc -injars /bin/myBase.jar:/bin/myGui.jar MyTracing.java + ajc -injars d:\bin\myBase.jar;d:\bin\myGui.jar MyTracing.java ++ +
The class files in the input jars must not have had advice woven + into them, since AspectJ enforces the requirement that advice is woven + into a particular classfile only once. So if the classfiles in the + jar file are to be created with the ajc compiler (as opposed to a pure + Java compiler), they should not be compiled with any non-abstract + aspects.
+ +This option may be used in conjunction with lst files, listing + .java files on the command line, and the -sourceroots option. +
+ +The -outjar option takes the name of a jar file into which the + results of the compilation should be put. For example: + +
ajc -injars myBase.jar MyTracing.java -outjar myTracedBase.jar ++ +
No meta information is placed in the output jar file.
+ +The AspectJ 1.1 compiler now supports incremental compilation. + When ajc is called with the -incremental option, it must also be + passed a -sourceroots option specifying a directory to incrementally + compile. Once the initial compile is done, ajc waits for console + input. Every time it reads a new line (i.e., every time the user + hits return) ajc recompiles those input files that need recompiling. +
+ +This new functionality is still only lightly tested.
+ +The -XnoWeave option suppresses weaving, and generates + classfiles and that can be passed to ajc again (through the + -injars option) to generate final, woven classfiles.
+ + This option was originally envisioned to be the primary way to
+ generate binary aspects that could be linked with other code, and
+ so it was previously (in AspectJ 1.1beta1) named
+ -noweave
. We feel that using the
+ -aspectpath
option is a
+ much better option. There may still be use cases for unwoven
+ classfiles, but we've moved the flag to experimental status.
+
When aspects are compiled into classfiles, they include all + information necessary for the ajc compiler to weave their advice + and deal with their inter-type declarations. In order for these + aspects to have an effect on a compilation process, they must be + passed to the compiler on the -aspectpath. Every .jar file on + this path will be searched for aspects and any aspects that are + found will be enabled during the compilation. The binary forms of + this aspects will be untouched.
+ +The 1.0 implementation of AspectJ, when given: +
+ +class MyRunnable implements Runnable { + public void run() { ... } + } + + aspect A { + call(): (void run()) && target(MyRunnable) { + // do something here + } + } ++ +
would cause A's advice to execute even when, say, java.lang.Thread + called run() on a MyRunnable instance. +
+ +With the new compiler, two things have happened in regard to + callee-side calls: +
+ +With these two points in mind, advice in an aspect will not be + applied to call join points whose call site is completely + unavailable to the aspect.
+ +This implementation decision is completely in the letter and + the spirit of the AspectJ language. From the semantics guide + describing code the implementation controls:
+ ++ But AspectJ implementations are permitted to deviate from this + in a well-defined way -- they are permitted to advise only + accesses in code the implementation + controls. Each implementation is free within certain + bounds to provide its own definition of what it means to control + code. ++ +
And about a particular decision about the 1.0.6 + implementation:
+ ++ Different join points have different requirements. Method call + join points can be advised only if ajc controls + either the code for the caller or the code + for the receiver, and some call pointcut designators may + require caller context (what the static type of the receiver + is, for example) to pick out join points. ++ +
The 1.1 implementation makes a different design decision: + Method call join points can be advised only if ajc (in compiler or + linker form) controls the code for the caller.
+ +What does 1.1 gain from this?
+ +What does 1.1 lose from this?
+ +What are the possibilities for the future?
+ +How will this affect developers?
+The AspectJ 1.0 compiler supported a number of options that + started with X, for "experimental". Some of them will not be + supported in 1.1, either because the "experiment" succeeded (in + which case it's part of the normal functionality) or failed. + Others will be supported as is (or nearly so) in 1.1: +
+ +-XnoInline
.
+ Building on the eclipse compiler has given us access to a very + sophisticated problem reporting system as well as highly optimized + error messages for pure Java code. Often this leads to noticeably + better error messages than from ajc-1.0.6. However, when we don't + handle errors correctly this can sometimes lead to cascading error + messages where a single small syntax error will produce dozens of + other messages. Please report any very confusing error messages as + bugs.
+ + +For compiler errors and warnings detected during bytecode weaving, + source code context will not be displayed. In particular, for declare + error and declare warning statements, the compiler now only emits the + file and line. We are investigating ways to overcome this in cases + where the source code is available; in cases where source code is + not available, we might specify the signature of the offending code. + For more information, see bug 31724.
+ + +-Xlint:ignore,error,warning
will set the level for
+ all Xlint warnings. -Xlint
, alone, is an
+ abbreviation for -Xlint:warning
.
The -Xlintfile:lint.properties
allows fine-grained
+ control. In tools.jar, see
+ org/aspectj/weaver/XlintDefault.properties
for the
+ default behavior and a template to copy.
More -Xlint
warnings are supported now, and
+ we may add disabled warnings in subsequent bug-fix releases of 1.1.
+ Because the configurability allows users to turn off
+ warnings, we will be able to warn about more potentially
+ dangerous situations, such as the potentially unsafe casts used by
+ very polymorphic uses of proceed in around advice.
Because AspectJ 1.1 does not generate source code after + weaving, the source-code-specific options -preprocess, -usejavac, + -nocomment and -workingdir options are meaningless and so not + supported.
+ +Because AspectJ 1.1 uses the Eclipse compiler, which has its + own mechanism for changing strictness, we no longer support the + -strict and -lenient options.
+ +AspectJ 1.1 does not have a -porting option.
+ +Because we build on Eclipse, the compiler will no longer run + under J2SE 1.2. You must run the compiler (and all tools based on + the compiler) using J2SE 1.3 or later. The code generated by the + compiler can still run on Java 1.1 or later VM's if compiled against + the correct runtime libraries.
+ +AspectJ 1.1 does not allow the inter-type definition of a + zero-argument constructor on a class with a visible default + constructor. So this is no longer allowed:
+ ++ class C {} + + aspect A { + C.new() {} // was allowed in 1.0.6 + // is a "multiple definitions" conflict in 1.1 + } ++ +
In the Java Programming Language, a class defined without a
+ constructor actually has a "default" constructor that takes no
+ arguments and just calls super()
.
This default constructor is a member of the class like any other + member, and can be referenced by other classes, and has code generated + for it in classfiles. Therefore, it was an oversight that AspectJ + 1.0.6 allowed such an "overriding" inter-type constructor definition. +
+ +In AspectJ, interfaces may have non-static members due to + inter-type declarations. Because of this, the semantics of AspectJ + defines the order that initializer code for interfaces is run. +
+ +In the semantics document for AspectJ 1.0.6, the following + promises were made about the order of this initialization: +
+ +The first two properties are important and are preserved in + AspectJ 1.1, but the third property is and was ludicrous, and was + never properly implemented (and never could be) in AspectJ 1.0.6. + Consider:
+ ++ interface Top0 {} + interface Top1 {} + interface I extends Top0, Top1 {} + interface J extends Top1, Top0 {} + + class C implements I, J {} + // I says Top0's inits must run before Top1's + // J says Top1's inits must run before Top0's + + aspect A { + int Top0.i = foo("I'm in Top0"); + int Top1.i = foo("I'm in Top1"); + static int foo(String s) { + System.out.println(s); + return 37; + } + } ++ +
This was simply a bug in the AspectJ specification. The correct + third rule is: +
+ +the initializers for a type's superclass are run before the + initializers for its superinterfaces. ++ + +
In AspectJ 1.0.6, the join point for setting a field F had, as a + return type, F's type. This was "java compatible" because + field assignment in java, such as "Foo.i = 37", is in fact an + expression, and does in fact return a value, the value that the + field is assigned to. +
+ +This was never "java programmer compatible", however, largely + because programmers have absorbed the good style of rarely using an + assignment statement in a value context. Programmers typically expect + "Foo.i = 37" not to return a value, but to simply assign a value.
+ +Thus, programmers typically wanted to write something like: +
+ ++ void around(): set(int Foo.i) { + if (theSetIsAllowed()) { + proceed(); + } + } ++ +
And were confused by it being a compile-time error. They weren't + confused for long, and soon adapted to writing: +
+ ++ int around(): set(int Foo.i) { + if (theSetIsAllowed()) { + return proceed(); + } else { + return Foo.i; + } + } ++ +
But there was definitely a short disconnect.
+ +On top of that, we were never shown a convincing use-case for + returning an interesting value from a set join point. When we + revisited this issue, in fact, we realized we had a long-standing bug + in 1.0.6 dealing with the return value of pre-increment expressions + (such as ++Foo.i) that nobody had found because nobody cares about the + return value of such join points. +
+ +So, because it's easier to implement, and because we believe that + this is the last possibility to make the semantics more useful, we + have made set join points have a void return type in 1.1.
+ + The -XnoInline
+ option to indicate that no inlining of any kind should be done. This
+ is purely a compiler pragma: No program semantics (apart from stack
+ traces) will be changed by the presence or absence of this option.
+
Even in 1.0.6, the AspectJ compiler has occasionally needed to + convert the visibility of a package-level class to a public one. This + was previously done in an ad-hoc basis that took whole-program + analysis into account. With the incremental compilation model of + AspectJ 1.1, we can now specify the occasions when the compiler makes + these visibility changes. +
+ + In particular, the types used in the this
,
+ target
, and args
pointcuts are made public,
+ as are the super-types from declare parents
and the
+ exception type from declare soft
.
+
We believe the visibility changes could be avoided in the future + with various implementation tricks if they become a serious + concern, but did not encounter them as such a concern when they were + done in the 1.0.6 implementation.
+ +In Java, the + operator sometimes results in StringBuffer objects +being created, appended to, and used to generate a new String. Thus, +
+ ++class Foo { + String makeEmphatic(String s) { + return s + "!"; + } +} ++ +
is approximately the same at runtime as +
+ ++class Foo { + String makeEmphatic(String s) { + return new StringBuffer(s).append("!").toString(); + } +} ++ + +
In the design process of AspectJ 1.0.6 we didn't expose those +StringBuffer methods and constructors as join points (though we did +discuss it), but in 1.1 we do.
+ +This change is likely to affect highly wildcarded aspects, and can +do so in surprising ways. In particular: +
+ ++class A { + before(int i): call(* *(int)) && args(i) { + System.err.println("entering with " + i); + } +} ++ +
may result in a stack overflow error, since the argument to +println is really
+ ++new StringBuffer("entering with ").append(i).toString() ++ +
which has a call to StringBuffer.append(int). In such cases, it's +worth restricting your pointcut, with something like one of: +
+ ++call(* *(int)) && args(i) && !within(A) +call(* *(int)) && args(i) && !target(StringBuffer) ++ +
Consider the following aspect +
+ ++public aspect SwingCalls { + + pointcut callingAnySwing(): call(* javax.swing..*+.*(..)); + + before(): callingAnySwing() { + System.out.println("Calling any Swing"); + } +} ++ +
And then consider the two statements +
+ ++ JFrame frame = new JFrame(); + frame.setTitle("Title"); ++ +
According to the Java Language Specification version 2, the call
+to frame.setTitle("Title")
should always produce the
+bytecode for a call to javax.swing.JFrame.setTitle
.
+However, older compilers (and eclipse when run without the
+-1.4
flag) will generate the bytecode for a call to
+java.awt.Frame.setTitle
instead since this method is not
+overriden by JFrame. The AspectJ weaver depends on the correctly
+generated bytecode in order to match patterns like the one you show
+correctly.
This is a good example of why the pattern call(* *(..)) &&
+target(JFrame)
is the recommended style. In general, OO
+programmers don't want to care about the static type of an object at a
+call site, but only want to know the dynamic instanceof behavior which
+is what the target matching will handle.
The AspectJ 1.1.0 release contains a small number of known limitations +relative to the AspectJ 1.1 language. +For the most up-to-date information about known limitations in an +AspectJ 1.1 release, see the bug database at + http://bugs.eclipse.org/bugs, +especially the open bugs for the + + compiler, + + IDE support, + + documentation, and + + Ant tasks. +Developers should know about bugs marked with the "info" keyword +because those bugs reflect failures to implement the 1.1 language perfectly. +These might be fixed during the 1.1 release cycle; find them using the query + + http://bugs.eclipse.org/bugs/buglist.cgi?product=AspectJ&keywords=info + +For ajc's 1.1 implementation limitations, see + + Programming Guide Appendix: "Implementation Notes". + +
+ -- 2.39.5