From: Alexander Kriegisch Date: Thu, 4 Jan 2024 06:07:07 +0000 (+0700) Subject: Move compiler-weaver.adoc up one directory level X-Git-Tag: V1_9_21_1~57 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=32eccc458582cf8298047f6ac3ba1936d4c02854;p=aspectj.git Move compiler-weaver.adoc up one directory level Also rename ADOC on the way and move images to subdirectory. This makes the Maven Resource execution superfluous. TODO: Adjust image links in compiler-weaver.adoc in next commit. Signed-off-by: Alexander Kriegisch --- diff --git a/docs/developer/compiler-weaver.adoc b/docs/developer/compiler-weaver.adoc new file mode 100644 index 000000000..817656e35 --- /dev/null +++ b/docs/developer/compiler-weaver.adoc @@ -0,0 +1,1192 @@ +== Guide for Developers of the AspectJ Compiler and Weaver + +_Latest (non-license) content update: 2004-02-20 by jhugunin_ + +This document is written for developers who want to understand the +implementation of AspectJ. It provides a top-down picture of the +compiler and weaver implementations. This high-level picture should make +it easier to read and understand the source code for AspectJ. + +The AspectJ compiler/weaver (ajc) is composed of three primary modules. + +* *org.aspectj.ajdt.core* - this is the compiler front-end and extends +the eclipse Java compiler from *org.eclipse.jdt.core*. Because of the +dependencies on parts of eclipse this generates a large ~6MB jar. +* *weaver* - this provides the bytecode weaving functionality. It has +very few external dependencies to minimize the size required for +deployment of load-time weavers. Currently the build process doesn't +produce a separate jar for just the weaver, but that will have to change +for AspectJ-1.2. +* *runtime* - these are the classes that are used by generated code at +runtime and must be redistributed with any system built using AspectJ. +This module has no external dependencies and produces a tiny ~30KB jar. + +image:overview.gif[image] + +The AspectJ compiler accepts both AspectJ bytecode and source code and +produces pure Java bytecode as a result. Internally it has two stages. +The front-end (org.aspectj.ajdt.core) compiles both AspectJ and pure +Java source code into pure Java bytecode annotated with additional +attributes representing any non-java forms such as advice and pointcut +declarations. The back-end of the AspectJ compiler (weaver) implements +the transformations encoded in these attributes to produce woven class +files. The back-end can be run stand-alone to weave pre-compiled aspects +into pre-compiled .jar files. In addition, the back-end exposes a +weaving API which can be used to implement ClassLoaders that will weave +advice into classes dynamically as they are loaded by the virtual +machine. + +=== Compiler front-end (org.aspectj.ajdt.core) + +The front-end of the AspectJ compiler is implemented as an extension of +the Java compiler from eclipse.org. The source-file portion of the +AspectJ compiler is made complicated by inter-type declarations, declare +parents, declare soft, and privileged aspects. All of these constructs +require changes to the underlying compiler to modify Java’s name-binding +and static checking behavior. + +As the compiler extends the jdt.core compiler, the package structure of +this module mimics that of the jdt.core module. The design works hard to +minimize the set of changes required to org.eclipse.jdt.core because a +fun 3-way merge is required each time we want to move to a new +underlying version of this code. The ultimate goal is to contribute all +of our changes to jdt.core back into the main development branch some +day. + +The basic structure of a compile is very simple: + +. Perform a shallow parse on all source files +. Pass these compilation units through AjLookupManager to do type +binding and some AspectJ augmentation +. For each source file do a deep parse, annotation/analysis, and then +code generation + +==== Top-level parse tree + +Let's trace the following example program through the compiler. + +[source, java] +.... +package example.parse.tree; + +import org.aspectj.lang.*; + +public class Main { + public static void main(String[] args) { + new Main().doit(); + } + + private void doit() { + System.out.println("hello"); + } +} + +aspect A { + pointcut entries(Main o): execution(void doit()) && this(o); + before(Main o): entries(o) { + o.counter++; + System.out.println("entering: " + thisJoinPoint); + } + + private int Main.counter = 0; +} +.... + +When parsed, this program will produce the following tree. + +image:top-tree.gif[image] + +==== PointcutDeclaration processing + +Let's look more closely at the pointcut declaration: + +[source, java] +.... +pointcut entries(Main o): execution(void doit()) && this(o); +.... + +image:pointcut-dec.gif[image] + +The pointcut declaration is implemented as a subtype of a method +declaration. The actual pointcut is parsed by the weaver module. This +parsing happens as part of the shallow parse phase. This is because this +information might be needed to implement a declare soft. + +==== AdviceDeclaration processing + +Next we look at the processing for an advice declaration: + +[source, java] +.... +before(Main o): entries(o) { + o.counter++; + System.out.println("entering: " + thisJoinPoint); +} +.... + +After parsing, the AdviceDeclaration.postParse method will be called to +make this a valid MethodDeclaration so that the standard eclipse code +for analyzing a method body can be applied to the advice. After +postParse, the selector is filled in and several additional arguments +are added for the special thisJoinPoint forms that could be used in the +body. + +image:advice-dec.gif[image] + +At this point the statements field which will hold the body of the +advice is still null. This field is not filled in until the second stage +of the compiler when full parsing is done on each source file as a +prelude to generating the classfile. + +==== Overview of the main classes in org.aspectj.ajdt.core + +The main classes in this module are shown in the following diagram: + +image:ajdt-uml.gif[image] + +=== Weaving back-end (weaver) + +This provides all of the weaving functionality. It has very few +dependencies to keep the code as small as possible for deployment in +load-time weavers - only asm, bridge and util which are each very small +modules with no further dependencies. This also depends on a patched +version of the bcel library from apache.org. The patches are only to fix +bcel bugs that can't be worked around in any other way. + +There are only four packages in this system. + +* org.aspectj.weaver - general classes that can be used by any weaver +implementation +* org.aspectj.weaver.patterns - patterns to represent pointcut +designators and related matching constructs +* org.aspectj.weaver.ast - a very small library to represent simple +expressions without any bcel dependencies +* org.aspectj.weaver.bcel - the concrete implementation of shadows and +the weaver using the bcel library from apache.org + +The back-end of the AspectJ compiler instruments the code of the system +by inserting calls to the precompiled advice methods. It does this by +considering that certain principled places in bytecode represent +possible join points; these are the “static shadow” of those join +points. For each such static shadow, it checks each piece of advice in +the system and determines if the advice's pointcut could match that +static shadow. If it could match, it inserts a call to the advice’s +implementation method guarded by any dynamic testing needed to ensure +the match. + +=== Runtime support library (runtime) + +This library provides classes that are used by the generated code at +runtime. These are the only classes that must be redistributed with a +system built using AspectJ. Because these classes are redistributed +this library must always be kept as small as possible. It is also +important to worry about binary compatibility when making changes to +this library. There are two packages that are considered public and may +be used by AspectJ programs. + +* org.aspectj.lang +* org.apectj.lang.reflect + +There are also several packages all under the header org.aspectj.runtime +that are considered private to the implementation and may only be used +by code generated by the AspectJ compiler. + +=== Mappings from AspectJ language to implementation + +[cols=",,",] +|=== +| |org.aspectj.ajdt.internal.compiler |weaver - org.aspectj.weaver. + +|aspect |ast.AspectDeclaration |CrosscuttingMembers + +|advice |ast.AdviceDeclaration |Advice + bcel.BcelShadowMunger + +|pointcut declaration |ast.PointcutDeclaration +|ResolvedPointcutDefinition + +|declare error/warning |ast.DeclareDeclaration |Checker + +patterns.DeclareErrorOrWarning + +|declare soft |ast.DeclareDeclaration + problem.AjProblemReporter +|Advice (w/ kind = Softener) + patterns.DeclareSoft + +|declare parents |ast.DeclareDeclaration + lookup.AjLookupEnvironment +|patterns.DeclareParents + NewParentTypeMunger + +|inter-type decls |ast.InterType*Declaration + lookup.InterType*Binding ++ lookup.AjLookupEnvironment |New*TypeMunger + bcel.BcelTypeMunger + +|if pcd |ast.IfPseudoToken + ast.IfMethodDeclaration +|patterns.IfPointcut + +|pcd |ast.PointcutDesignator |patterns.Pointcut hierarchy +|=== + +== Tutorial: implementing a throw join point + +This tutorial will walk step-by-step through the process of adding a new +join point to AspectJ for the moment when an exception is thrown. In +Java source code, the shadow of this point is a throw statement. In Java +bytecode, the shadow is the athrow instruction. + +This tutorial is recommended to anyone who wants to get a better feel +for how the implementation of AspectJ really works. Even if you're just +working on a bug fix or minor enhancement, the process of working with +the AspectJ implementation will be similar to that described below. The +size of your actual code changes will likely be smaller, but you are +likely to need to be familiar with all of the pieces of the +implementation described below. + +=== Part 1: Adding the join point and corresponding pcd + +The first part of this tutorial will implement the main features of the +throw join point. We will create a new join point shadow corresponding +to the athrow instruction and also create a new pointcut designator +(pcd) for matching it. + +==== Step 1. Synchronize with repository and run the existing test suite + +Do a Team->Synchronize With Repository and make sure that your tree is +completely in sync with the existing repository. Make sure to address +any differences before moving on. + +Run the existing test suite. I currently do this in four steps: + +* weaver/testsrc/BcWeaverModuleTests.java +* org.aspectj.ajdt.core/testsrc/EajcModuleTests.java +* ajde/testsrc/AjdeModuleTests.java +* Harness on ajctests.xml -- at least under 1.4, preferably under both +1.3 and 1.4. + +There should be no failures when you run these tests. If there are +failures, resolve them with the AspectJ developers before moving on. + +==== Step 2. Write a proto test case + +{empty}a. Create a new file in tests/design/pcds/Throw.java + +[source, java] +.... +import org.aspectj.testing.Tester; + +public class Throws { + public static void main(String[] args) { + try { + willThrow(); + Tester.checkFailed("should have thrown exception"); + } catch (RuntimeException re) { + Tester.checkEqual("expected exception", re.getMessage()); + } + } + + static void willThrow() { + throw new RuntimeException("expected exception"); + } +} + +aspect A { + before(): withincode(void willThrow()) { + System.out.println("about to execute: " + thisJoinPoint); + } +} +.... + +{empty}b. Create a temporary test harness file to run just this test in +myTests.xml + +[source, xml] +.... + + + + + + + +.... + +{empty}c. Run this test using the harness. You should see: + +[source, text] +.... +about to execute: execution(void Throws.willThrow()) +about to execute: call(java.lang.RuntimeException(String)) +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 2 seconds +.... + +==== Step 3. Implement the new join point shadow kind + +Modify runtime/org.aspectj.lang/JoinPoint.java to add a name for the +Throw shadow kind. + +[source, java] +.... +static String THROW = "throw"; +.... + +Modify weaver/org.aspectj.weaver/Shadow.java to add the Throw shadow +kind. This adds a static typesafe enum for the Throw Kind. The +constructor uses the name from the runtime API to ensure that these +names will always match. The '12' is used for serialization of this kind +to classfiles and is part of the binary API for aspectj. The final +'true' indicates that this joinpoint has its arguments on the stack. +This is because the throw bytecode in Java operates on a single argument +that is a Throwable which must be the top element on the stack. This +argument is removed from the stack by the bytecode. + +[source, java] +.... +public static final Kind Throw = new Kind(JoinPoint.THROW, 12, true); +.... + +We also modify the neverHasTarget method to include the Throw kind +because in Java there is no target for the throwing of an exception. + +[source, java] +.... +public boolean neverHasTarget() { + return this == ConstructorCall + || this == ExceptionHandler + || this == PreInitialization + || this == StaticInitialization + || this == Throw; +} +.... + +In the read method on Shadow.Kind, add another case to read in our new +Shadow.Kind. + +[source, java] +.... +case 12: return Throw; +.... + +==== Step 4. Create this new kind of joinpoint for the throw bytecode + +Modify weaver/org.aspectj.weaver.bcel/BcelClassWeaver.java to recognize +this new joinpoint kind. In the method + +[source, java] +.... +private void match( + LazyMethodGen mg, + InstructionHandle ih, + BcelShadow enclosingShadow, + List shadowAccumulator) +{ +.... + +Add a test for this instruction, i.e. + +[source, java] +.... +} else if (i == InstructionConstants.ATHROW) { + match(BcelShadow.makeThrow(world, mg, ih, enclosingShadow), + shadowAccumulator); +} +.... + +Then, modify BcelShadow.java to create this new kind of join point +shadow: + +[source, java] +.... +public static BcelShadow makeThrow( + BcelWorld world, + LazyMethodGen enclosingMethod, + InstructionHandle throwHandle, + BcelShadow enclosingShadow) +{ + final InstructionList body = enclosingMethod.getBody(); + TypeX throwType = TypeX.THROWABLE; //!!! not as precise as we'd like + TypeX inType = enclosingMethod.getEnclosingClass().getType(); + BcelShadow s = + new BcelShadow( + world, + Throw, + Member.makeThrowSignature(inType, throwType), + enclosingMethod, + enclosingShadow); + ShadowRange r = new ShadowRange(body); + r.associateWithShadow(s); + r.associateWithTargets( + Range.genStart(body, throwHandle), + Range.genEnd(body, throwHandle)); + retargetAllBranches(throwHandle, r.getStart()); + return s; +} +.... + +Finally modify weaver/org.aspectj.weaver/Member.java to generate the +needed signature + +[source, java] +.... +public static Member makeThrowSignature(TypeX inType, TypeX throwType) { + return new Member( + HANDLER, + inType, + Modifier.STATIC, + "throw", + "(" + throwType.getSignature() + ")V"); +} +.... + +Run the proto test again and you should see: + +[source, text] +.... +about to execute: execution(void Throws.willThrow()) +about to execute: call(java.lang.RuntimeException(String)) +about to execute: throw(catch(Throwable)) +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 3 seconds +.... + +That last line shows the 'throw(catch(Throwable))' join point. This is a +slightly confusing string form, but it is the first sign of our brand +new join point. The reason for the weird 'catch(Throwable)' part is that +we used Member.HANDLER for the kind of the signature of this join point. +That's clearly not correct. We'll fix that at the end of the lesson as +part of the clean-up. For now, let's go on with the interesting parts. + +==== Step 5. Extend our proto-test to use a pointcut designator for matching + +Add a second piece of before advice to the test aspect A: + +[source, java] +.... +before(): throw(Throwable) { + System.out.println("about to throw: " + thisJoinPoint); +} +.... + +When we run the test again we'll get a long error message from the +harness. The interesting part of the message is the following: + +[source, text] +.... +[ 0] [error 0]: error can't find referenced pointcut at C:\aspectj\eclipse\tests\design\pcds\Throws.java:23:0 +.... + +This error is not quite what you might have expected. You might have +hoped for a syntax error saying that there is not 'throw' pointcut +designator defined. Unfortunately, this is a weakness in the syntax of +AspectJ where primitive PCDs and named PCDs have the same syntax, so the +compiler can't tell the difference between a misspelled or non-existent +primitive PCD and a named PCD reference that is missing. This also has +some impact on extending the primitive PCDs because it will break +existing programs. In this case, when we add the throw PCD we will break +any existing programs that use throw as the name for a user-defined PCD. +Fortunately because throw is a Java keyword this particular change is +very safe. + +==== Step 6. Extend the PCD parser to handle this new primitive PCD + +Modify the parseSinglePointcut method in +weaver/org.aspectj.weaver.patterns/PatternParser.java to add one more +else if clause for the throw pcd: + +[source, java] +.... +} else if (kind.equals("throw")) { + parseIdentifier(); eat("("); + TypePattern typePat = parseTypePattern(); + eat(")"); + return new KindedPointcut(Shadow.Throw, + new SignaturePattern(Member.HANDLER, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, + new TypePatternList(new TypePattern[] {typePat}), + ThrowsPattern.ANY)); +.... + +Modify the matches method in +weaver/org.aspectj.weaver.patterns/SignaturePattern.java to add: + +[source, java] +.... +if (kind == Member.HANDLER) { + return parameterTypes.matches(world.resolve(sig.getParameterTypes()), + TypePattern.STATIC).alwaysTrue(); +} +.... + +Run the proto test again and you should see: + +[source, text] +.... +about to execute: execution(void Throws.willThrow()) +about to execute: call(java.lang.RuntimeException(String)) +about to execute: throw(catch(Throwable)) +about to throw: throw(catch(Throwable)) +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds +.... + +Make sure that you see the 'about to throw' printed before moving on. +This shows that the throw PCD is now successfully matching the throw +join point shadow we added earlier. + +==== Step 7. Check that we're properly providing the single thrown argument (and clean-up the test) + +Now that we have a valid pcd for this advice, we can simplify our test +case. Modify our test aspect A to be the following. In addition to +removing the overly generic withincode pcd, this change also prints the +actual object that is about to be thrown: + +[source, java] +.... +aspect A { + before(Throwable t): throw(*) && args(t) { + System.out.println("about to throw: '" + t+ "' at " + thisJoinPoint); + } +} +.... + +When we run the test again we should see the output below: + +[source, text] +.... +about to throw: 'java.lang.RuntimeException: expected exception' at throw(catch(Throwable)) +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds +.... + +Congratulations! You've just implemented the throw join point and PCD. +This code isn't yet ready to be checked into any repository. It still +has some rough edges that need to be smoothed. However, you've now added +a new join point to the AspectJ language and a corresponding PCD to +match it. This is a good time to take a break before moving on to part +two. + +=== Part 2: Getting the signature of this new join point right + +We know that throw(catch(Throwable)) is not the right thing to be +printing for the signature at this join point. What is the correct +signature? At the beginning of the tutorial, we explained that the +preferred design for the pcd was to have +throw(StaticTypeOfExceptionThrown). In step 4, we set the type of the +exception thrown to be 'Throwable'. Can we set this to be more accurate? +Looking at the source code, it seems easy to identify the static type of +the exception that is thrown: + +[source, java] +.... +throw new RuntimeException("expected exception"); +.... + +In the source code to a Java program there is a well-defined static type +for the exception that is thrown. This static type is used for various +stages of flow analysis to make sure that checked exceptions are always +correctly handled or declared. The ThrowStatement class in our own +compiler has a special field for exceptionType that stores the static +type of the exception thrown. Unfortunately, this static type is much +harder to recover from the corresponding bytecode. In this case we would +need to do flow analysis to figure out what the static type is for the +object on the top of the stack when the athrow instruction executes. +This analysis can certainly be done. In fact this analysis is a small +part of what every JVM must do to verify the type safety of a loaded +classfile. + +However, the current AspectJ weaver doesn't do any of this analysis. +There are many good reasons to extend it in this direction in order to +optimize the code produced by the weaver. If we were really implementing +this feature, this would be the time for a long discussion on the +aspectj-dev list to decide if this was the right time to extend the +weaver with the code flow analysis needed to support a static type for +the throw join point. For the purposes of this tutorial, we're going to +assume that it isn't the right time to do this (implementing flow +analysis for bytecodes would add another 50 pages to this tutorial). +Instead we're going to change the definition of the throw join point to +state that its argument always has a static type of Throwable. We still +allow dynamic matching in args to select more specific types. In +general, good AspectJ code should use this dynamic matching anyway to +correspond to good OO designs. + +==== Step 1. Change the signature of the throw pcd + +Since we aren't going to recover the static type of the exception +thrown, we need to fix the parser for the throw pcd to remove this +information. We'll fix the PatternParser code that we added in step 1.6 +to read as follows: + +[source, java] +.... +} else if (kind.equals("throw")) { + parseIdentifier(); eat("("); + eat(")"); + return new KindedPointcut(Shadow.Throw, + new SignaturePattern(Member.THROW, ModifiersPattern.ANY, + TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, + TypePatternList.ANY, + ThrowsPattern.ANY)); +.... + +Notice that this code also starts to fix the member kind to be +Member.THROW instead of the bogus Member.HANDLER that we were using +before. To make this work we have a set of things to do. First, let's +create this new kind in org.aspectj.weaver.Member. Find where the +HANDLER kind is defined there, and add a corresponding throw kind: + +[source, java] +.... +public static final Kind THROW = new Kind("THROW", 8); +.... + +We also need to fix the serialization kind in +Member.Kind.read(DataInputStream) just above this constant list to add a +case for this new kind: + +[source, java] +.... +case 8: return THROW; +.... + +Still in this file, we also need to fix Member.makeThrowSignature to use +this new kind: + +[source, java] +.... +public static Member makeThrowSignature(TypeX inType, TypeX throwType) { + return new ResolvedMember( + THROW, + inType, + Modifier.STATIC, + "throw", + "(" + throwType.getSignature() + ")V"); +} +.... + +If you run the test now you'll get an error from the parser reminding us +that the throw pcd now doesn't accept a type pattern: + +[source, text] +.... +------------ FAIL: simple throw join point() +... +C:\aspectj\eclipse\tests\design\pcds\Throws.java:19:0 Syntax error on token "*", ")" expected + +FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds +.... + +This is an easy fix to the test case as we modify our pcd for the new +syntax in the aspect A in our Throws.java test code: + +[source, java] +.... +before(Throwable t): throw() && args(t) { +.... + +Now when we run the test case it looks like everything's fixed and we're +passing: + +[source, text] +.... +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 2 seconds +.... + +==== Part 2. Make a real test case + +The pass result from running our test should worry you. Unlike previous +runs, this test run doesn't show the output from our System.out.println +in the before advice. So, it's clear this advice is not running. The +problem is that even though the advice is not running, the test case is +passing. We need to make this a real test case to fix this. We'll do +that by adding code that notes when the advice runs and then checks for +this event. This code uses the Tester.event and Tester.checkEvent +methods: + +[source, java] +.... +import org.aspectj.testing.Tester; + +public class Throws { + public static void main(String[] args) { + try { + willThrow(); + Tester.checkFailed("should have thrown exception"); + } catch (RuntimeException re) { + Tester.checkEqual("expected exception", re.getMessage()); + } + Tester.checkEvents(new String[] { "before throw" }); + } + + static void willThrow() { + throw new RuntimeException("expected exception"); + } +} + +aspect A { + before(Throwable t): throw() && args(t) { + Tester.event("before throw"); + //System.out.println("about to throw: '" + t+ "' at " + thisJoinPoint); + } +} +.... + +Now when we run our test case it will fail. This failure is good because +we're not matching the throw join point anymore. + +[source, text] +.... +------------ FAIL: simple throw join point() +... +[ 1] [fail 0]: fail [ expected event "before throw" not found] + +FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds +.... + +==== Step 3. Fix signature matching again + +In org.aspectj.weaver.patterns.SignaturePattern.matches, we need to +handle throw signature matching the same way we handle advice signature +matching. Both of these pcds match solely on the kind of join point and +use combinations with other pcds to narrow their matches. So, find the +line for kind == Member.ADVICE and add the same line below it for +Member.THROW. + +[source, java] +.... +if (kind == Member.ADVICE) return true; +if (kind == Member.THROW) return true; +.... + +This change will make our test case pass again. Run it to be sure. + +There's an interesting tension between a good automated test and a good +test for development. Our new test case now correctly includes an +automated test to let us know when we are and are not matching the new +throw join point. However, without the println the test doesn't feel as +satisfactory to me to run during development. I often like to turn this +kind of printing back on the see what's happening. If you uncomment to +System.out.println in the test aspect A and rerun the test, you won't be +very happy with the results: + +[source, text] +.... +------------ FAIL: simple throw join point() +... +unimplemented +java.lang.RuntimeException: unimplemented + at org.aspectj.weaver.Member.getSignatureString(Member.java:596) +... + +FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds +.... + +It looks like there's more work to do to add the new member kind for +Member.THROW. This problem only shows up when we try to print +thisJoinPoint. It's showing that we haven't updated the reflection API +to understand this new signature kind. + +==== Step 4. Extend org.aspectj.lang.reflect to understand throw signatures + +We need to add a couple of classes to the reflection API to implement +the throw signature. Because we decided at the beginning of this section +to not include the static type of the exception thrown in the throw +signature, these classes are extremely simple. Nevertheless, we have to +build them. Notice that when we add new source files to the system we +need to include the standard eclipse EPL license header. + +[source, java] +.... +/* ******************************************************************* + * Copyright (c) 2006 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v 2.0 + * which accompanies this distribution and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt + * + * Contributors: + * Jim Hugunin initial implementation + * ******************************************************************/ + +package org.aspectj.lang.reflect; +import org.aspectj.lang.Signature; + +public interface ThrowSignature extends Signature { } +.... + +[source, java] +.... +/* ******************************************************************* + * Copyright (c) 2006 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v 2.0 + * which accompanies this distribution and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt + * + * Contributors: + * Jim Hugunin initial implementation + * ******************************************************************/ + +package org.aspectj.runtime.reflect; +import org.aspectj.lang.reflect.ThrowSignature; + +class ThrowSignatureImpl extends SignatureImpl implements ThrowSignature { + + ThrowSignatureImpl(Class declaringType) { + super(0, "throw", declaringType); + } + + ThrowSignatureImpl(String stringRep) { + super(stringRep); + } + + String toString(StringMaker sm) { + return "throw"; + } +} +.... + +To finish up our work in the runtime module, we need to extend +org.aspectj.runtime.reflect.Factory to add a factory method for this new +signature kind: + +[source, java] +.... +public ThrowSignature makeThrowSig(String stringRep) { + ThrowSignatureImpl ret = new ThrowSignatureImpl(stringRep); + ret.setLookupClassLoader(lookupClassLoader); + return ret; +} +.... + +We're not done yet. We still need to fix up the +org.aspectj.weaver.Member class to use these new methods and types and +fix the unimplemented exception that started us down this road in the +first place. First let's add a method to create a string for the throw +signature. This is a very simple method copied from the other +create*SignatureString methods. + +[source, java] +.... +private String getThrowSignatureString(World world) { + StringBuffer buf = new StringBuffer(); + buf.append('-'); // no modifiers + buf.append('-'); // no name + buf.append(makeString(getDeclaringType())); + buf.append('-'); + return buf.toString(); +} +.... + +Now we need to modify three methods to add cases for the new +Member.THROW kind. First, Member.getSignatureMakerName add: + +[source, java] +.... +} else if (kind == THROW) { + return "makeThrowSig"; +.... + +Next, to Member.getSignatureType add: + +[source, java] +.... +} else if (kind == THROW) { + return "org.aspectj.lang.reflect.ThrowSignature"; +.... + +Finally, to Member.getSignatureString add: + +[source, java] +.... +} else if (kind == THROW) { + return getThrowSignatureString(world); +.... + +With all of these changes in place we should have working code for +thisJoinPoint reflection using our new join point and signature kinds. +Rerun the test to confirm: + +[source, text] +.... +about to throw: 'java.lang.RuntimeException: expected exception' at throw(throw) +PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds +.... + +==== Step 5. Extend the test for automated coverage of reflection + +Modify the before advice to include at least minimal checks of the new +reflective information: + +[source, java] +.... +before(Throwable t): throw() && args(t) { + Tester.event("before throw"); + Tester.checkEqual(thisJoinPoint.getSignature().toShortString(), "throw"); + Tester.checkEqual(t.getMessage(), "expected exception"); +} +.... + +As usual, you should rerun the tests and make sure they pass. + +With these changes to the reflection code, it looks like we have a +working version of the throw join point and there are no obvious pieces +that we've skipped. Take a break before proceeding to the final phase of +tests. + +=== Part 3: More serious testing + +Now it's time to get a decent testing story. The test work that we will +do here is probably too little for adding a new join point to the +aspectj language; however, it should at least give you a sense of what's +involved. + +==== Step 1. Run the test suite again + +Rerun the tests you ran at the beginning of part 1. Any failures that +occur should be resolved at this point. At the time of writing this +tutorial, I found 31 failures in the BcWeaverModuleTests. These failures +are for all of the test cases that check the exact set of shadows +produces by a given program. These test cases need to be updated based +on the new join point we're adding. These particular test cases will +probably be removed from the AspectJ test suite very soon because +they've shown themselves to be very fragile over time and they often +break for changes that are not introducing new bugs. However, you should +be aware of this kind of failure because you may find it in other unit +tests. + +You should expect to see at least one other test case fail when you run +ajcTests.xml. Here's the failure message: + +[source, text] +.... +------------ FAIL: validate (enclosing) join point and source locations() +... +[ 1] [fail 0]: fail [ unexpected event "before AllTargetJoinPoints throw(throw)" found] +.... + +Most of this message can be ignored. To find out what went wrong you +should look for messages that have "fail" in them. The last line tells +you what happened. There was an unexpected event, "before +AllTargetJoinPoints throw(catch(Throwable))". This is the signature for +one of the new throw join points that we added in part 1. How could an +existing test case match this new join point? The failing test case uses +'within(TargetClass)' to collect information about ALL join points that +are lexically within a given class. Whenever we add a new kind of join +point to the language we will extend the set of points matched by pcds +like within. This means that these changes need to be very prominently +noted in the release notes for any AspectJ release. Since we're not +writing documentation in this tutorial, we will move on an fix the test +case. + +==== Step 2. Fix the failing test case + +Now we need to fix this failing test case. The first step is to copy the +test specification into our local myTests.xml file. The easiest way to +do this is to copy the title of the failing test from the output buffer, +then open ajcTests.xml and use find to search for this title. Then copy +the xml spec for this one test into myTests.xml. Finally, run +myTests.xml to make sure you got the failing test. You should see the +same failure as before in step 1, but you should see it a lot faster +because we're only running 2 tests. + +To fix the test we need to find the source code. If you look at the test +specification, you can see that the source file is the new directory +with the name NegativeSourceLocation.java. Looking at the bottom of this +file, we see a large list of expected events. These are the join points +that we expect to see. If we look back up in TargetClass, we can see +that the only occurence of throw is just before the handler for +catch(Error) and right after the call to new Error. We should add our +new expected event between these two: + +[source, text] +.... +, "before AllTargetJoinPoints call(java.lang.Error(String))" +, "before AllTargetJoinPoints throw(throw)" // added for new throw join point +, "before AllTargetJoinPoints handler(catch(Error))" +.... + +Run the test suite again to see that this test now passes. + +==== Step 3. Extend test coverage to after advice + +There is a lot we should do now to extend test coverage for this new +kind of join point. For the purpose of this tutorial, we're just going +to make sure that the new join point kind is compatible with all 5 kinds +of advice. Let's extend our current simple Throws test to check for +before and the three kinds of after advice: + +[source, java] +.... +import org.aspectj.testing.Tester; + +public class Throws { + public static void main(String[] args) { + try { + willThrow(true); + Tester.checkFailed("should have thrown exception"); + } catch (RuntimeException re) { + Tester.checkEqual("expected exception", re.getMessage()); + } + Tester.checkEvents(new String[] + { "before throw", "after throwing throw", "after throw" }); + } + + static void willThrow(boolean shouldThrow) { + int x; + if (shouldThrow) throw new RuntimeException("expected exception"); + else x = 42; + System.out.println("x = " + x); + } +} + +aspect A { + before(Throwable t): throw() && args(t) { + Tester.event("before throw"); + Tester.checkEqual(thisJoinPoint.getSignature().toShortString(), "throw"); + Tester.checkEqual(t.getMessage(), "expected exception"); + } + + after() returning: throw() { + Tester.checkFailed("shouldn't ever return normally from a throw"); + } + + after() throwing(RuntimeException re): throw() { + Tester.event("after throwing throw"); + Tester.checkEqual(re.getMessage(), "expected exception"); + } + + after(): throw() { + Tester.event("after throw"); + } +} +.... + +Run this test to confirm that it still passes. This is a very nice +property of the orthogonality of the implementation of join points and +advice. We never had to do any implementation work to make our new join +point kind work for before and all three kinds of after advice. + +==== Step 4. Look at around advice on throw join points + +Let's create a new test case to see how this new join point interacts +with around advice. + +[source, java] +.... +import org.aspectj.testing.Tester; + +public class AroundThrows { + public static void main(String[] args) { + try { + willThrow(true); + Tester.checkFailed("should have thrown exception"); + } catch (RuntimeException re) { + Tester.checkEqual("expected exception", re.getMessage()); + } + } + + static void willThrow(boolean shouldThrow) { + int x; + if (!shouldThrow) x = 42; + else throw new RuntimeException("expected exception"); + System.out.println("x = " + x); + } +} + +aspect A { + void around(): throw() { + System.out.println("about to throw something"); + proceed(); + } +} +.... + +When we run this test case we get a very unpleasant result: + +[source, text] +.... +------------ FAIL: simple throw join point with around() +... +[ 1] --- thrown +java.lang.VerifyError: (class: AroundThrows, method: willThrow signature: (Z)V) Accessing value from uninitialized register 1 +... +FAIL Suite.Spec(c:\aspectj\eclipse\tests) 3 tests (1 failed, 2 passed) 3 seconds +.... + +A VerifyError at runtime is the second worst kind of bug the AspectJ +compiler can produce. The worst is silently behaving incorrectly. + +Unfortunately, this VerifyError is either impossible or very hard to +fix. Think about what would happen if the around advice body didn't call +proceed. In this case the local variable x would in fact be +uninitialized. There is another serious language design question here, +and for a real implementation this would once again be the time to start +a discussion on the aspectj-dev mailing list to reach consensus on the +best design. For the purpose of this tutorial we're once again going to +make the language design choice that is easiest to implement and press +on. + +==== Step 5. Prohibit around advice on this new join point kind + +The easiest solution to implement is to prohibit around advice on throw +join points. There are already a number of these kinds of rules +implemented in the org.aspectj.weaver.Shadow.match(Shadow, World) +method. We can add our new rule at the beginning of the if(kind == +AdviceKind.Around) block: + +[source, java] +.... +} else if (kind == AdviceKind.Around) { + if (shadow.getKind() == Shadow.Throw) { + world.showMessage(IMessage.ERROR, + "around on throw not supported (possibly compiler limitation)", + getSourceLocation(), shadow.getSourceLocation()); + return false; + } +.... + +Now if we rerun our test we'll see errors telling us that around is +prohibited on throw join points: + +[source, text] +.... +------------ FAIL: simple throw join point with around() +... +[ 0] [error 0]: error at C:\aspectj\eclipse\tests\design\pcds\AroundThrows.java:22 around on throw not supported (possibly compiler limitation) +[ 0] [error 1]: error at C:\aspectj\eclipse\tests\design\pcds\AroundThrows.java:16 around on throw not supported (possibly compiler limitation) +... +FAIL Suite.Spec(c:\aspectj\eclipse\tests) 3 tests (1 failed, 2 passed) 3 seconds +.... + +To finish this test case up we need to modify the specification to be +looking for these errors as the correct behavior. This will produce the +following specification: + +[source, xml] +.... + + + + + + +.... + +Run myTests.xml one last time to see both tests passing. + +==== Step 6. Final preparations for a commit or patch + +You probably want to stop here for the purposes of this tutorial. We've +pointed out several language design decisions that would need to be +resolved before actually adding a throw join point to AspectJ. Some of +those might involve a large amount of additional implementation work. If +this was actually going into the tree, it would also be important to add +several more test cases exploring the space of what can be done with +throw. + +Assuming those issues were resolved and you are ready to commit this new +feature to the tree there are three steps left to follow: + +. Move our new test specifications from myTests.xml to the end of +ajcTests.xml +. Rerun ajcTests.xml and the unit tests to ensure everything's okay. +. Update from the repository to get any changes from other committers +since you started work on this new feature. +. Rerun ajcTests.xml and the unit tests to make sure nothing broke as a +result of the update. +. Finally you can commit these changes to the AspectJ tree. diff --git a/docs/developer/compiler-weaver/advice-dec.gif b/docs/developer/compiler-weaver/advice-dec.gif deleted file mode 100644 index 43f540a4f..000000000 Binary files a/docs/developer/compiler-weaver/advice-dec.gif and /dev/null differ diff --git a/docs/developer/compiler-weaver/ajdt-uml.gif b/docs/developer/compiler-weaver/ajdt-uml.gif deleted file mode 100644 index 3a6ff797d..000000000 Binary files a/docs/developer/compiler-weaver/ajdt-uml.gif and /dev/null differ diff --git a/docs/developer/compiler-weaver/dev-guide-diagrams.vsd b/docs/developer/compiler-weaver/dev-guide-diagrams.vsd deleted file mode 100644 index 5636872df..000000000 Binary files a/docs/developer/compiler-weaver/dev-guide-diagrams.vsd and /dev/null differ diff --git a/docs/developer/compiler-weaver/dev-guide-uml.vsd b/docs/developer/compiler-weaver/dev-guide-uml.vsd deleted file mode 100644 index d3fa03dea..000000000 Binary files a/docs/developer/compiler-weaver/dev-guide-uml.vsd and /dev/null differ diff --git a/docs/developer/compiler-weaver/index.adoc b/docs/developer/compiler-weaver/index.adoc deleted file mode 100644 index 817656e35..000000000 --- a/docs/developer/compiler-weaver/index.adoc +++ /dev/null @@ -1,1192 +0,0 @@ -== Guide for Developers of the AspectJ Compiler and Weaver - -_Latest (non-license) content update: 2004-02-20 by jhugunin_ - -This document is written for developers who want to understand the -implementation of AspectJ. It provides a top-down picture of the -compiler and weaver implementations. This high-level picture should make -it easier to read and understand the source code for AspectJ. - -The AspectJ compiler/weaver (ajc) is composed of three primary modules. - -* *org.aspectj.ajdt.core* - this is the compiler front-end and extends -the eclipse Java compiler from *org.eclipse.jdt.core*. Because of the -dependencies on parts of eclipse this generates a large ~6MB jar. -* *weaver* - this provides the bytecode weaving functionality. It has -very few external dependencies to minimize the size required for -deployment of load-time weavers. Currently the build process doesn't -produce a separate jar for just the weaver, but that will have to change -for AspectJ-1.2. -* *runtime* - these are the classes that are used by generated code at -runtime and must be redistributed with any system built using AspectJ. -This module has no external dependencies and produces a tiny ~30KB jar. - -image:overview.gif[image] - -The AspectJ compiler accepts both AspectJ bytecode and source code and -produces pure Java bytecode as a result. Internally it has two stages. -The front-end (org.aspectj.ajdt.core) compiles both AspectJ and pure -Java source code into pure Java bytecode annotated with additional -attributes representing any non-java forms such as advice and pointcut -declarations. The back-end of the AspectJ compiler (weaver) implements -the transformations encoded in these attributes to produce woven class -files. The back-end can be run stand-alone to weave pre-compiled aspects -into pre-compiled .jar files. In addition, the back-end exposes a -weaving API which can be used to implement ClassLoaders that will weave -advice into classes dynamically as they are loaded by the virtual -machine. - -=== Compiler front-end (org.aspectj.ajdt.core) - -The front-end of the AspectJ compiler is implemented as an extension of -the Java compiler from eclipse.org. The source-file portion of the -AspectJ compiler is made complicated by inter-type declarations, declare -parents, declare soft, and privileged aspects. All of these constructs -require changes to the underlying compiler to modify Java’s name-binding -and static checking behavior. - -As the compiler extends the jdt.core compiler, the package structure of -this module mimics that of the jdt.core module. The design works hard to -minimize the set of changes required to org.eclipse.jdt.core because a -fun 3-way merge is required each time we want to move to a new -underlying version of this code. The ultimate goal is to contribute all -of our changes to jdt.core back into the main development branch some -day. - -The basic structure of a compile is very simple: - -. Perform a shallow parse on all source files -. Pass these compilation units through AjLookupManager to do type -binding and some AspectJ augmentation -. For each source file do a deep parse, annotation/analysis, and then -code generation - -==== Top-level parse tree - -Let's trace the following example program through the compiler. - -[source, java] -.... -package example.parse.tree; - -import org.aspectj.lang.*; - -public class Main { - public static void main(String[] args) { - new Main().doit(); - } - - private void doit() { - System.out.println("hello"); - } -} - -aspect A { - pointcut entries(Main o): execution(void doit()) && this(o); - before(Main o): entries(o) { - o.counter++; - System.out.println("entering: " + thisJoinPoint); - } - - private int Main.counter = 0; -} -.... - -When parsed, this program will produce the following tree. - -image:top-tree.gif[image] - -==== PointcutDeclaration processing - -Let's look more closely at the pointcut declaration: - -[source, java] -.... -pointcut entries(Main o): execution(void doit()) && this(o); -.... - -image:pointcut-dec.gif[image] - -The pointcut declaration is implemented as a subtype of a method -declaration. The actual pointcut is parsed by the weaver module. This -parsing happens as part of the shallow parse phase. This is because this -information might be needed to implement a declare soft. - -==== AdviceDeclaration processing - -Next we look at the processing for an advice declaration: - -[source, java] -.... -before(Main o): entries(o) { - o.counter++; - System.out.println("entering: " + thisJoinPoint); -} -.... - -After parsing, the AdviceDeclaration.postParse method will be called to -make this a valid MethodDeclaration so that the standard eclipse code -for analyzing a method body can be applied to the advice. After -postParse, the selector is filled in and several additional arguments -are added for the special thisJoinPoint forms that could be used in the -body. - -image:advice-dec.gif[image] - -At this point the statements field which will hold the body of the -advice is still null. This field is not filled in until the second stage -of the compiler when full parsing is done on each source file as a -prelude to generating the classfile. - -==== Overview of the main classes in org.aspectj.ajdt.core - -The main classes in this module are shown in the following diagram: - -image:ajdt-uml.gif[image] - -=== Weaving back-end (weaver) - -This provides all of the weaving functionality. It has very few -dependencies to keep the code as small as possible for deployment in -load-time weavers - only asm, bridge and util which are each very small -modules with no further dependencies. This also depends on a patched -version of the bcel library from apache.org. The patches are only to fix -bcel bugs that can't be worked around in any other way. - -There are only four packages in this system. - -* org.aspectj.weaver - general classes that can be used by any weaver -implementation -* org.aspectj.weaver.patterns - patterns to represent pointcut -designators and related matching constructs -* org.aspectj.weaver.ast - a very small library to represent simple -expressions without any bcel dependencies -* org.aspectj.weaver.bcel - the concrete implementation of shadows and -the weaver using the bcel library from apache.org - -The back-end of the AspectJ compiler instruments the code of the system -by inserting calls to the precompiled advice methods. It does this by -considering that certain principled places in bytecode represent -possible join points; these are the “static shadow” of those join -points. For each such static shadow, it checks each piece of advice in -the system and determines if the advice's pointcut could match that -static shadow. If it could match, it inserts a call to the advice’s -implementation method guarded by any dynamic testing needed to ensure -the match. - -=== Runtime support library (runtime) - -This library provides classes that are used by the generated code at -runtime. These are the only classes that must be redistributed with a -system built using AspectJ. Because these classes are redistributed -this library must always be kept as small as possible. It is also -important to worry about binary compatibility when making changes to -this library. There are two packages that are considered public and may -be used by AspectJ programs. - -* org.aspectj.lang -* org.apectj.lang.reflect - -There are also several packages all under the header org.aspectj.runtime -that are considered private to the implementation and may only be used -by code generated by the AspectJ compiler. - -=== Mappings from AspectJ language to implementation - -[cols=",,",] -|=== -| |org.aspectj.ajdt.internal.compiler |weaver - org.aspectj.weaver. - -|aspect |ast.AspectDeclaration |CrosscuttingMembers - -|advice |ast.AdviceDeclaration |Advice + bcel.BcelShadowMunger - -|pointcut declaration |ast.PointcutDeclaration -|ResolvedPointcutDefinition - -|declare error/warning |ast.DeclareDeclaration |Checker + -patterns.DeclareErrorOrWarning - -|declare soft |ast.DeclareDeclaration + problem.AjProblemReporter -|Advice (w/ kind = Softener) + patterns.DeclareSoft - -|declare parents |ast.DeclareDeclaration + lookup.AjLookupEnvironment -|patterns.DeclareParents + NewParentTypeMunger - -|inter-type decls |ast.InterType*Declaration + lookup.InterType*Binding -+ lookup.AjLookupEnvironment |New*TypeMunger + bcel.BcelTypeMunger - -|if pcd |ast.IfPseudoToken + ast.IfMethodDeclaration -|patterns.IfPointcut - -|pcd |ast.PointcutDesignator |patterns.Pointcut hierarchy -|=== - -== Tutorial: implementing a throw join point - -This tutorial will walk step-by-step through the process of adding a new -join point to AspectJ for the moment when an exception is thrown. In -Java source code, the shadow of this point is a throw statement. In Java -bytecode, the shadow is the athrow instruction. - -This tutorial is recommended to anyone who wants to get a better feel -for how the implementation of AspectJ really works. Even if you're just -working on a bug fix or minor enhancement, the process of working with -the AspectJ implementation will be similar to that described below. The -size of your actual code changes will likely be smaller, but you are -likely to need to be familiar with all of the pieces of the -implementation described below. - -=== Part 1: Adding the join point and corresponding pcd - -The first part of this tutorial will implement the main features of the -throw join point. We will create a new join point shadow corresponding -to the athrow instruction and also create a new pointcut designator -(pcd) for matching it. - -==== Step 1. Synchronize with repository and run the existing test suite - -Do a Team->Synchronize With Repository and make sure that your tree is -completely in sync with the existing repository. Make sure to address -any differences before moving on. - -Run the existing test suite. I currently do this in four steps: - -* weaver/testsrc/BcWeaverModuleTests.java -* org.aspectj.ajdt.core/testsrc/EajcModuleTests.java -* ajde/testsrc/AjdeModuleTests.java -* Harness on ajctests.xml -- at least under 1.4, preferably under both -1.3 and 1.4. - -There should be no failures when you run these tests. If there are -failures, resolve them with the AspectJ developers before moving on. - -==== Step 2. Write a proto test case - -{empty}a. Create a new file in tests/design/pcds/Throw.java - -[source, java] -.... -import org.aspectj.testing.Tester; - -public class Throws { - public static void main(String[] args) { - try { - willThrow(); - Tester.checkFailed("should have thrown exception"); - } catch (RuntimeException re) { - Tester.checkEqual("expected exception", re.getMessage()); - } - } - - static void willThrow() { - throw new RuntimeException("expected exception"); - } -} - -aspect A { - before(): withincode(void willThrow()) { - System.out.println("about to execute: " + thisJoinPoint); - } -} -.... - -{empty}b. Create a temporary test harness file to run just this test in -myTests.xml - -[source, xml] -.... - - - - - - - -.... - -{empty}c. Run this test using the harness. You should see: - -[source, text] -.... -about to execute: execution(void Throws.willThrow()) -about to execute: call(java.lang.RuntimeException(String)) -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 2 seconds -.... - -==== Step 3. Implement the new join point shadow kind - -Modify runtime/org.aspectj.lang/JoinPoint.java to add a name for the -Throw shadow kind. - -[source, java] -.... -static String THROW = "throw"; -.... - -Modify weaver/org.aspectj.weaver/Shadow.java to add the Throw shadow -kind. This adds a static typesafe enum for the Throw Kind. The -constructor uses the name from the runtime API to ensure that these -names will always match. The '12' is used for serialization of this kind -to classfiles and is part of the binary API for aspectj. The final -'true' indicates that this joinpoint has its arguments on the stack. -This is because the throw bytecode in Java operates on a single argument -that is a Throwable which must be the top element on the stack. This -argument is removed from the stack by the bytecode. - -[source, java] -.... -public static final Kind Throw = new Kind(JoinPoint.THROW, 12, true); -.... - -We also modify the neverHasTarget method to include the Throw kind -because in Java there is no target for the throwing of an exception. - -[source, java] -.... -public boolean neverHasTarget() { - return this == ConstructorCall - || this == ExceptionHandler - || this == PreInitialization - || this == StaticInitialization - || this == Throw; -} -.... - -In the read method on Shadow.Kind, add another case to read in our new -Shadow.Kind. - -[source, java] -.... -case 12: return Throw; -.... - -==== Step 4. Create this new kind of joinpoint for the throw bytecode - -Modify weaver/org.aspectj.weaver.bcel/BcelClassWeaver.java to recognize -this new joinpoint kind. In the method - -[source, java] -.... -private void match( - LazyMethodGen mg, - InstructionHandle ih, - BcelShadow enclosingShadow, - List shadowAccumulator) -{ -.... - -Add a test for this instruction, i.e. - -[source, java] -.... -} else if (i == InstructionConstants.ATHROW) { - match(BcelShadow.makeThrow(world, mg, ih, enclosingShadow), - shadowAccumulator); -} -.... - -Then, modify BcelShadow.java to create this new kind of join point -shadow: - -[source, java] -.... -public static BcelShadow makeThrow( - BcelWorld world, - LazyMethodGen enclosingMethod, - InstructionHandle throwHandle, - BcelShadow enclosingShadow) -{ - final InstructionList body = enclosingMethod.getBody(); - TypeX throwType = TypeX.THROWABLE; //!!! not as precise as we'd like - TypeX inType = enclosingMethod.getEnclosingClass().getType(); - BcelShadow s = - new BcelShadow( - world, - Throw, - Member.makeThrowSignature(inType, throwType), - enclosingMethod, - enclosingShadow); - ShadowRange r = new ShadowRange(body); - r.associateWithShadow(s); - r.associateWithTargets( - Range.genStart(body, throwHandle), - Range.genEnd(body, throwHandle)); - retargetAllBranches(throwHandle, r.getStart()); - return s; -} -.... - -Finally modify weaver/org.aspectj.weaver/Member.java to generate the -needed signature - -[source, java] -.... -public static Member makeThrowSignature(TypeX inType, TypeX throwType) { - return new Member( - HANDLER, - inType, - Modifier.STATIC, - "throw", - "(" + throwType.getSignature() + ")V"); -} -.... - -Run the proto test again and you should see: - -[source, text] -.... -about to execute: execution(void Throws.willThrow()) -about to execute: call(java.lang.RuntimeException(String)) -about to execute: throw(catch(Throwable)) -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 3 seconds -.... - -That last line shows the 'throw(catch(Throwable))' join point. This is a -slightly confusing string form, but it is the first sign of our brand -new join point. The reason for the weird 'catch(Throwable)' part is that -we used Member.HANDLER for the kind of the signature of this join point. -That's clearly not correct. We'll fix that at the end of the lesson as -part of the clean-up. For now, let's go on with the interesting parts. - -==== Step 5. Extend our proto-test to use a pointcut designator for matching - -Add a second piece of before advice to the test aspect A: - -[source, java] -.... -before(): throw(Throwable) { - System.out.println("about to throw: " + thisJoinPoint); -} -.... - -When we run the test again we'll get a long error message from the -harness. The interesting part of the message is the following: - -[source, text] -.... -[ 0] [error 0]: error can't find referenced pointcut at C:\aspectj\eclipse\tests\design\pcds\Throws.java:23:0 -.... - -This error is not quite what you might have expected. You might have -hoped for a syntax error saying that there is not 'throw' pointcut -designator defined. Unfortunately, this is a weakness in the syntax of -AspectJ where primitive PCDs and named PCDs have the same syntax, so the -compiler can't tell the difference between a misspelled or non-existent -primitive PCD and a named PCD reference that is missing. This also has -some impact on extending the primitive PCDs because it will break -existing programs. In this case, when we add the throw PCD we will break -any existing programs that use throw as the name for a user-defined PCD. -Fortunately because throw is a Java keyword this particular change is -very safe. - -==== Step 6. Extend the PCD parser to handle this new primitive PCD - -Modify the parseSinglePointcut method in -weaver/org.aspectj.weaver.patterns/PatternParser.java to add one more -else if clause for the throw pcd: - -[source, java] -.... -} else if (kind.equals("throw")) { - parseIdentifier(); eat("("); - TypePattern typePat = parseTypePattern(); - eat(")"); - return new KindedPointcut(Shadow.Throw, - new SignaturePattern(Member.HANDLER, ModifiersPattern.ANY, - TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, - new TypePatternList(new TypePattern[] {typePat}), - ThrowsPattern.ANY)); -.... - -Modify the matches method in -weaver/org.aspectj.weaver.patterns/SignaturePattern.java to add: - -[source, java] -.... -if (kind == Member.HANDLER) { - return parameterTypes.matches(world.resolve(sig.getParameterTypes()), - TypePattern.STATIC).alwaysTrue(); -} -.... - -Run the proto test again and you should see: - -[source, text] -.... -about to execute: execution(void Throws.willThrow()) -about to execute: call(java.lang.RuntimeException(String)) -about to execute: throw(catch(Throwable)) -about to throw: throw(catch(Throwable)) -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds -.... - -Make sure that you see the 'about to throw' printed before moving on. -This shows that the throw PCD is now successfully matching the throw -join point shadow we added earlier. - -==== Step 7. Check that we're properly providing the single thrown argument (and clean-up the test) - -Now that we have a valid pcd for this advice, we can simplify our test -case. Modify our test aspect A to be the following. In addition to -removing the overly generic withincode pcd, this change also prints the -actual object that is about to be thrown: - -[source, java] -.... -aspect A { - before(Throwable t): throw(*) && args(t) { - System.out.println("about to throw: '" + t+ "' at " + thisJoinPoint); - } -} -.... - -When we run the test again we should see the output below: - -[source, text] -.... -about to throw: 'java.lang.RuntimeException: expected exception' at throw(catch(Throwable)) -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds -.... - -Congratulations! You've just implemented the throw join point and PCD. -This code isn't yet ready to be checked into any repository. It still -has some rough edges that need to be smoothed. However, you've now added -a new join point to the AspectJ language and a corresponding PCD to -match it. This is a good time to take a break before moving on to part -two. - -=== Part 2: Getting the signature of this new join point right - -We know that throw(catch(Throwable)) is not the right thing to be -printing for the signature at this join point. What is the correct -signature? At the beginning of the tutorial, we explained that the -preferred design for the pcd was to have -throw(StaticTypeOfExceptionThrown). In step 4, we set the type of the -exception thrown to be 'Throwable'. Can we set this to be more accurate? -Looking at the source code, it seems easy to identify the static type of -the exception that is thrown: - -[source, java] -.... -throw new RuntimeException("expected exception"); -.... - -In the source code to a Java program there is a well-defined static type -for the exception that is thrown. This static type is used for various -stages of flow analysis to make sure that checked exceptions are always -correctly handled or declared. The ThrowStatement class in our own -compiler has a special field for exceptionType that stores the static -type of the exception thrown. Unfortunately, this static type is much -harder to recover from the corresponding bytecode. In this case we would -need to do flow analysis to figure out what the static type is for the -object on the top of the stack when the athrow instruction executes. -This analysis can certainly be done. In fact this analysis is a small -part of what every JVM must do to verify the type safety of a loaded -classfile. - -However, the current AspectJ weaver doesn't do any of this analysis. -There are many good reasons to extend it in this direction in order to -optimize the code produced by the weaver. If we were really implementing -this feature, this would be the time for a long discussion on the -aspectj-dev list to decide if this was the right time to extend the -weaver with the code flow analysis needed to support a static type for -the throw join point. For the purposes of this tutorial, we're going to -assume that it isn't the right time to do this (implementing flow -analysis for bytecodes would add another 50 pages to this tutorial). -Instead we're going to change the definition of the throw join point to -state that its argument always has a static type of Throwable. We still -allow dynamic matching in args to select more specific types. In -general, good AspectJ code should use this dynamic matching anyway to -correspond to good OO designs. - -==== Step 1. Change the signature of the throw pcd - -Since we aren't going to recover the static type of the exception -thrown, we need to fix the parser for the throw pcd to remove this -information. We'll fix the PatternParser code that we added in step 1.6 -to read as follows: - -[source, java] -.... -} else if (kind.equals("throw")) { - parseIdentifier(); eat("("); - eat(")"); - return new KindedPointcut(Shadow.Throw, - new SignaturePattern(Member.THROW, ModifiersPattern.ANY, - TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, - TypePatternList.ANY, - ThrowsPattern.ANY)); -.... - -Notice that this code also starts to fix the member kind to be -Member.THROW instead of the bogus Member.HANDLER that we were using -before. To make this work we have a set of things to do. First, let's -create this new kind in org.aspectj.weaver.Member. Find where the -HANDLER kind is defined there, and add a corresponding throw kind: - -[source, java] -.... -public static final Kind THROW = new Kind("THROW", 8); -.... - -We also need to fix the serialization kind in -Member.Kind.read(DataInputStream) just above this constant list to add a -case for this new kind: - -[source, java] -.... -case 8: return THROW; -.... - -Still in this file, we also need to fix Member.makeThrowSignature to use -this new kind: - -[source, java] -.... -public static Member makeThrowSignature(TypeX inType, TypeX throwType) { - return new ResolvedMember( - THROW, - inType, - Modifier.STATIC, - "throw", - "(" + throwType.getSignature() + ")V"); -} -.... - -If you run the test now you'll get an error from the parser reminding us -that the throw pcd now doesn't accept a type pattern: - -[source, text] -.... ------------- FAIL: simple throw join point() -... -C:\aspectj\eclipse\tests\design\pcds\Throws.java:19:0 Syntax error on token "*", ")" expected - -FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds -.... - -This is an easy fix to the test case as we modify our pcd for the new -syntax in the aspect A in our Throws.java test code: - -[source, java] -.... -before(Throwable t): throw() && args(t) { -.... - -Now when we run the test case it looks like everything's fixed and we're -passing: - -[source, text] -.... -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 2 seconds -.... - -==== Part 2. Make a real test case - -The pass result from running our test should worry you. Unlike previous -runs, this test run doesn't show the output from our System.out.println -in the before advice. So, it's clear this advice is not running. The -problem is that even though the advice is not running, the test case is -passing. We need to make this a real test case to fix this. We'll do -that by adding code that notes when the advice runs and then checks for -this event. This code uses the Tester.event and Tester.checkEvent -methods: - -[source, java] -.... -import org.aspectj.testing.Tester; - -public class Throws { - public static void main(String[] args) { - try { - willThrow(); - Tester.checkFailed("should have thrown exception"); - } catch (RuntimeException re) { - Tester.checkEqual("expected exception", re.getMessage()); - } - Tester.checkEvents(new String[] { "before throw" }); - } - - static void willThrow() { - throw new RuntimeException("expected exception"); - } -} - -aspect A { - before(Throwable t): throw() && args(t) { - Tester.event("before throw"); - //System.out.println("about to throw: '" + t+ "' at " + thisJoinPoint); - } -} -.... - -Now when we run our test case it will fail. This failure is good because -we're not matching the throw join point anymore. - -[source, text] -.... ------------- FAIL: simple throw join point() -... -[ 1] [fail 0]: fail [ expected event "before throw" not found] - -FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds -.... - -==== Step 3. Fix signature matching again - -In org.aspectj.weaver.patterns.SignaturePattern.matches, we need to -handle throw signature matching the same way we handle advice signature -matching. Both of these pcds match solely on the kind of join point and -use combinations with other pcds to narrow their matches. So, find the -line for kind == Member.ADVICE and add the same line below it for -Member.THROW. - -[source, java] -.... -if (kind == Member.ADVICE) return true; -if (kind == Member.THROW) return true; -.... - -This change will make our test case pass again. Run it to be sure. - -There's an interesting tension between a good automated test and a good -test for development. Our new test case now correctly includes an -automated test to let us know when we are and are not matching the new -throw join point. However, without the println the test doesn't feel as -satisfactory to me to run during development. I often like to turn this -kind of printing back on the see what's happening. If you uncomment to -System.out.println in the test aspect A and rerun the test, you won't be -very happy with the results: - -[source, text] -.... ------------- FAIL: simple throw join point() -... -unimplemented -java.lang.RuntimeException: unimplemented - at org.aspectj.weaver.Member.getSignatureString(Member.java:596) -... - -FAIL Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 failed) 1 seconds -.... - -It looks like there's more work to do to add the new member kind for -Member.THROW. This problem only shows up when we try to print -thisJoinPoint. It's showing that we haven't updated the reflection API -to understand this new signature kind. - -==== Step 4. Extend org.aspectj.lang.reflect to understand throw signatures - -We need to add a couple of classes to the reflection API to implement -the throw signature. Because we decided at the beginning of this section -to not include the static type of the exception thrown in the throw -signature, these classes are extremely simple. Nevertheless, we have to -build them. Notice that when we add new source files to the system we -need to include the standard eclipse EPL license header. - -[source, java] -.... -/* ******************************************************************* - * Copyright (c) 2006 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v 2.0 - * which accompanies this distribution and is available at - * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt - * - * Contributors: - * Jim Hugunin initial implementation - * ******************************************************************/ - -package org.aspectj.lang.reflect; -import org.aspectj.lang.Signature; - -public interface ThrowSignature extends Signature { } -.... - -[source, java] -.... -/* ******************************************************************* - * Copyright (c) 2006 Contributors. - * All rights reserved. - * This program and the accompanying materials are made available - * under the terms of the Eclipse Public License v 2.0 - * which accompanies this distribution and is available at - * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt - * - * Contributors: - * Jim Hugunin initial implementation - * ******************************************************************/ - -package org.aspectj.runtime.reflect; -import org.aspectj.lang.reflect.ThrowSignature; - -class ThrowSignatureImpl extends SignatureImpl implements ThrowSignature { - - ThrowSignatureImpl(Class declaringType) { - super(0, "throw", declaringType); - } - - ThrowSignatureImpl(String stringRep) { - super(stringRep); - } - - String toString(StringMaker sm) { - return "throw"; - } -} -.... - -To finish up our work in the runtime module, we need to extend -org.aspectj.runtime.reflect.Factory to add a factory method for this new -signature kind: - -[source, java] -.... -public ThrowSignature makeThrowSig(String stringRep) { - ThrowSignatureImpl ret = new ThrowSignatureImpl(stringRep); - ret.setLookupClassLoader(lookupClassLoader); - return ret; -} -.... - -We're not done yet. We still need to fix up the -org.aspectj.weaver.Member class to use these new methods and types and -fix the unimplemented exception that started us down this road in the -first place. First let's add a method to create a string for the throw -signature. This is a very simple method copied from the other -create*SignatureString methods. - -[source, java] -.... -private String getThrowSignatureString(World world) { - StringBuffer buf = new StringBuffer(); - buf.append('-'); // no modifiers - buf.append('-'); // no name - buf.append(makeString(getDeclaringType())); - buf.append('-'); - return buf.toString(); -} -.... - -Now we need to modify three methods to add cases for the new -Member.THROW kind. First, Member.getSignatureMakerName add: - -[source, java] -.... -} else if (kind == THROW) { - return "makeThrowSig"; -.... - -Next, to Member.getSignatureType add: - -[source, java] -.... -} else if (kind == THROW) { - return "org.aspectj.lang.reflect.ThrowSignature"; -.... - -Finally, to Member.getSignatureString add: - -[source, java] -.... -} else if (kind == THROW) { - return getThrowSignatureString(world); -.... - -With all of these changes in place we should have working code for -thisJoinPoint reflection using our new join point and signature kinds. -Rerun the test to confirm: - -[source, text] -.... -about to throw: 'java.lang.RuntimeException: expected exception' at throw(throw) -PASS Suite.Spec(c:\aspectj\eclipse\tests) 1 tests (1 passed) 1 seconds -.... - -==== Step 5. Extend the test for automated coverage of reflection - -Modify the before advice to include at least minimal checks of the new -reflective information: - -[source, java] -.... -before(Throwable t): throw() && args(t) { - Tester.event("before throw"); - Tester.checkEqual(thisJoinPoint.getSignature().toShortString(), "throw"); - Tester.checkEqual(t.getMessage(), "expected exception"); -} -.... - -As usual, you should rerun the tests and make sure they pass. - -With these changes to the reflection code, it looks like we have a -working version of the throw join point and there are no obvious pieces -that we've skipped. Take a break before proceeding to the final phase of -tests. - -=== Part 3: More serious testing - -Now it's time to get a decent testing story. The test work that we will -do here is probably too little for adding a new join point to the -aspectj language; however, it should at least give you a sense of what's -involved. - -==== Step 1. Run the test suite again - -Rerun the tests you ran at the beginning of part 1. Any failures that -occur should be resolved at this point. At the time of writing this -tutorial, I found 31 failures in the BcWeaverModuleTests. These failures -are for all of the test cases that check the exact set of shadows -produces by a given program. These test cases need to be updated based -on the new join point we're adding. These particular test cases will -probably be removed from the AspectJ test suite very soon because -they've shown themselves to be very fragile over time and they often -break for changes that are not introducing new bugs. However, you should -be aware of this kind of failure because you may find it in other unit -tests. - -You should expect to see at least one other test case fail when you run -ajcTests.xml. Here's the failure message: - -[source, text] -.... ------------- FAIL: validate (enclosing) join point and source locations() -... -[ 1] [fail 0]: fail [ unexpected event "before AllTargetJoinPoints throw(throw)" found] -.... - -Most of this message can be ignored. To find out what went wrong you -should look for messages that have "fail" in them. The last line tells -you what happened. There was an unexpected event, "before -AllTargetJoinPoints throw(catch(Throwable))". This is the signature for -one of the new throw join points that we added in part 1. How could an -existing test case match this new join point? The failing test case uses -'within(TargetClass)' to collect information about ALL join points that -are lexically within a given class. Whenever we add a new kind of join -point to the language we will extend the set of points matched by pcds -like within. This means that these changes need to be very prominently -noted in the release notes for any AspectJ release. Since we're not -writing documentation in this tutorial, we will move on an fix the test -case. - -==== Step 2. Fix the failing test case - -Now we need to fix this failing test case. The first step is to copy the -test specification into our local myTests.xml file. The easiest way to -do this is to copy the title of the failing test from the output buffer, -then open ajcTests.xml and use find to search for this title. Then copy -the xml spec for this one test into myTests.xml. Finally, run -myTests.xml to make sure you got the failing test. You should see the -same failure as before in step 1, but you should see it a lot faster -because we're only running 2 tests. - -To fix the test we need to find the source code. If you look at the test -specification, you can see that the source file is the new directory -with the name NegativeSourceLocation.java. Looking at the bottom of this -file, we see a large list of expected events. These are the join points -that we expect to see. If we look back up in TargetClass, we can see -that the only occurence of throw is just before the handler for -catch(Error) and right after the call to new Error. We should add our -new expected event between these two: - -[source, text] -.... -, "before AllTargetJoinPoints call(java.lang.Error(String))" -, "before AllTargetJoinPoints throw(throw)" // added for new throw join point -, "before AllTargetJoinPoints handler(catch(Error))" -.... - -Run the test suite again to see that this test now passes. - -==== Step 3. Extend test coverage to after advice - -There is a lot we should do now to extend test coverage for this new -kind of join point. For the purpose of this tutorial, we're just going -to make sure that the new join point kind is compatible with all 5 kinds -of advice. Let's extend our current simple Throws test to check for -before and the three kinds of after advice: - -[source, java] -.... -import org.aspectj.testing.Tester; - -public class Throws { - public static void main(String[] args) { - try { - willThrow(true); - Tester.checkFailed("should have thrown exception"); - } catch (RuntimeException re) { - Tester.checkEqual("expected exception", re.getMessage()); - } - Tester.checkEvents(new String[] - { "before throw", "after throwing throw", "after throw" }); - } - - static void willThrow(boolean shouldThrow) { - int x; - if (shouldThrow) throw new RuntimeException("expected exception"); - else x = 42; - System.out.println("x = " + x); - } -} - -aspect A { - before(Throwable t): throw() && args(t) { - Tester.event("before throw"); - Tester.checkEqual(thisJoinPoint.getSignature().toShortString(), "throw"); - Tester.checkEqual(t.getMessage(), "expected exception"); - } - - after() returning: throw() { - Tester.checkFailed("shouldn't ever return normally from a throw"); - } - - after() throwing(RuntimeException re): throw() { - Tester.event("after throwing throw"); - Tester.checkEqual(re.getMessage(), "expected exception"); - } - - after(): throw() { - Tester.event("after throw"); - } -} -.... - -Run this test to confirm that it still passes. This is a very nice -property of the orthogonality of the implementation of join points and -advice. We never had to do any implementation work to make our new join -point kind work for before and all three kinds of after advice. - -==== Step 4. Look at around advice on throw join points - -Let's create a new test case to see how this new join point interacts -with around advice. - -[source, java] -.... -import org.aspectj.testing.Tester; - -public class AroundThrows { - public static void main(String[] args) { - try { - willThrow(true); - Tester.checkFailed("should have thrown exception"); - } catch (RuntimeException re) { - Tester.checkEqual("expected exception", re.getMessage()); - } - } - - static void willThrow(boolean shouldThrow) { - int x; - if (!shouldThrow) x = 42; - else throw new RuntimeException("expected exception"); - System.out.println("x = " + x); - } -} - -aspect A { - void around(): throw() { - System.out.println("about to throw something"); - proceed(); - } -} -.... - -When we run this test case we get a very unpleasant result: - -[source, text] -.... ------------- FAIL: simple throw join point with around() -... -[ 1] --- thrown -java.lang.VerifyError: (class: AroundThrows, method: willThrow signature: (Z)V) Accessing value from uninitialized register 1 -... -FAIL Suite.Spec(c:\aspectj\eclipse\tests) 3 tests (1 failed, 2 passed) 3 seconds -.... - -A VerifyError at runtime is the second worst kind of bug the AspectJ -compiler can produce. The worst is silently behaving incorrectly. - -Unfortunately, this VerifyError is either impossible or very hard to -fix. Think about what would happen if the around advice body didn't call -proceed. In this case the local variable x would in fact be -uninitialized. There is another serious language design question here, -and for a real implementation this would once again be the time to start -a discussion on the aspectj-dev mailing list to reach consensus on the -best design. For the purpose of this tutorial we're once again going to -make the language design choice that is easiest to implement and press -on. - -==== Step 5. Prohibit around advice on this new join point kind - -The easiest solution to implement is to prohibit around advice on throw -join points. There are already a number of these kinds of rules -implemented in the org.aspectj.weaver.Shadow.match(Shadow, World) -method. We can add our new rule at the beginning of the if(kind == -AdviceKind.Around) block: - -[source, java] -.... -} else if (kind == AdviceKind.Around) { - if (shadow.getKind() == Shadow.Throw) { - world.showMessage(IMessage.ERROR, - "around on throw not supported (possibly compiler limitation)", - getSourceLocation(), shadow.getSourceLocation()); - return false; - } -.... - -Now if we rerun our test we'll see errors telling us that around is -prohibited on throw join points: - -[source, text] -.... ------------- FAIL: simple throw join point with around() -... -[ 0] [error 0]: error at C:\aspectj\eclipse\tests\design\pcds\AroundThrows.java:22 around on throw not supported (possibly compiler limitation) -[ 0] [error 1]: error at C:\aspectj\eclipse\tests\design\pcds\AroundThrows.java:16 around on throw not supported (possibly compiler limitation) -... -FAIL Suite.Spec(c:\aspectj\eclipse\tests) 3 tests (1 failed, 2 passed) 3 seconds -.... - -To finish this test case up we need to modify the specification to be -looking for these errors as the correct behavior. This will produce the -following specification: - -[source, xml] -.... - - - - - - -.... - -Run myTests.xml one last time to see both tests passing. - -==== Step 6. Final preparations for a commit or patch - -You probably want to stop here for the purposes of this tutorial. We've -pointed out several language design decisions that would need to be -resolved before actually adding a throw join point to AspectJ. Some of -those might involve a large amount of additional implementation work. If -this was actually going into the tree, it would also be important to add -several more test cases exploring the space of what can be done with -throw. - -Assuming those issues were resolved and you are ready to commit this new -feature to the tree there are three steps left to follow: - -. Move our new test specifications from myTests.xml to the end of -ajcTests.xml -. Rerun ajcTests.xml and the unit tests to ensure everything's okay. -. Update from the repository to get any changes from other committers -since you started work on this new feature. -. Rerun ajcTests.xml and the unit tests to make sure nothing broke as a -result of the update. -. Finally you can commit these changes to the AspectJ tree. diff --git a/docs/developer/compiler-weaver/overview.gif b/docs/developer/compiler-weaver/overview.gif deleted file mode 100644 index b32f11b15..000000000 Binary files a/docs/developer/compiler-weaver/overview.gif and /dev/null differ diff --git a/docs/developer/compiler-weaver/pointcut-dec.gif b/docs/developer/compiler-weaver/pointcut-dec.gif deleted file mode 100644 index fed640d36..000000000 Binary files a/docs/developer/compiler-weaver/pointcut-dec.gif and /dev/null differ diff --git a/docs/developer/compiler-weaver/top-tree.gif b/docs/developer/compiler-weaver/top-tree.gif deleted file mode 100644 index d8e2b26ff..000000000 Binary files a/docs/developer/compiler-weaver/top-tree.gif and /dev/null differ diff --git a/docs/developer/design-overview.adoc b/docs/developer/design-overview.adoc index ee3bd755e..06d00d83a 100644 --- a/docs/developer/design-overview.adoc +++ b/docs/developer/design-overview.adoc @@ -23,7 +23,7 @@ longer and are more likely to introduce new 'bugs'." ATTENTION: Please do not remove blank lines in between 'include::' statements. Otherwise, section numbers in the table of contents (TOC) can be wrong and the first section of each document missing completely. //// -include::compiler-weaver/index.adoc[Guide for Developers of the AspectJ Compiler and Weaver] +include::compiler-weaver.adoc[Guide for Developers of the AspectJ Compiler and Weaver] include::modules.adoc[Module Structure] diff --git a/docs/developer/images/advice-dec.gif b/docs/developer/images/advice-dec.gif new file mode 100644 index 000000000..43f540a4f Binary files /dev/null and b/docs/developer/images/advice-dec.gif differ diff --git a/docs/developer/images/ajdt-uml.gif b/docs/developer/images/ajdt-uml.gif new file mode 100644 index 000000000..3a6ff797d Binary files /dev/null and b/docs/developer/images/ajdt-uml.gif differ diff --git a/docs/developer/images/dev-guide-diagrams.vsd b/docs/developer/images/dev-guide-diagrams.vsd new file mode 100644 index 000000000..5636872df Binary files /dev/null and b/docs/developer/images/dev-guide-diagrams.vsd differ diff --git a/docs/developer/images/dev-guide-uml.vsd b/docs/developer/images/dev-guide-uml.vsd new file mode 100644 index 000000000..d3fa03dea Binary files /dev/null and b/docs/developer/images/dev-guide-uml.vsd differ diff --git a/docs/developer/images/overview.gif b/docs/developer/images/overview.gif new file mode 100644 index 000000000..b32f11b15 Binary files /dev/null and b/docs/developer/images/overview.gif differ diff --git a/docs/developer/images/pointcut-dec.gif b/docs/developer/images/pointcut-dec.gif new file mode 100644 index 000000000..fed640d36 Binary files /dev/null and b/docs/developer/images/pointcut-dec.gif differ diff --git a/docs/developer/images/top-tree.gif b/docs/developer/images/top-tree.gif new file mode 100644 index 000000000..d8e2b26ff Binary files /dev/null and b/docs/developer/images/top-tree.gif differ diff --git a/docs/pom.xml b/docs/pom.xml index d0726a217..e6c256dd9 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -22,30 +22,6 @@ - - org.apache.maven.plugins - maven-resources-plugin - - - copy-images-compiler-weaver - generate-resources - - copy-resources - - - ${project.build.directory}/html/developer - - - ${project.basedir}/developer/compiler-weaver - - *.gif - - - - - - - org.asciidoctor asciidoctor-maven-plugin