--- /dev/null
+import java.util.Random;
+
+public class CalculatePI {
+
+ static Random r = new Random();
+ static double piApproximation = 1.0f;
+ static int repetitions = 500000;
+ static int iteration = 0;
+ static double inSquare = 0;
+ static double inCircle = 0;
+
+ public static void main(String[] args) {
+ for (iteration = 0;iteration<repetitions;iteration++) approximate();
+ piApproximation = (inCircle/inSquare)*4.0f;
+ System.out.println("After "+repetitions+" iterations, pi is estimated to be "+piApproximation);
+ }
+
+ public static void approximate() {
+ double x = r.nextDouble();
+ double y = r.nextDouble();
+ inSquare++;
+ if (x*x + y*y < 1) {inCircle++;}
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+\r
+\r
+public aspect Logger {\r
+ \r
+ after(): call(* approximate(..)) {\r
+ if (CalculatePI.iteration%10000==0) \r
+ System.out.println("Approximation is now:"+\r
+ (CalculatePI.inCircle/CalculatePI.inSquare)*4.0f);\r
+ }\r
+ \r
+}
\ No newline at end of file
--- /dev/null
+CalculatePI.java\r
+Logger.aj\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+CalculatePI.java\r
+Logger.aj\r
+-Xreweavable\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+SecondAspect.aj\r
+-Xreweavable\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+CalculatePI.java\r
+Logger.aj\r
+-Xreweavable:compress\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+Logger.aj\r
+-Xreweavable\r
+-verbose
\ No newline at end of file
--- /dev/null
+\r
+public aspect SecondAspect {\r
+\r
+ declare parents: Logger implements java.io.Serializable;\r
+}
\ No newline at end of file
--- /dev/null
+tjp/Demo.java\r
+tjp/GetInfo.java\r
+-Xreweavable\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+-Xreweavable\r
+-verbose\r
+-noExit
\ No newline at end of file
--- /dev/null
+\r
+public aspect ThirdAspect {\r
+\r
+ int CalculatePI.x;\r
+}
\ No newline at end of file
--- /dev/null
+
+/*
+
+Copyright (c) Xerox Corporation 1998-2002. All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted. Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+*/
+package tjp;
+
+public class Demo {
+ static Demo d;
+
+ public static void main(String[] args){
+ new Demo().go();
+ }
+
+ void go(){
+ d = new Demo();
+ d.foo(1,d);
+ System.out.println(d.bar(new Integer(3)));
+ }
+
+ void foo(int i, Object o){
+ System.out.println("Demo.foo(" + i + ", " + o + ")\n");
+ }
+
+ String bar (Integer j){
+ System.out.println("Demo.bar(" + j + ")\n");
+ return "Demo.bar(" + j + ")";
+ }
+}
--- /dev/null
+
+/*
+Copyright (c) Xerox Corporation 1998-2002. All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted. Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+*/
+
+package tjp;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.CodeSignature;
+
+aspect GetInfo {
+
+ static final void println(String s){ System.out.println(s); }
+
+ pointcut goCut(): cflow(this(Demo) && execution(void go()));
+
+ pointcut demoExecs(): within(Demo) && execution(* *(..));
+
+ Object around(): demoExecs() && !execution(* go()) && goCut() {
+ println("Intercepted message: " +
+ thisJoinPointStaticPart.getSignature().getName());
+ println("in class: " +
+ thisJoinPointStaticPart.getSignature().getDeclaringType().getName());
+ printParameters(thisJoinPoint);
+ println("Running original method: \n" );
+ Object result = proceed();
+ println(" result: " + result );
+ return result;
+ }
+
+ static private void printParameters(JoinPoint jp) {
+ println("Arguments: " );
+ Object[] args = jp.getArgs();
+ String[] names = ((CodeSignature)jp.getSignature()).getParameterNames();
+ Class[] types = ((CodeSignature)jp.getSignature()).getParameterTypes();
+ for (int i = 0; i < args.length; i++) {
+ println(" " + i + ". " + names[i] +
+ " : " + types[i].getName() +
+ " = " + args[i]);
+ }
+ }
+}
suite.addTestSuite(AsmDeclarationsTest.class);
suite.addTestSuite(AsmRelationshipsTest.class);
suite.addTestSuite(InpathTestcase.class);
+ suite.addTestSuite(ReweavableTestCase.class);
suite.addTestSuite(ResourceCopyTestCase.class);
suite.addTestSuite(ModelPerformanceTest.class);
suite.addTestSuite(SavedModelConsistencyTest. class);
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2004 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * Andy Clement Initial version
+ * ******************************************************************/
+
+package org.aspectj.ajde;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.aspectj.ajde.internal.CompilerAdapter;
+import org.aspectj.ajde.ui.UserPreferencesAdapter;
+import org.aspectj.ajde.ui.internal.AjcBuildOptions;
+import org.aspectj.ajdt.internal.core.builder.AjBuildConfig;
+import org.aspectj.bridge.MessageHandler;
+import org.aspectj.util.FileUtil;
+
+public class ReweavableTestCase extends AjdeTestCase {
+
+ private MessageHandler messageHandler;
+ private NullIdeProperties projectProperties;
+ private AjcBuildOptions buildOptions;
+ private UserPreferencesAdapter preferencesAdapter = null;
+ private CompilerAdapter compilerAdapter;
+ private static final String configFile =
+ AjdeTests.TESTDATA_PATH + "/examples/figures-coverage/all.lst";
+ public static final String PROJECT_DIR = "ReweavableTest";
+
+ private AjBuildConfig buildConfig = null;
+ public static final String binDir = "bin";
+
+ public static final String indir1Name = "indir1";
+ public static final String indir2Name = "indir2";
+ public static final String injarName = "injar.jar";
+ public static final String outjarName = "/bin/output.jar";
+
+
+ public static int nonreweavesize_CalculatePI;
+ public static int nonreweavesize_Logger;
+ public static int reweavablesize_CalculatePI;
+ public static int reweavablesize_Logger;
+
+ /**
+ * Constructor for JarResourceCopyTestCase.
+ * @param arg0
+ */
+ public ReweavableTestCase(String arg0) {
+ super(arg0);
+ }
+
+
+
+ /*
+ * Ensure the output directpry in clean
+ */
+ protected void setUp() throws Exception {
+ super.setUp(PROJECT_DIR);
+ FileUtil.deleteContents(openFile(binDir));
+ }
+
+
+ /**
+ * Aim: Check we haven't damaged 'normal compilation' when not supplying -Xreweavable. Also determines
+ * baseline sizes for the compiled class files for later comparison.
+ *
+ * Inputs to the compiler:
+ * NonReweavable1.lst
+ * -> CalculatePI.java
+ * -> Logger.aj
+ * -> -verbose
+ * -> -noExit
+ *
+ * Expected result = Compile successful, the types will not be reweavable and the weaver
+ * should not report it is running in reweavable mode.
+ */
+ public void testNonReweavableCompile() {
+ System.out.println("testNonReweavableCompile: Building with NonReweavable1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("NonReweavable1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertFalse("Did not expect to find a message about the weaver operating in reweavable mode",
+ checkFor("weaver operating in reweavable mode"));
+
+ File fCalc = openFile("bin/CalculatePI.class");
+ File fLog = openFile("bin/Logger.class");
+ assertTrue("bin/CalculatePI.class should exist?!?",fCalc.exists());
+ assertTrue("bin/Logger.class should exist?!?",fLog.exists());
+ System.out.println("CalculatePI.class is of size: "+fCalc.length());
+ System.out.println("Logger.class is of size: "+fLog.length());
+ System.out.println("\n\n\n");
+ nonreweavesize_CalculatePI = (int)fCalc.length();
+ nonreweavesize_Logger = (int)fLog.length();
+ }
+
+
+ /**
+ * Aim: Basic call to -Xreweavable. Weaver should report it is in reweavable mode and the
+ * classes produced should be much larger than normal classes (those produced in the first
+ * test).
+ *
+ * Inputs to the compiler:
+ * Reweavable1.lst
+ * -> CalculatePI.java
+ * -> Logger.aj
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ *
+ * Expected result = Compile successful, the types will be reweavable and the weaver
+ * should report it is running in reweavable mode. The files produced
+ * should be larger than those created during the last test.
+ */
+ public void testReweavableCompile() {
+ System.out.println("testReweavableCompile: Building with Reweavable1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("Reweavable1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+ File fCalc = openFile("bin/CalculatePI.class");
+ File fLog = openFile("bin/Logger.class");
+ assertTrue("bin/CalculatePI.class should exist?!?",fCalc.exists());
+ assertTrue("bin/Logger.class should exist?!?",fLog.exists());
+ System.out.println("CalculatePI.class is of size: "+fCalc.length());
+ System.out.println("Logger.class is of size: "+fLog.length());
+ assertTrue("Reweavable version should be larger than non-reweavable version of CalculatePI",
+ fCalc.length()>nonreweavesize_CalculatePI);
+ assertTrue("Reweavable version should be larger than non-reweavable version of Logger",
+ fLog.length()>nonreweavesize_Logger);
+
+ reweavablesize_CalculatePI = (int)fCalc.length();
+ reweavablesize_Logger = (int)fLog.length();
+
+ System.out.println("\n\n\n");
+ }
+
+
+ /**
+ * Aim: Use the optional ':compress' modifier on -Xreweavable. This causes some of the meta-data
+ * for use in reweaving to be compressed. It should succeed and produce class files smaller
+ * than straight -Xreweavable but larger than without specifying -Xreweavable.
+ *
+ * Inputs to the compiler:
+ * ReweavableCompress1.lst
+ * -> CalculatePI.java
+ * -> Logger.aj
+ * -> -Xreweavable:compress
+ * -> -verbose
+ * -> -noExit
+ *
+ * Expected result = Compile successful, the types will be reweavable and the weaver
+ * should report it is running in reweavable mode. The files created should
+ * have a size between the non-reweavable versions and the reweavable (without
+ * compression) versions.
+ */
+ public void testReweavableCompressCompile() {
+ System.out.println("testReweavableCompressCompile: Building with ReweavableCompress1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("ReweavableCompress1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+ File fCalc = openFile("bin/CalculatePI.class");
+ File fLog = openFile("bin/Logger.class");
+ assertTrue("bin/CalculatePI.class should exist?!?",fCalc.exists());
+ assertTrue("bin/Logger.class should exist?!?",fLog.exists());
+ System.out.println("CalculatePI.class is of size: "+fCalc.length());
+ System.out.println("Logger.class is of size: "+fLog.length());
+ assertTrue("Reweavable version should be larger than non-reweavable version of CalculatePI",
+ fCalc.length()>nonreweavesize_CalculatePI);
+ assertTrue("Reweavable version should be larger than non-reweavable version of Logger",
+ fLog.length()>nonreweavesize_Logger);
+
+ assertTrue("Reweavable (with compression) version should be smaller than reweavable (without compression) version of CalculatePI",
+ fCalc.length()<reweavablesize_CalculatePI);
+ assertTrue("Reweavable (with compression) version should be smaller than reweavable (without compression) version of Logger",
+ fLog.length()<reweavablesize_Logger);
+
+ System.out.println("\n\n\n");
+ }
+
+
+ /**
+ * Aim: The tests above have determined that reweaving appears to be behaving in terms of the .class
+ * files it is creating. Now lets actually attempt a reweave. For this, we build two files
+ * as reweavable and then build a single file whilst specifying an inpath that contains the
+ * .class files from the first compile. This should succeed.
+ *
+ * Inputs to the first compile:
+ * Reweavable1.lst
+ * -> CalculatePI.java
+ * -> Logger.aj
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ *
+ * Input to the second compile:
+ * Reweavable2.lst
+ * -> SecondAspect.aj
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ * -inpath bin\.
+ *
+ * Expected result = Both compiles will succeed.
+ */
+ public void testReweavableSimpleCompile() {
+ System.out.println("testReweavableSimpleCompile: Building with Reweavable1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("Reweavable1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+
+ System.out.println("\ntestReweavableSimpleCompile: Building with Reweavable2.lst");
+ Set paths = new HashSet();
+ paths.add(openFile(binDir));
+ ideManager.getProjectProperties().setInpath(paths);
+ compilerAdapter.compile((String) openFile("Reweavable2.lst").getAbsolutePath(),new BPM(),false);
+
+
+ String expMessage ="successfully verified type Logger exists";
+ assertTrue("Expected message '"+expMessage+"' but did not find it",
+ checkFor(expMessage));
+
+ File fCalc = openFile("bin/CalculatePI.class");
+ File fLog = openFile("bin/Logger.class");
+ File fSec = openFile("bin/SecondAspect.class");
+ assertTrue("bin/CalculatePI.class should exist?!?",fCalc.exists());
+ assertTrue("bin/Logger.class should exist?!?",fLog.exists());
+ assertTrue("bin/SecondAspect.class should exist?!?",fSec.exists());
+
+ System.out.println("\n\n\n");
+ }
+
+
+ /**
+ * Aim: Based on the test above, if we delete Logger.class between the first and second compiles
+ * the second compile should fail because there is not enough information to reweave CalculatePI
+ *
+ * Inputs to the first compile:
+ * Reweavable1.lst
+ * -> CalculatePI.java
+ * -> Logger.aj
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ *
+ * Input to the second compile:
+ * Reweavable2.lst
+ * -> SecondAspect.aj
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ * -inpath bin\.
+ *
+ * Expected result = Second compile will fail - reporting that Logger is missing (it 'touched' in the first compile CalculatePI)
+ */
+ public void testForReweavableSimpleErrorCompile() {
+ System.out.println("testForReweavableSimpleErrorCompile: Building with Reweavable2.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("Reweavable1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+
+ assertTrue("Could not delete bin/Logger.class??",openFile("bin/Logger.class").delete());
+
+
+ System.out.println("\ntestForReweavableSimpleErrorCompile: Building with Reweavable2.lst");
+ Set paths = new HashSet();
+ paths.add(openFile(binDir));
+ ideManager.getProjectProperties().setInpath(paths);
+ compilerAdapter.compile((String) openFile("Reweavable2.lst").getAbsolutePath(),new BPM(),false);
+
+
+ String expMessage ="type Logger is needed by reweavable type CalculatePI";
+ assertTrue("Expected message '"+expMessage+"' but did not find it",
+ checkFor(expMessage));
+
+ File fCalc = openFile("bin/CalculatePI.class");
+ File fLog = openFile("bin/Logger.class");
+ File fSec = openFile("bin/SecondAspect.class");
+ assertTrue("bin/CalculatePI.class should exist!",fCalc.exists());
+ assertTrue("bin/Logger.class should not exist!",!fLog.exists());
+ assertTrue("bin/SecondAspect.class should not exist!",fSec.exists());
+
+ System.out.println("\n\n\n");
+ }
+
+
+ /**
+ * Aim: Based on the test above, if we delete Logger.class between the first and second compiles
+ * the second compile should fail because there is not enough information to reweave CalculatePI
+ *
+ * Inputs to the first compile:
+ * TJP1.lst
+ * -> tjp/Demo.java
+ * -> tjp/GetInfo.java
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ *
+ * Now, delete bin\tjp\GetInfo.class and do a compile with:
+ * TJP2.lst
+ * -> -Xreweavable
+ * -> -verbose
+ * -> -noExit
+ * -inpath bin\.
+ *
+ * Expected result = Second compile will fail - reporting that tjp.GetInfo is missing (it 'touched' in the first compile tjp.Demo)
+ */
+ public void testErrorScenario2Compile() {
+ System.out.println("testErrorScenario2: Building with TJP1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("TJP1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+
+ assertTrue("Could not delete bin/tjp/GetInfo.class??",openFile("bin/tjp/GetInfo.class").delete());
+
+
+ System.out.println("\ntestErrorScenario2: Building with TJP2.lst");
+ Set paths = new HashSet();
+ paths.add(openFile(binDir));
+ ideManager.getProjectProperties().setInpath(paths);
+ compilerAdapter.compile((String) openFile("TJP2.lst").getAbsolutePath(),new BPM(),false);
+
+
+ String expMessage ="type tjp.GetInfo is needed by reweavable type tjp.Demo";
+ assertTrue("Expected message '"+expMessage+"' but did not find it",
+ checkFor(expMessage));
+
+ File fDemo = openFile("bin/tjp/Demo.class");
+ File fGetInfo = openFile("bin/tjp/GetInfo.class");
+ assertTrue("bin/tjp/Demo.class should exist!",fDemo.exists());
+ assertTrue("bin/tjp/GetInfo.class should not exist!",!fGetInfo.exists());
+
+ System.out.println("\n\n\n");
+ }
+
+ public void testWorkingScenario2Compile() {
+ System.out.println("testWorkingScenario2: Building with TJP1.lst");
+ compilerAdapter = new CompilerAdapter();
+ compilerAdapter.showInfoMessages(true);
+ compilerAdapter.compile((String) openFile("TJP1.lst").getAbsolutePath(),new BPM(),false);
+
+ assertTrue("Expected a message about operating in reweavable mode, but didn't get one",
+ checkFor("weaver operating in reweavable mode"));
+
+
+ System.out.println("\ntestWorkingScenario2: Building with TJP2.lst");
+ Set paths = new HashSet();
+ paths.add(openFile(binDir));
+ ideManager.getProjectProperties().setInpath(paths);
+ compilerAdapter.compile((String) openFile("TJP2.lst").getAbsolutePath(),new BPM(),false);
+
+
+ String expMessage ="successfully verified type tjp.GetInfo exists";
+ assertTrue("Expected message '"+expMessage+"' but did not find it",
+ checkFor(expMessage));
+
+ File fGetInfo = openFile("bin/tjp/GetInfo.class");
+ File fDemo = openFile("bin/tjp/Demo.class");
+ assertTrue("bin/tjp/GetInfo.class should exist!",fGetInfo.exists());
+ assertTrue("bin/tjp/Demo.class should not exist!",fDemo.exists());
+
+ System.out.println("\n\n\n");
+ }
+
+
+ private class BPM implements BuildProgressMonitor {
+ public void start(String configFile) {}
+
+ public void setProgressText(String text) {}
+
+ public void setProgressBarVal(int newVal) { }
+
+ public void incrementProgressBarVal() {}
+
+ public void setProgressBarMax(int maxVal) { }
+
+ public int getProgressBarMax() {
+ return 0;
+ }
+
+ public void finish() {}
+
+ }
+
+
+
+
+ private boolean checkFor(String what) {
+ List ll = ideManager.getCompilationSourceLineTasks();
+ for (Iterator iter = ll.iterator(); iter.hasNext();) {
+ Object element = (Object) iter.next();
+ if (element.toString().indexOf(what)!=-1) return true;
+ }
+ return false;
+ }
+
+
+}
for input using -injars.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>-Xreweavable[:compress]</term>
+ <listitem><para>(Experimental) runs weaver in reweavable mode which causes
+ it to create woven classes that can be rewoven, subject to the restriction that
+ on attempting a reweave all the types that advised the woven type must be accessible.
+ </para></listitem>
+ </varlistentry>
<varlistentry>
<term>-XnoInline</term>
buildConfig.setXserializableAspects(true);
} else if (arg.equals("-XlazyTjp")) {
buildConfig.setXlazyTjp(true);
+ } else if (arg.startsWith("-Xreweavable")) {
+ buildConfig.setXreweavable(true);
+ if (arg.endsWith(":compress")) {
+ buildConfig.setXreweavableCompressClasses(true);
+ }
} else if (arg.equals("-XnoInline")) {
buildConfig.setXnoInline(true);
} else if (arg.equals("-Xlintfile")) {
\ -showversion print compiler version and continue\n
## options not documented above (per ..ajdt.ajc.BuildArgParser.java):
-# -XincrementalFile, -XnoWeave, -XserializableAspects, -XnoInline
+# -XincrementalFile, -XnoWeave, -XserializableAspects, -XnoInline, -Xreweavable[:compress]
###############################################################################
# Copyright (c) 2000, 2004 IBM Corporation and others.
private boolean XserializableAspects = false;
private boolean XlazyTjp = false;
private boolean XnoInline = false;
+ private boolean Xreweavable = false;
+ private boolean XreweavableCompressClasses = false;
private String lintMode = AJLINT_DEFAULT;
private File lintSpecFile = null;
public void setInJars(List sourceJars) {
this.inJars = sourceJars;
}
+
+ public void setInPath(List dirsOrJars) {
+ inPath = dirsOrJars;
+ }
public List getSourceRoots() {
return sourceRoots;
XlazyTjp = b;
}
+ public void setXreweavable(boolean b) {
+ Xreweavable = true;
+ }
+
+ public boolean isXreweavable() {
+ return Xreweavable;
+ }
+
+ public void setXreweavableCompressClasses(boolean b) {
+ XreweavableCompressClasses = true;
+ }
+
+ public boolean getXreweavableCompressClasses() {
+ return XreweavableCompressClasses;
+ }
+
}
// bcelWeaver.addResource(resource, buildConfig.getOutputDir());
}
}
+
+ bcelWeaver.setReweavableMode(buildConfig.isXreweavable(),buildConfig.getXreweavableCompressClasses());
+
//check for org.aspectj.runtime.JoinPoint
bcelWorld.resolve("org.aspectj.lang.JoinPoint");
}
public void setXNoweave(boolean noweave) {
cmd.addFlag("-XnoWeave", noweave);
}
+
+ public void setXReweavable(boolean reweavable) {
+ cmd.addFlag("-Xreweavable",reweavable);
+ }
public void setNowarn(boolean nowarn) {
cmd.addFlag("-nowarn", nowarn);
setXlintfile(new File(in.next()));
} else if ("-Xnoweave".equals(flag)) {
setXNoweave(true);
+ } else if ("-Xreweavable".equals(flag)) {
+ setXReweavable(true);
} else if (flag.startsWith("@")) {
File file = new File(flag.substring(1));
if (file.canRead()) {
"1.5" }}),
factory.create("XnoInline"),
factory.create("XnoWeave"),
+ factory.create("Xreweavable"),
factory.create("XserializableAspects")
};
protected TypePattern exceptionType; // just for Softener kind
- public static Advice makeCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, int nFreeVars, List innerCflowEntries) {
+ public static Advice makeCflowEntry(World world, Pointcut entry, boolean isBelow, Member stackField, int nFreeVars, List innerCflowEntries, ResolvedTypeX inAspect){
Advice ret = world.concreteAdvice(isBelow ? AdviceKind.CflowBelowEntry : AdviceKind.CflowEntry,
entry, stackField, 0, entry);
//0);
ret.innerCflowEntries = innerCflowEntries;
ret.nFreeVars = nFreeVars;
+ ret.concreteAspect = inAspect;
return ret;
}
return ret;
}
- public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType) {
+ public static Advice makeSoftener(World world, Pointcut entry, TypePattern exceptionType,ResolvedTypeX inAspect) {
Advice ret = world.concreteAdvice(AdviceKind.Softener,
entry, null, 0, entry);
ret.exceptionType = exceptionType;
- //System.out.println("made ret: " + ret + " with " + exceptionType);
+ ret.concreteAspect = inAspect;
+ // System.out.println("made ret: " + ret + " with " + exceptionType);
return ret;
}
DeclareSoft d = (DeclareSoft)declare;
Pointcut concretePointcut = d.getPointcut().concretize(inAspect, 0);
declareSofts.add(new DeclareSoft(d.getException(), concretePointcut));
- ShadowMunger m = Advice.makeSoftener(world, concretePointcut, d.getException());
+ ShadowMunger m = Advice.makeSoftener(world, concretePointcut, d.getException(),inAspect);
addConcreteShadowMunger(m);
} else {
throw new RuntimeException("unimplemented");
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import org.aspectj.weaver.bcel.BcelTypeMunger;
+import org.aspectj.weaver.patterns.DeclareParents;
/**
* This holds on to all CrosscuttingMembers for a world. It handles
declareDominates = ret;
}
return declareDominates;
- }
+ }
+
+
+ public ResolvedTypeX findAspectDeclaringParents(DeclareParents p) {
+ Set result = new HashSet();
+ Set keys = this.members.keySet();
+ for (Iterator iter = keys.iterator(); iter.hasNext();) {
+ ResolvedTypeX element = (ResolvedTypeX) iter.next();
+ for (Iterator i = ((CrosscuttingMembers)members.get(element)).getDeclareParents().iterator(); i.hasNext(); ) {
+ DeclareParents dp = (DeclareParents)i.next();
+ return element;
+ }
+ }
+ return null;
+ }
+
+
}
protected final Shadow enclosingShadow;
protected List mungers = new ArrayList(1);
+
// ----
protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) {
this.kind = kind;
// ----
public abstract World getIWorld();
-
+
+ public List /*ShadowMunger*/ getMungers() {
+ return mungers;
+ }
+
/**
* could this(*) pcd ever match
*/
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
import org.aspectj.bridge.IMessage;
import org.aspectj.weaver.bcel.BcelTypeMunger;
+
+/**
+ * WeaverStateInfo represents how a type was processed. It is used by the weaver to determine how a type
+ * was previously treated and whether reweaving is allowed.
+ * The format in the data stream is:
+ *
+ * Byte: Kind. UNTOUCHED|WOVEN|EXTENDED - If extended it can have two extra bits set 'REWEAVABLE' and 'REWEAVABLE_COMPRESSION_BIT'
+ * Short: typeMungerCount - how many type mungers have affected this type
+ * <TypeX & ResolvedTypeMunger>: The type mungers themselves
+ * If we are reweavable then we also have:
+ * Short: Number of aspects that touched this type in some way when it was previously woven
+ * <String> The fully qualified name of each type
+ * Int: Length of class file data (i.e. the unwovenclassfile)
+ * Byte[]: The class file data, compressed if REWEAVABLE_COMPRESSION_BIT set.
+ */
+
+
public class WeaverStateInfo {
private List/*Entry*/ typeMungers;
private boolean oldStyle;
+
+
+ private boolean reweavable;
+ private boolean reweavableCompressedMode; // If true, unwovenClassFile is compressed on write and uncompressed on read
+ private Set /*String*/ aspectsAffectingType; // These must exist in the world for reweaving to be valid
+ private byte[] unwovenClassFile; // Original 'untouched' class file
+ private static boolean reweavableDefault = false;
+ private static boolean reweavableCompressedModeDefault = false;
+
public WeaverStateInfo() {
- this(new ArrayList(), false);
+ this(new ArrayList(), false,reweavableDefault,reweavableCompressedModeDefault);
}
- private WeaverStateInfo(List typeMungers, boolean oldStyle) {
+ private WeaverStateInfo(List typeMungers, boolean oldStyle,boolean reweavableMode,boolean reweavableCompressedMode) {
this.typeMungers = typeMungers;
- this.oldStyle = oldStyle;
+ this.oldStyle = oldStyle;
+ this.reweavable = reweavableMode;
+ this.reweavableCompressedMode = reweavableCompressedMode;
+ this.aspectsAffectingType= new HashSet();
+ this.unwovenClassFile = null;
+ }
+
+ public static void setReweavableModeDefaults(boolean mode, boolean compress) {
+ reweavableDefault = mode;
+ reweavableCompressedModeDefault = compress;
}
private static final int UNTOUCHED=0, WOVEN=2, EXTENDED=3;
+ // Use 'bits' for these capabilities - only valid in EXTENDED mode
+ private static final byte REWEAVABLE_BIT = 1<<4;
+ private static final byte REWEAVABLE_COMPRESSION_BIT = 1<<5;
+
public static final WeaverStateInfo read(DataInputStream s, ISourceContext context) throws IOException {
byte b = s.readByte();
+ boolean isReweavable = ((b&REWEAVABLE_BIT)!=0);
+ if (isReweavable) b=(byte) (b-REWEAVABLE_BIT);
+
+ boolean isReweavableCompressed = ((b&REWEAVABLE_COMPRESSION_BIT)!=0);
+ if (isReweavableCompressed) b=(byte) (b-REWEAVABLE_COMPRESSION_BIT);
+
switch(b) {
case UNTOUCHED:
throw new RuntimeException("unexpected UNWOVEN");
case WOVEN:
- return new WeaverStateInfo(Collections.EMPTY_LIST, true);
+ return new WeaverStateInfo(Collections.EMPTY_LIST, true,isReweavable,isReweavableCompressed);
case EXTENDED:
int n = s.readShort();
List l = new ArrayList();
ResolvedTypeMunger.read(s, context);
l.add(new Entry(aspectType, typeMunger));
}
- return new WeaverStateInfo(l, false);
+ WeaverStateInfo wsi = new WeaverStateInfo(l,false,isReweavable,isReweavableCompressed);
+ readAnyReweavableData(wsi,s);
+ return wsi;
}
throw new RuntimeException("bad WeaverState.Kind: " + b);
}
+
+
private static class Entry {
public TypeX aspectType;
public ResolvedTypeMunger typeMunger;
public void write(DataOutputStream s) throws IOException {
if (oldStyle) throw new RuntimeException("shouldn't be writing this");
- s.writeByte(EXTENDED);
+ byte weaverStateInfoKind = EXTENDED;
+ if (reweavable) weaverStateInfoKind |= REWEAVABLE_BIT;
+ if (reweavableCompressedMode) weaverStateInfoKind |= REWEAVABLE_COMPRESSION_BIT;
+ s.writeByte(weaverStateInfoKind);
int n = typeMungers.size();
s.writeShort(n);
for (int i=0; i < n; i++) {
e.aspectType.write(s);
e.typeMunger.write(s);
}
+ writeAnyReweavableData(this,s);
}
public void addConcreteMunger(ConcreteTypeMunger munger) {
public boolean isOldStyle() {
return oldStyle;
}
+
+ public byte[] getUnwovenClassFileData() {
+ return unwovenClassFile;
+ }
+
+ public void setUnwovenClassFileData(byte[] data) {
+ unwovenClassFile = data;
+ }
+
+ public boolean isReweavable() {
+ return reweavable;
+ }
+
+ public void setReweavable(boolean rw,boolean compressData) {
+ reweavable = rw;
+ reweavableCompressedMode = compressData;
+ }
+
+ public void addAspectsAffectingType(Collection /*String*/ aspects) {
+ aspectsAffectingType.addAll(aspects);
+ }
+ public void addAspectAffectingType(String aspectType) {
+ aspectsAffectingType.add(aspectType);
+ }
+ public Set /*String*/ getAspectsAffectingType() {
+ return this.aspectsAffectingType;
+ }
+
+
+ ////
+
+ private static void readAnyReweavableData(WeaverStateInfo wsi,DataInputStream s) throws IOException {
+
+ if (wsi.isReweavable()) {
+ // Load list of aspects that need to exist in the world for reweaving to be 'legal'
+ int numberAspectsAffectingType = s.readShort();
+ for (int i=0; i < numberAspectsAffectingType; i++) {wsi.addAspectAffectingType(s.readUTF());}
+
+ int unwovenClassFileSize = s.readInt();
+ byte[] classData = null;
+ // The data might or might not be compressed:
+ if (!wsi.reweavableCompressedMode) {
+ // Read it straight in
+ classData = new byte[unwovenClassFileSize];
+ int bytesread = s.read(classData);
+ if (bytesread!=unwovenClassFileSize)
+ throw new IOException("ERROR whilst reading reweavable data, expected "+
+ unwovenClassFileSize+" bytes, only found "+bytesread);
+ } else {
+ // Decompress it
+ classData = new byte[unwovenClassFileSize];
+
+ ZipInputStream zis = new ZipInputStream(s);
+ ZipEntry zen = zis.getNextEntry();
+ int current = 0;
+ int bytesToGo=unwovenClassFileSize;
+ while (bytesToGo>0) {
+ int amount = zis.read(classData,current,bytesToGo);
+ current+=amount;
+ bytesToGo-=amount;
+ }
+ zis.closeEntry();
+ if (bytesToGo!=0)
+ throw new IOException("ERROR whilst reading compressed reweavable data, expected "+
+ unwovenClassFileSize+" bytes, only found "+current);
+ }
+ wsi.setUnwovenClassFileData(classData);
+ }
+ }
+
+
+
+ private static void writeAnyReweavableData(WeaverStateInfo wsi,DataOutputStream s) throws IOException {
+ if (wsi.isReweavable()) {
+ // Write out list of aspects that must exist next time we try and weave this class
+ s.writeShort(wsi.aspectsAffectingType.size());
+ if (wsi.aspectsAffectingType.size()>0) {
+ for (Iterator iter = wsi.aspectsAffectingType.iterator(); iter.hasNext();) {
+ String type = (String) iter.next();
+ s.writeUTF(type);
+ }
+ }
+ byte[] data = wsi.unwovenClassFile;
+ s.writeInt(data.length);
+ // Do we need to compress the data?
+ if (!wsi.reweavableCompressedMode) {
+ s.write(wsi.unwovenClassFile);
+ } else {
+ ZipOutputStream zos = new ZipOutputStream(s);
+ ZipEntry ze = new ZipEntry("data");
+ zos.putNextEntry(ze);
+ zos.write(wsi.unwovenClassFile,0,wsi.unwovenClassFile.length);
+ zos.closeEntry();
+ }
+ }
+ }
+
}
public class BcelCflowStackFieldAdder extends BcelTypeMunger {
private ResolvedMember cflowStackField;
public BcelCflowStackFieldAdder(ResolvedMember cflowStackField) {
- super(null, null);
+ super(null,(ResolvedTypeX)cflowStackField.getDeclaringType());
this.cflowStackField = cflowStackField;
}
import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
+import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.Shadow.Kind;
import org.aspectj.weaver.patterns.FastMatchInfo;
private final List addedLazyMethodGens = new ArrayList();
private final Set addedDispatchTargets = new HashSet();
+
+ // Static setting across BcelClassWeavers
+ private static boolean inReweavableMode = false;
+ private static boolean compressReweavableAttributes = false;
+
private List addedSuperInitializersAsList = null; // List<IfaceInitList>
private final Map addedSuperInitializers = new HashMap(); // Interface -> IfaceInitList
// ----
public boolean weave() {
- if (clazz.isWoven()) {
+
+ if (clazz.isWoven() && !clazz.isReweavable()) {
world.showMessage(IMessage.ERROR,
- "class \'" + clazz.getType().getName() + "\' is already woven",
+ "class \'" + clazz.getType().getName() + "\' is already woven and has not been built with -Xreweavable",
ty.getSourceLocation(), null);
return false;
}
+
+
+ Set aspectsAffectingType = null;
+ if (inReweavableMode) aspectsAffectingType = new HashSet();
boolean isChanged = false;
continue;
}
BcelTypeMunger munger = (BcelTypeMunger)o;
- isChanged |= munger.munge(this);
+ boolean typeMungerAffectedType = munger.munge(this);
+ if (typeMungerAffectedType) {
+ isChanged = true;
+ if (inReweavableMode) aspectsAffectingType.add(munger.getAspectType().getName());
+ }
}
// XXX do major sort of stuff
LazyMethodGen mg = (LazyMethodGen)i.next();
//mg.getBody();
if (! mg.hasBody()) continue;
- isChanged |= match(mg);
+ boolean shadowMungerMatched = match(mg);
+ if (shadowMungerMatched) {
+ // For matching mungers, add their declaring aspects to the list that affected this type
+ if (inReweavableMode) aspectsAffectingType.addAll(findAspectsForMungers(mg));
+ isChanged = true;
+ }
}
if (! isChanged) return false;
weaveInAddedMethods();
}
+ if (inReweavableMode) {
+ WeaverStateInfo wsi = clazz.getOrCreateWeaverStateInfo();
+ wsi.addAspectsAffectingType(aspectsAffectingType);
+ wsi.setUnwovenClassFileData(ty.getJavaClass().getBytes());
+ wsi.setReweavable(true,compressReweavableAttributes);
+ } else {
+ clazz.getOrCreateWeaverStateInfo().setReweavable(false,false);
+ }
+
return isChanged;
}
+ private Set findAspectsForMungers(LazyMethodGen mg) {
+ Set aspectsAffectingType = new HashSet();
+ for (Iterator iter = mg.matchedShadows.iterator(); iter.hasNext();) {
+ BcelShadow aShadow = (BcelShadow) iter.next();
+ // Mungers in effect on that shadow
+ for (Iterator iter2 = aShadow.getMungers().iterator();iter2.hasNext();) {
+ ShadowMunger aMunger = (ShadowMunger) iter2.next();
+ if (aMunger instanceof BcelAdvice) {
+ BcelAdvice bAdvice = (BcelAdvice)aMunger;
+ aspectsAffectingType.add(bAdvice.getConcreteAspect().getName());
+ } else {
+ // It is a 'Checker' - we don't need to remember aspects that only contributed Checkers...
+ }
+ }
+ }
+ return aspectsAffectingType;
+ }
+
private void inlineSelfConstructors(List methodGens) {
for (Iterator i = methodGens.iterator(); i.hasNext();) {
public BcelWorld getWorld() {
return world;
}
+
+ // Called by the BcelWeaver to let us know all BcelClassWeavers need to collect reweavable info
+ public static void setReweavableMode(boolean mode,boolean compress) {
+ inReweavableMode = mode;
+ compressReweavableAttributes = compress;
+ }
}
unpackAspectAttributes();
}
+
+ // repeat initialization
+ public void setJavaClass(JavaClass newclass) {
+ this.javaClass = newclass;
+ resetState();
+ }
+
public int getModifiers() {
return javaClass.getAccessFlags();
}
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
+import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IProgressListener;
import org.aspectj.util.FileUtil;
import org.aspectj.weaver.ResolvedTypeX;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.TypeX;
+import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.FastMatchInfo;
private IProgressListener progressListener = null;
private double progressMade;
private double progressPerClassFile;
-
+
+ private boolean inReweavableMode = false;
+ private boolean compressReweavableAttributes = false;
+
public BcelWeaver(BcelWorld world) {
super();
this.world = world;
//System.err.println("typeMungers: " + typeMungerList);
+
+ if (inReweavableMode)
+ world.showMessage(IMessage.INFO, "weaver operating in reweavable mode. Need to verify any required types exist.", null, null);
+
+
+ Set alreadyConfirmedOK = new HashSet();
// clear all state from files we'll be reweaving
for (Iterator i = filesToWeave.iterator(); i.hasNext(); ) {
UnwovenClassFile classFile = (UnwovenClassFile)i.next();
String className = classFile.getClassName();
BcelObjectType classType = BcelWorld.getBcelObjectType(world.resolve(className));
- classType.resetState();
+
+
+ // If the class is marked reweavable, check any aspects around when it was built are in this world
+ WeaverStateInfo wsi = classType.getWeaverState();
+ if (wsi!=null && wsi.isReweavable()) { // Check all necessary types are around!
+ world.showMessage(IMessage.INFO,"processing reweavable type "+className+": "+classType.getSourceLocation().getSourceFile(),null,null);
+ Set aspectsPreviouslyInWorld = wsi.getAspectsAffectingType();
+ if (aspectsPreviouslyInWorld!=null) {
+ for (Iterator iter = aspectsPreviouslyInWorld.iterator(); iter.hasNext();) {
+ String requiredTypeName = (String) iter.next();
+ if (!alreadyConfirmedOK.contains(requiredTypeName)) {
+ ResolvedTypeX rtx = world.resolve(TypeX.forName(requiredTypeName),true);
+ boolean exists = rtx!=ResolvedTypeX.MISSING;
+ if (!exists) {
+ world.showMessage(IMessage.ERROR, "type " + requiredTypeName +
+ " is needed by reweavable type " + className,
+ classType.getSourceLocation(), null);
+ } else {
+ if (!world.getMessageHandler().isIgnoring(IMessage.INFO))
+ world.showMessage(IMessage.INFO,"successfully verified type "+requiredTypeName+
+ " exists. Originates from "+rtx.getSourceLocation().getSourceFile(),null,null);
+ alreadyConfirmedOK.add(requiredTypeName);
+ }
+ }
+ }
+ }
+ classType.setJavaClass(Utility.makeJavaClass(classType.getJavaClass().getFileName(), wsi.getUnwovenClassFileData()));
+ } else {
+ classType.resetState();
+ }
}
classType.addParent(newParent);
ResolvedTypeMunger newParentMunger = new NewParentTypeMunger(newParent);
- onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, null));
+ onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, xcutSet.findAspectDeclaringParents(p)));
}
}
}
return ret;
}
+
private LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType, boolean dump) throws IOException {
if (classType.isSynthetic()) {
this.progressPerClassFile = progressPerClassFile;
}
+ public void setReweavableMode(boolean mode,boolean compress) {
+ inReweavableMode = mode;
+ compressReweavableAttributes = compress;
+ WeaverStateInfo.setReweavableModeDefaults(mode,compress);
+ BcelClassWeaver.setReweavableMode(mode,compress);
+ }
+
}
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
return myType.getWeaverState() != null;
}
+ public boolean isReweavable() {
+ if (myType.getWeaverState()==null) return false;
+ return myType.getWeaverState().isReweavable();
+ }
+
+ public Set getAspectsAffectingType() {
+ if (myType.getWeaverState()==null) return null;
+ return myType.getWeaverState().getAspectsAffectingType();
+ }
+
public WeaverStateInfo getOrCreateWeaverStateInfo() {
WeaverStateInfo ret = myType.getWeaverState();
if (ret != null) return ret;
//XXX and then that info above needs to be mapped down here to help with
//XXX getting the exposed state right
concreteAspect.crosscuttingMembers.addConcreteShadowMunger(
- Advice.makeCflowEntry(world, concreteEntry, isBelow, cflowField, freeVars.length, innerCflowEntries));
+ Advice.makeCflowEntry(world, concreteEntry, isBelow, cflowField, freeVars.length, innerCflowEntries,inAspect));
concreteAspect.crosscuttingMembers.addTypeMunger(
world.makeCflowStackFieldAdder(cflowField));