From: aclement Date: Tue, 24 Feb 2004 13:43:56 +0000 (+0000) Subject: Fix for Bug 36430: Xreweavable support X-Git-Tag: v_preCompileLoopAlteration~27 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=16a0abd70e5fe2538c32994de05f52b6bf939ef5;p=aspectj.git Fix for Bug 36430: Xreweavable support --- diff --git a/ajde/testdata/ReweavableTest/CalculatePI.java b/ajde/testdata/ReweavableTest/CalculatePI.java new file mode 100644 index 000000000..84ae08583 --- /dev/null +++ b/ajde/testdata/ReweavableTest/CalculatePI.java @@ -0,0 +1,26 @@ +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 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() 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; + } + + +} diff --git a/docs/devGuideDB/ajc.xml b/docs/devGuideDB/ajc.xml index afe218887..0bfcbb769 100644 --- a/docs/devGuideDB/ajc.xml +++ b/docs/devGuideDB/ajc.xml @@ -380,6 +380,14 @@ for input using -injars. + + + -Xreweavable[:compress] + (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. + + -XnoInline diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/BuildArgParser.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/BuildArgParser.java index cf6174746..6132b74d0 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/BuildArgParser.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/BuildArgParser.java @@ -444,6 +444,11 @@ public class BuildArgParser extends Main { 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")) { diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/messages.properties b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/messages.properties index ac2113113..27b3cb7ae 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/messages.properties +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/ajc/messages.properties @@ -115,7 +115,7 @@ Standard Eclipse compiler options:\n\ \ -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. diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildConfig.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildConfig.java index ad182a589..f9c2571db 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildConfig.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildConfig.java @@ -56,6 +56,8 @@ public class AjBuildConfig { // XXX needs bootclasspath? 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; @@ -214,6 +216,10 @@ public class AjBuildConfig { // XXX needs bootclasspath? public void setInJars(List sourceJars) { this.inJars = sourceJars; } + + public void setInPath(List dirsOrJars) { + inPath = dirsOrJars; + } public List getSourceRoots() { return sourceRoots; @@ -462,4 +468,20 @@ public class AjBuildConfig { // XXX needs bootclasspath? 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; + } + } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java index d0a79ca3c..efa482548 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java @@ -226,6 +226,9 @@ public class AjBuildManager { // bcelWeaver.addResource(resource, buildConfig.getOutputDir()); } } + + bcelWeaver.setReweavableMode(buildConfig.isXreweavable(),buildConfig.getXreweavableCompressClasses()); + //check for org.aspectj.runtime.JoinPoint bcelWorld.resolve("org.aspectj.lang.JoinPoint"); } diff --git a/taskdefs/src/org/aspectj/tools/ant/taskdefs/AjcTask.java b/taskdefs/src/org/aspectj/tools/ant/taskdefs/AjcTask.java index 0bf34a24e..10f7c42dc 100644 --- a/taskdefs/src/org/aspectj/tools/ant/taskdefs/AjcTask.java +++ b/taskdefs/src/org/aspectj/tools/ant/taskdefs/AjcTask.java @@ -454,6 +454,10 @@ public class AjcTask extends MatchingTask { 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); @@ -1699,6 +1703,8 @@ public class AjcTask extends MatchingTask { 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()) { diff --git a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java index efa60f1c1..084c17c7a 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java +++ b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java @@ -1645,6 +1645,7 @@ public class CompilerRun implements IAjcRun { "1.5" }}), factory.create("XnoInline"), factory.create("XnoWeave"), + factory.create("Xreweavable"), factory.create("XserializableAspects") }; diff --git a/weaver/src/org/aspectj/weaver/Advice.java b/weaver/src/org/aspectj/weaver/Advice.java index b25df052c..4e76e2e28 100644 --- a/weaver/src/org/aspectj/weaver/Advice.java +++ b/weaver/src/org/aspectj/weaver/Advice.java @@ -37,12 +37,13 @@ public abstract class Advice extends ShadowMunger { 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; } @@ -66,12 +67,13 @@ public abstract class Advice extends ShadowMunger { 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; } diff --git a/weaver/src/org/aspectj/weaver/CrosscuttingMembers.java b/weaver/src/org/aspectj/weaver/CrosscuttingMembers.java index b5311a9ec..0992fef89 100644 --- a/weaver/src/org/aspectj/weaver/CrosscuttingMembers.java +++ b/weaver/src/org/aspectj/weaver/CrosscuttingMembers.java @@ -110,7 +110,7 @@ public class CrosscuttingMembers { 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"); diff --git a/weaver/src/org/aspectj/weaver/CrosscuttingMembersSet.java b/weaver/src/org/aspectj/weaver/CrosscuttingMembersSet.java index 2dcae923c..4a359d69a 100644 --- a/weaver/src/org/aspectj/weaver/CrosscuttingMembersSet.java +++ b/weaver/src/org/aspectj/weaver/CrosscuttingMembersSet.java @@ -15,10 +15,14 @@ package org.aspectj.weaver; 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 @@ -144,5 +148,21 @@ public class CrosscuttingMembersSet { 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; + } + + } diff --git a/weaver/src/org/aspectj/weaver/Shadow.java b/weaver/src/org/aspectj/weaver/Shadow.java index ade65de83..266c8b4db 100644 --- a/weaver/src/org/aspectj/weaver/Shadow.java +++ b/weaver/src/org/aspectj/weaver/Shadow.java @@ -39,6 +39,7 @@ public abstract class Shadow { protected final Shadow enclosingShadow; protected List mungers = new ArrayList(1); + // ---- protected Shadow(Kind kind, Member signature, Shadow enclosingShadow) { this.kind = kind; @@ -49,7 +50,11 @@ public abstract class Shadow { // ---- public abstract World getIWorld(); - + + public List /*ShadowMunger*/ getMungers() { + return mungers; + } + /** * could this(*) pcd ever match */ diff --git a/weaver/src/org/aspectj/weaver/WeaverStateInfo.java b/weaver/src/org/aspectj/weaver/WeaverStateInfo.java index a2e78ed01..127bb3ee9 100644 --- a/weaver/src/org/aspectj/weaver/WeaverStateInfo.java +++ b/weaver/src/org/aspectj/weaver/WeaverStateInfo.java @@ -17,36 +17,87 @@ import java.io.DataInputStream; 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 + * : 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 + * 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(); @@ -56,11 +107,15 @@ public class WeaverStateInfo { 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; @@ -77,7 +132,10 @@ public class WeaverStateInfo { 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++) { @@ -85,6 +143,7 @@ public class WeaverStateInfo { e.aspectType.write(s); e.typeMunger.write(s); } + writeAnyReweavableData(this,s); } public void addConcreteMunger(ConcreteTypeMunger munger) { @@ -117,4 +176,101 @@ public class WeaverStateInfo { 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(); + } + } + } + } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java b/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java index bd0f31f0c..0de033913 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelCflowStackFieldAdder.java @@ -28,7 +28,7 @@ import org.aspectj.weaver.ResolvedTypeX; public class BcelCflowStackFieldAdder extends BcelTypeMunger { private ResolvedMember cflowStackField; public BcelCflowStackFieldAdder(ResolvedMember cflowStackField) { - super(null, null); + super(null,(ResolvedTypeX)cflowStackField.getDeclaringType()); this.cflowStackField = cflowStackField; } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java b/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java index d67d4eb87..514e92262 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelClassWeaver.java @@ -62,6 +62,7 @@ import org.aspectj.weaver.ResolvedMember; 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; @@ -97,6 +98,11 @@ class BcelClassWeaver implements IClassWeaver { 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 private final Map addedSuperInitializers = new HashMap(); // Interface -> IfaceInitList @@ -279,12 +285,17 @@ class BcelClassWeaver implements IClassWeaver { // ---- 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; @@ -300,7 +311,11 @@ class BcelClassWeaver implements IClassWeaver { 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 @@ -327,7 +342,12 @@ class BcelClassWeaver implements IClassWeaver { 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; @@ -352,9 +372,36 @@ class BcelClassWeaver implements IClassWeaver { 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();) { @@ -1095,5 +1142,11 @@ class BcelClassWeaver implements IClassWeaver { 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; + } } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java b/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java index a055f92a4..1c8a44c0c 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelObjectType.java @@ -85,6 +85,13 @@ public class BcelObjectType extends ResolvedTypeX.ConcreteName { unpackAspectAttributes(); } + + // repeat initialization + public void setJavaClass(JavaClass newclass) { + this.javaClass = newclass; + resetState(); + } + public int getModifiers() { return javaClass.getAccessFlags(); } diff --git a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java b/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java index 7dea8184b..12d7518d6 100644 --- a/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java +++ b/weaver/src/org/aspectj/weaver/bcel/BcelWeaver.java @@ -29,12 +29,14 @@ import java.util.HashSet; 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; @@ -46,6 +48,7 @@ import org.aspectj.weaver.ResolvedTypeMunger; 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; @@ -55,7 +58,10 @@ public class BcelWeaver implements IWeaver { 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; @@ -354,12 +360,47 @@ public class BcelWeaver implements IWeaver { //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(); + } } @@ -431,7 +472,7 @@ public class BcelWeaver implements IWeaver { classType.addParent(newParent); ResolvedTypeMunger newParentMunger = new NewParentTypeMunger(newParent); - onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, null)); + onType.addInterTypeMunger(new BcelTypeMunger(newParentMunger, xcutSet.findAspectDeclaringParents(p))); } } } @@ -462,6 +503,7 @@ public class BcelWeaver implements IWeaver { return ret; } + private LazyClassGen weave(UnwovenClassFile classFile, BcelObjectType classType, boolean dump) throws IOException { if (classType.isSynthetic()) { @@ -574,4 +616,11 @@ public class BcelWeaver implements IWeaver { this.progressPerClassFile = progressPerClassFile; } + public void setReweavableMode(boolean mode,boolean compress) { + inReweavableMode = mode; + compressReweavableAttributes = compress; + WeaverStateInfo.setReweavableModeDefaults(mode,compress); + BcelClassWeaver.setReweavableMode(mode,compress); + } + } diff --git a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java b/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java index 6a37aee39..c00320171 100644 --- a/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java +++ b/weaver/src/org/aspectj/weaver/bcel/LazyClassGen.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -470,6 +471,16 @@ public final class LazyClassGen { 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; diff --git a/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java b/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java index 6aa5f7fc3..4c00603e9 100644 --- a/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java +++ b/weaver/src/org/aspectj/weaver/patterns/CflowPointcut.java @@ -161,7 +161,7 @@ public class CflowPointcut extends Pointcut { //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));