From 1d471f7cfaf166d4321c56b52fe47859e9974441 Mon Sep 17 00:00:00 2001 From: aclement Date: Thu, 10 Dec 2009 20:03:37 +0000 Subject: [PATCH] 296734: various optimizations for LTW and aop.xml include/exclude processing --- loadtime/.classpath | 1 + .../loadtime/ClassLoaderWeavingAdaptor.java | 271 ++++++++--- .../loadtime/ConcreteAspectCodeGen.java | 11 +- .../ClassLoaderWeavingAdaptorTest.java | 456 +++++++++++++++++- 4 files changed, 666 insertions(+), 73 deletions(-) diff --git a/loadtime/.classpath b/loadtime/.classpath index 2d821b9df..c4d795a66 100644 --- a/loadtime/.classpath +++ b/loadtime/.classpath @@ -14,5 +14,6 @@ + diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java b/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java index b8dc0ff1d..694e7afb5 100644 --- a/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java +++ b/loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java @@ -63,10 +63,21 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { private List m_dumpTypePattern = new ArrayList(); private boolean m_dumpBefore = false; private boolean dumpDirPerClassloader = false; - private List m_includeTypePattern = new ArrayList(); - private List m_excludeTypePattern = new ArrayList(); - private List m_includeStartsWith = new ArrayList(); - private List m_excludeStartsWith = new ArrayList(); + + private boolean hasExcludes = false; + private List excludeTypePattern = new ArrayList(); // anything + private List excludeStartsWith = new ArrayList(); // com.foo..* + private List excludeStarDotDotStar = new ArrayList(); // *..*CGLIB* + private List excludeExactName = new ArrayList(); // com.foo.Bar + private List excludeEndsWith = new ArrayList(); // com.foo.Bar + private List excludeSpecial = new ArrayList(); + + private boolean hasIncludes = false; + private List includeTypePattern = new ArrayList(); + private List m_includeStartsWith = new ArrayList(); + private List includeExactName = new ArrayList(); + private boolean includeStar = false; + private List m_aspectExcludeTypePattern = new ArrayList(); private List m_aspectExcludeStartsWith = new ArrayList(); private List m_aspectIncludeTypePattern = new ArrayList(); @@ -135,7 +146,6 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { } boolean success = true; - // if (trace.isTraceEnabled()) trace.enter("initialize",this,new Object[] { classLoader, context }); this.weavingContext = context; if (weavingContext == null) { @@ -155,9 +165,8 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { return; } - bcelWorld = new LTWWorld(classLoader, weavingContext, // TODO when the world works in terms of the context, we can remove - // the loader... - getMessageHandler(), null); + // TODO when the world works in terms of the context, we can remove the loader + bcelWorld = new LTWWorld(classLoader, weavingContext, getMessageHandler(), null); weaver = new BcelWeaver(bcelWorld); @@ -371,6 +380,9 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { bcelWorld.getLint().loadDefaultProperties(); } else { bcelWorld.getLint().setAll(weaverOption.lint); + if (weaverOption.lint.equals("ignore")) { + bcelWorld.setAllLintIgnored(); + } } } // TODO proceedOnError option @@ -474,7 +486,8 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { break; } - ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes())); + ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes()), + true); concreteAspects.add(gen); @@ -534,7 +547,7 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { } /** - * Register the include / exclude filters We duplicate simple patterns in startWith filters that will allow faster matching + * Register the include / exclude filters. We duplicate simple patterns in startWith filters that will allow faster matching * without ResolvedType * * @param weaver @@ -546,38 +559,120 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { for (Iterator iterator = definitions.iterator(); iterator.hasNext();) { Definition definition = (Definition) iterator.next(); for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) { + hasIncludes = true; String include = (String) iterator1.next(); - TypePattern includePattern = new PatternParser(include).parseTypePattern(); - m_includeTypePattern.add(includePattern); fastMatchInfo = looksLikeStartsWith(include); if (fastMatchInfo != null) { m_includeStartsWith.add(fastMatchInfo); + } else if (include.equals("*")) { + includeStar = true; + } else if ((fastMatchInfo = looksLikeExactName(include)) != null) { + includeExactName.add(fastMatchInfo); + } else { + TypePattern includePattern = new PatternParser(include).parseTypePattern(); + includeTypePattern.add(includePattern); } } for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) { + hasExcludes = true; String exclude = (String) iterator1.next(); - TypePattern excludePattern = new PatternParser(exclude).parseTypePattern(); - m_excludeTypePattern.add(excludePattern); fastMatchInfo = looksLikeStartsWith(exclude); if (fastMatchInfo != null) { - m_excludeStartsWith.add(fastMatchInfo); + excludeStartsWith.add(fastMatchInfo); + } else if ((fastMatchInfo = looksLikeStarDotDotStarExclude(exclude)) != null) { + excludeStarDotDotStar.add(fastMatchInfo); + } else if ((fastMatchInfo = looksLikeExactName(exclude)) != null) { + excludeExactName.add(exclude); + } else if ((fastMatchInfo = looksLikeEndsWith(exclude)) != null) { + excludeEndsWith.add(fastMatchInfo); + } else if (exclude + .equals("org.codehaus.groovy..* && !org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController*")) { + // TODO need a more sophisticated analysis here, to allow for similar situations + excludeSpecial.add(new String[] { "org.codehaus.groovy.", + "org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController" }); + // for the related test: + // } else if (exclude.equals("testdata..* && !testdata.sub.Oran*")) { + // excludeSpecial.add(new String[] { "testdata.", "testdata.sub.Oran" }); + } else { + TypePattern excludePattern = new PatternParser(exclude).parseTypePattern(); + excludeTypePattern.add(excludePattern); } } } } /** - * Checks if the type pattern can be handled as a startswith check - * - * TODO AV - enhance to support "char.sss" ie FQN direclty (match iff equals) we could also add support for "*..*charss" - * endsWith style? + * Checks if the pattern looks like "*..*XXXX*" and if so returns XXXX. This will enable fast name matching of CGLIB exclusion * - * @param typePattern - * @return null if not possible, or the startWith sequence to test against + */ + private String looksLikeStarDotDotStarExclude(String typePattern) { + if (!typePattern.startsWith("*..*")) { + return null; + } + if (!typePattern.endsWith("*")) { + return null; + } + String subPattern = typePattern.substring(4, typePattern.length() - 1); + if (hasStarDot(subPattern, 0)) { + return null; + } + return subPattern.replace('$', '.'); + } + + /** + * Checks if the pattern looks like "com.foo.Bar" - an exact name + */ + private String looksLikeExactName(String typePattern) { + if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.indexOf("*") != -1) { + return null; + } + return typePattern.replace('$', '.'); + } + + /** + * Checks if the pattern looks like "*Exception" + */ + private String looksLikeEndsWith(String typePattern) { + if (typePattern.charAt(0) != '*') { + return null; + } + if (hasSpaceAnnotationPlus(typePattern, 1) || hasStarDot(typePattern, 1)) { + return null; + } + return typePattern.substring(1).replace('$', '.'); + } + + /** + * Determine if something in the string is going to affect our ability to optimize. Checks for: ' ' '@' '+' + */ + private boolean hasSpaceAnnotationPlus(String string, int pos) { + for (int i = pos, max = string.length(); i < max; i++) { + char ch = string.charAt(i); + if (ch == ' ' || ch == '@' || ch == '+') { + return true; + } + } + return false; + } + + /** + * Determine if something in the string is going to affect our ability to optimize. Checks for: '*' '.' + */ + private boolean hasStarDot(String string, int pos) { + for (int i = pos, max = string.length(); i < max; i++) { + char ch = string.charAt(i); + if (ch == '*' || ch == '.') { + return true; + } + } + return false; + } + + /** + * Checks if the type pattern looks like "com.foo..*" */ private String looksLikeStartsWith(String typePattern) { - if (typePattern.indexOf('@') >= 0 || typePattern.indexOf('+') >= 0 || typePattern.indexOf(' ') >= 0 - || typePattern.charAt(typePattern.length() - 1) != '*') { + if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.charAt(typePattern.length() - 1) != '*') { return null; } // now must looks like with "charsss..*" or "cha.rss..*" etc @@ -586,9 +681,8 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { int length = typePattern.length(); if (typePattern.endsWith("..*") && length > 3) { if (typePattern.indexOf("..") == length - 3 // no ".." before last sequence - && typePattern.indexOf('*') == length - 1) { // no "*" before last sequence - return typePattern.substring(0, length - 2).replace('$', '.'); - // ie "charsss." or "char.rss." etc + && typePattern.indexOf('*') == length - 1) { // no earlier '*' + return typePattern.substring(0, length - 2).replace('$', '.'); // "charsss." or "char.rss." etc } } return null; @@ -618,67 +712,120 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor { } } + /** + * Determine whether a type should be accepted for weaving, by checking it against any includes/excludes. + * + * @param className the name of the type to possibly accept + * @param bytes the bytecode for the type (in case we need to look inside, eg. annotations) + * @return true if it should be accepted for weaving + */ @Override protected boolean accept(String className, byte[] bytes) { - // avoid ResolvedType if not needed - if (m_excludeTypePattern.isEmpty() && m_includeTypePattern.isEmpty()) { + + if (!hasExcludes && !hasIncludes) { return true; } // still try to avoid ResolvedType if we have simple patterns String fastClassName = className.replace('/', '.').replace('$', '.'); - for (int i = 0; i < m_excludeStartsWith.size(); i++) { - if (fastClassName.startsWith((String) m_excludeStartsWith.get(i))) { + for (String excludeStartsWithString : excludeStartsWith) { + if (fastClassName.startsWith(excludeStartsWithString)) { return false; } } + // Fast exclusion of patterns like: "*..*CGLIB*" + if (!excludeStarDotDotStar.isEmpty()) { + for (String namePiece : excludeStarDotDotStar) { + int index = fastClassName.lastIndexOf('.'); + if (fastClassName.indexOf(namePiece, index + 1) != -1) { + return false; + } + } + } + + if (!excludeEndsWith.isEmpty()) { + for (String lastPiece : excludeEndsWith) { + if (fastClassName.endsWith(lastPiece)) { + return false; + } + } + } + + // Fast exclusion of exact names + if (!excludeExactName.isEmpty()) { + for (String name : excludeExactName) { + if (fastClassName.equals(name)) { + return false; + } + } + } + + if (!excludeSpecial.isEmpty()) { + for (String[] entry : excludeSpecial) { + String excludeThese = entry[0]; + String exceptThese = entry[1]; + if (fastClassName.startsWith(excludeThese) && !fastClassName.startsWith(exceptThese)) { + return false; + } + } + } + /* * Bug 120363 If we have an exclude pattern that cannot be matched using "starts with" then we cannot fast accept */ - if (m_excludeTypePattern.isEmpty()) { + boolean didSomeIncludeMatching = false; + if (excludeTypePattern.isEmpty()) { + if (includeStar) { + return true; + } + if (!includeExactName.isEmpty()) { + didSomeIncludeMatching = true; + for (String exactname : includeExactName) { + if (fastClassName.equals(exactname)) { + return true; + } + } + } boolean fastAccept = false;// defaults to false if no fast include for (int i = 0; i < m_includeStartsWith.size(); i++) { - fastAccept = fastClassName.startsWith((String) m_includeStartsWith.get(i)); + didSomeIncludeMatching = true; + fastAccept = fastClassName.startsWith(m_includeStartsWith.get(i)); if (fastAccept) { - break; + return true; } } + // We may have processed all patterns now... check that and return + if (includeTypePattern.isEmpty()) { + return !didSomeIncludeMatching; + } } - // needs further analysis - // TODO AV - needs refactoring - // during LTW this calling resolve at that stage is BAD as we do have the bytecode from the classloader hook - // but still go thru resolve that will do a getResourcesAsStream on disk - // this is also problematic for jit stub which are not on disk - as often underlying infra - // does returns null or some other info for getResourceAsStream (f.e. WLS 9 CR248491) - // Instead I parse the given bytecode. But this also means it will be parsed again in - // new WeavingClassFileProvider() from WeavingAdaptor.getWovenBytes()... - - ensureDelegateInitialized(className, bytes); - ResolvedType classInfo = delegateForCurrentClass.getResolvedTypeX();// BAD: - // weaver.getWorld().resolve(UnresolvedType.forName( - // className), true); + boolean accept; + try { + ensureDelegateInitialized(className, bytes); - // exclude are "AND"ed - for (Iterator iterator = m_excludeTypePattern.iterator(); iterator.hasNext();) { - TypePattern typePattern = (TypePattern) iterator.next(); - if (typePattern.matchesStatically(classInfo)) { - // exclude match - skip - return false; + ResolvedType classInfo = delegateForCurrentClass.getResolvedTypeX(); + + // exclude are "AND"ed + for (TypePattern typePattern : excludeTypePattern) { + if (typePattern.matchesStatically(classInfo)) { + // exclude match - skip + return false; + } } - } - // include are "OR"ed - boolean accept = true;// defaults to true if no include - for (Iterator iterator = m_includeTypePattern.iterator(); iterator.hasNext();) { - TypePattern typePattern = (TypePattern) iterator.next(); - accept = typePattern.matchesStatically(classInfo); - if (accept) { - break; + // include are "OR"ed + accept = !didSomeIncludeMatching; // only true if no includes at all + for (TypePattern typePattern : includeTypePattern) { + accept = typePattern.matchesStatically(classInfo); + if (accept) { + break; + } + // goes on if this include did not match ("OR"ed) } - // goes on if this include did not match ("OR"ed) + } finally { + this.bcelWorld.demote(); } - this.bcelWorld.demote(); return accept; } diff --git a/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java b/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java index 18176f0e2..41b9b4ca3 100644 --- a/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java +++ b/loadtime/src/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java @@ -270,12 +270,14 @@ public class ConcreteAspectCodeGen { // hierarchy that have not been // concretized. private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map collector) { - if (type == null) + if (type == null) { return; + } // Get to the top if (!type.equals(ResolvedType.OBJECT)) { - if (type.getSuperclass() != null) + if (type.getSuperclass() != null) { getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector); + } } ResolvedMember[] rms = type.getDeclaredMethods(); if (rms != null) { @@ -309,8 +311,9 @@ public class ConcreteAspectCodeGen { private boolean hasPointcutAnnotation(ResolvedMember member) { AnnotationAJ[] as = member.getAnnotations(); - if (as == null || as.length == 0) + if (as == null || as.length == 0) { return false; + } for (int i = 0; i < as.length; i++) { if (as[i].getTypeSignature().equals("Lorg/aspectj/lang/annotation/Pointcut;")) { return true; @@ -440,7 +443,7 @@ public class ConcreteAspectCodeGen { // register the fresh new class into the world repository as it does not // exist on the classpath anywhere JavaClass jc = cg.getJavaClass((BcelWorld) world); - ((BcelWorld) world).addSourceObjectType(jc); + ((BcelWorld) world).addSourceObjectType(jc, true); return jc.getBytes(); } diff --git a/loadtime/testsrc/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptorTest.java b/loadtime/testsrc/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptorTest.java index 805b989a6..58166e20c 100644 --- a/loadtime/testsrc/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptorTest.java +++ b/loadtime/testsrc/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptorTest.java @@ -10,42 +10,484 @@ *******************************************************************************/ package org.aspectj.weaver.loadtime; +import java.io.File; +import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import junit.framework.TestCase; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.weaver.World; +import org.aspectj.weaver.World.TypeMap; +import org.aspectj.weaver.bcel.BcelWorld; +import org.aspectj.weaver.loadtime.definition.Definition; +import org.aspectj.weaver.tools.WeavingAdaptor; + public class ClassLoaderWeavingAdaptorTest extends TestCase { public void testClassLoaderWeavingAdaptor() { ClassLoader loader = new URLClassLoader(new URL[] {}, null); ClassLoaderWeavingAdaptor adaptor = new ClassLoaderWeavingAdaptor(); - adaptor.initialize(loader,null); + adaptor.initialize(loader, null); } public void testGetNamespace() { ClassLoader loader = new URLClassLoader(new URL[] {}, null); ClassLoaderWeavingAdaptor adaptor = new ClassLoaderWeavingAdaptor(); - adaptor.initialize(loader,null); + adaptor.initialize(loader, null); String namespace = adaptor.getNamespace(); - assertEquals("Namespace should be empty","",namespace); + assertEquals("Namespace should be empty", "", namespace); } public void testGeneratedClassesExistFor() { ClassLoader loader = new URLClassLoader(new URL[] {}, null); ClassLoaderWeavingAdaptor adaptor = new ClassLoaderWeavingAdaptor(); - adaptor.initialize(loader,null); + adaptor.initialize(loader, null); boolean exist = adaptor.generatedClassesExistFor("Junk"); - assertFalse("There should be no generated classes",exist); + assertFalse("There should be no generated classes", exist); } public void testFlushGeneratedClasses() { ClassLoader loader = new URLClassLoader(new URL[] {}, null); ClassLoaderWeavingAdaptor adaptor = new ClassLoaderWeavingAdaptor(); - adaptor.initialize(loader,null); + adaptor.initialize(loader, null); adaptor.flushGeneratedClasses(); boolean exist = adaptor.generatedClassesExistFor("Junk"); - assertFalse("There should be no generated classes",exist); + assertFalse("There should be no generated classes", exist); + } + + /** + * Testing fast excludes of the pattern "com.foo..*". World should not have any new types in it after rejection. + */ + public void testFastExclusionOne() throws Exception { + TestClassLoaderWeavingAdaptor adaptor = getAdaptor(null, "testdata..*"); + + String orangesSub = "testdata.sub.Oranges"; + JavaClass orangesClass = getClassFrom(orangesSub); + byte[] orangesBytes = orangesClass.getBytes(); + + boolean accepted = adaptor.accept(orangesSub, orangesBytes); + assertFalse("Should not be accepted", accepted); + TypeMap map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + // Important thing here is that the rejection of testdata.sub.Oranges did not require it to be loaded into the world at all + } + + /** + * Testing fast includes of the pattern "*". World should not have any new types in it after inclusion. + */ + public void testFastInclusionOne() throws Exception { + TestClassLoaderWeavingAdaptor adaptor = getAdaptor("*", null); + + String orangesSub = "testdata.sub.Oranges"; + JavaClass orangesClass = getClassFrom(orangesSub); + byte[] orangesBytes = orangesClass.getBytes(); + + boolean accepted = adaptor.accept(orangesSub, orangesBytes); + assertTrue("Should be accepted", accepted); + TypeMap map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + // Important thing here is that the rejection of testdata.sub.Oranges did not require it to be loaded into the world at all + } + + /** + * Testing fast excludes of the pattern "*Oranges". World should not have any new types in it after rejection. + */ + public void testFastExclusionTwo() throws Exception { + TestClassLoaderWeavingAdaptor adaptor = getAdaptor(null, "*Oranges"); + + String oranges = "testdata.Oranges"; + JavaClass orangesClass = getClassFrom(oranges); + byte[] orangesBytes = orangesClass.getBytes(); + + boolean accepted = adaptor.accept(oranges, orangesBytes); + assertFalse("Should not be accepted", accepted); + TypeMap map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + String orangesSub = "testdata.sub.Oranges"; + + JavaClass orangesSubClass = getClassFrom(orangesSub); + byte[] orangesSubBytes = orangesSubClass.getBytes(); + + accepted = adaptor.accept(orangesSub, orangesSubBytes); + assertFalse("Should not be accepted", accepted); + map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + } + + /** + * Testing fast excludes of the pattern "*..*Oranges*". World should not have any new types in it after rejection. + */ + public void testFastExclusionThree() throws Exception { + TestClassLoaderWeavingAdaptor adaptor = getAdaptor(null, "*..*ran*"); + + String oranges = "testdata.Oranges"; + JavaClass orangesClass = getClassFrom(oranges); + byte[] orangesBytes = orangesClass.getBytes(); + + boolean accepted = adaptor.accept(oranges, orangesBytes); + assertFalse("Should not be accepted", accepted); + TypeMap map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + String orangesSub = "testdata.sub.Oranges"; + JavaClass orangesSubClass = getClassFrom(orangesSub); + byte[] orangesSubBytes = orangesSubClass.getBytes(); + + accepted = adaptor.accept(orangesSub, orangesSubBytes); + assertFalse("Should not be accepted", accepted); + map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + String apples = "testdata.Apples"; + JavaClass applesClass = getClassFrom(apples); + byte[] applesBytes = applesClass.getBytes(); + + accepted = adaptor.accept(apples, applesBytes); + assertTrue("Should be accepted", accepted); + map = accessTypeMap(adaptor); + + // The aspect and the Apples type + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + } + + /** + * Testing fast inclusion checking of exact include names eg. "testdata.sub.Oranges" + */ + public void testFastInclusionTwo() throws Exception { + TestClassLoaderWeavingAdaptor adaptor = getAdaptor("testdata.sub.Oranges", null); + + String oranges = "testdata.Oranges"; + JavaClass orangesClass = getClassFrom(oranges); + byte[] orangesBytes = orangesClass.getBytes(); + + boolean accepted = adaptor.accept(oranges, orangesBytes); + assertFalse("Should not be accepted", accepted); + TypeMap map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + String orangesSub = "testdata.sub.Oranges"; + JavaClass orangesSubClass = getClassFrom(orangesSub); + byte[] orangesSubBytes = orangesSubClass.getBytes(); + + accepted = adaptor.accept(orangesSub, orangesSubBytes); + assertTrue("Should be accepted", accepted); + map = accessTypeMap(adaptor); + + // The aspect + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); + + String apples = "testdata.Apples"; + JavaClass applesClass = getClassFrom(apples); + byte[] applesBytes = applesClass.getBytes(); + + accepted = adaptor.accept(apples, applesBytes); + assertFalse("Should not be accepted", accepted); + map = accessTypeMap(adaptor); + + // The aspect and the Apples type + assertEquals(1, map.getExpendableMap().size()); + // primitives, void and jlObject + assertEquals(10, map.getMainMap().size()); } + /** + * Testing fast excludes of the pattern groovy related pattern - + */ + // public void testFastExclusionFour() throws Exception { + // TestClassLoaderWeavingAdaptor adaptor = getAdaptor("*", "testdata..* && !testdata.sub.Oran*"); + // + // String oranges = "testdata.Oranges"; + // JavaClass orangesClass = getClassFrom(oranges); + // byte[] orangesBytes = orangesClass.getBytes(); + // + // boolean accepted = adaptor.accept(oranges, orangesBytes); + // assertFalse("Should not be accepted", accepted); + // TypeMap map = accessTypeMap(adaptor); + // + // // The aspect + // assertEquals(1, map.getExpendableMap().size()); + // + // // primitives, void and jlObject + // assertEquals(10, map.getMainMap().size()); + // + // String orangesSub = "testdata.sub.Oranges"; + // JavaClass orangesSubClass = getClassFrom(orangesSub); + // byte[] orangesSubBytes = orangesSubClass.getBytes(); + // + // accepted = adaptor.accept(orangesSub, orangesSubBytes); + // assertTrue("Should be accepted", accepted); + // map = accessTypeMap(adaptor); + // + // // The aspect + // assertEquals(1, map.getExpendableMap().size()); + // // primitives, void and jlObject + // assertEquals(10, map.getMainMap().size()); + // } + + public void testAcceptanceSpeedStarDotDotStar() throws Exception { + URLClassLoader loader = new URLClassLoader(new URL[] { new File("../loadtime/bin").toURI().toURL() }, null); + + JavaClass jc = getClassFrom("../loadtime/bin", "org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOne"); + byte[] bs = jc.getBytes(); + jc = getClassFrom("../loadtime/bin", "org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOneCGLIB"); + byte[] bs2 = jc.getBytes(); + // InputStream is = loader.getResourceAsStream("org.aspectj.weaver.loadtime.ClassLoaderWeaverAdaptorTests$TestOne"); + assertNotNull(bs); + TestWeavingContext wc = new TestWeavingContext(loader); + Definition d = new Definition(); + d.getExcludePatterns().add("*..*CGLIB*"); + d.getAspectClassNames().add("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$AnAspect"); + wc.addDefinition(d); + ClassLoaderWeavingAdaptor adaptor = new ClassLoaderWeavingAdaptor(); + adaptor.initialize(loader, wc); + boolean exist = adaptor.generatedClassesExistFor("Junk"); + assertFalse("There should be no generated classes", exist); + + // before: + // Acceptance 331ms + // Rejection 3368ms + + // after: + // Acceptance 343ms + // Rejection 80ms + + long stime = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + boolean b = adaptor.accept("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOne", bs); + assertTrue("Should be accepted", b); + } + long etime = System.currentTimeMillis(); + System.out.println("Acceptance " + (etime - stime) + "ms"); + stime = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + adaptor.delegateForCurrentClass = null; + boolean b = adaptor.accept("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOneCGLIB", bs2); + assertFalse("Should not be accepting CGLIB", b); + } + etime = System.currentTimeMillis(); + System.out.println("Rejection " + (etime - stime) + "ms"); + + } + + // TODO + // shouldn't add it to the type patterns if we are going to fast handle it + // include for exact name, what does it mean? + // excludes="!xxxx" should also be fast matched... + + public void testAcceptanceSpeedExactName() throws Exception { + URLClassLoader loader = new URLClassLoader(new URL[] { new File("../loadtime/bin").toURI().toURL() }, null); + + JavaClass jc = getClassFrom("../loadtime/bin", "org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOne"); + byte[] bs = jc.getBytes(); + jc = getClassFrom("../loadtime/bin", "org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOneCGLIB"); + byte[] bs2 = jc.getBytes(); + // InputStream is = loader.getResourceAsStream("org.aspectj.weaver.loadtime.ClassLoaderWeaverAdaptorTests$TestOne"); + assertNotNull(bs); + TestWeavingContext wc = new TestWeavingContext(loader); + Definition d = new Definition(); + d.getExcludePatterns().add("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest.TestOneCGLIB"); + d.getAspectClassNames().add("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$AnAspect"); + wc.addDefinition(d); + TestClassLoaderWeavingAdaptor adaptor = new TestClassLoaderWeavingAdaptor(); + adaptor.initialize(loader, wc); + boolean exist = adaptor.generatedClassesExistFor("Junk"); + assertFalse("There should be no generated classes", exist); + + // before: + // Acceptance 331ms + // Rejection 3160ms + + // after: + // Acceptance 379ms + // Rejection 103ms + + long stime = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + boolean b = adaptor.accept("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOne", bs); + assertTrue("Should be accepted", b); + } + long etime = System.currentTimeMillis(); + System.out.println("Acceptance " + (etime - stime) + "ms"); + stime = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + adaptor.delegateForCurrentClass = null; + boolean b = adaptor.accept("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$TestOneCGLIB", bs2); + assertFalse("Should not be accepting CGLIB", b); + } + etime = System.currentTimeMillis(); + System.out.println("Rejection " + (etime - stime) + "ms"); + BcelWorld world = adaptor.getWorld(); + Field f = World.class.getDeclaredField("typeMap"); + f.setAccessible(true); + TypeMap typeMap = (TypeMap) f.get(world); + System.out.println(typeMap.getExpendableMap().size()); + System.out.println(typeMap.getMainMap().size()); + printExpendableMap(typeMap.getExpendableMap()); + printMainMap(typeMap.getMainMap()); + } + + // --- infrastructure --- + + private TypeMap accessTypeMap(TestClassLoaderWeavingAdaptor adaptor) { + try { + BcelWorld world = adaptor.getWorld(); + Field f = World.class.getDeclaredField("typeMap"); + f.setAccessible(true); + TypeMap typeMap = (TypeMap) f.get(world); + return typeMap; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public TestClassLoaderWeavingAdaptor getAdaptor(String includePattern, String excludePattern) { + return getAdaptor(includePattern == null ? null : new String[] { includePattern }, excludePattern == null ? null + : new String[] { excludePattern }); + } + + public TestClassLoaderWeavingAdaptor getAdaptor(String[] includePatterns, String[] excludePatterns) { + try { + URLClassLoader loader = new URLClassLoader(new URL[] { new File("../loadtime/bin").toURI().toURL() }, null); + TestWeavingContext wc = new TestWeavingContext(loader); + Definition d = new Definition(); + if (includePatterns != null) { + for (String s : includePatterns) { + d.getIncludePatterns().add(s); + } + } + if (excludePatterns != null) { + for (String s : excludePatterns) { + d.getExcludePatterns().add(s); + } + } + // need some random aspect or the weaver will shut down! + d.getAspectClassNames().add("org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptorTest$AnAspect"); + wc.addDefinition(d); + TestClassLoaderWeavingAdaptor adaptor = new TestClassLoaderWeavingAdaptor(); + adaptor.initialize(loader, wc); + return adaptor; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void printMaps(TypeMap map) { + printExpendableMap(map.getExpendableMap()); + printMainMap(map.getMainMap()); + } + + private void printExpendableMap(Map m) { + for (Object o : m.keySet()) { + String sig = (String) o; + System.out.println(sig + "=" + m.get(sig)); + } + } + + private void printMainMap(Map m) { + for (Object o : m.keySet()) { + String sig = (String) o; + System.out.println(sig + "=" + m.get(sig)); + } + } + + static class TestClassLoaderWeavingAdaptor extends ClassLoaderWeavingAdaptor { + + public BcelWorld getWorld() { + return bcelWorld; + } + } + + public static JavaClass getClassFrom(String clazzname) throws ClassNotFoundException { + return getClassFrom("../loadtime/bin", clazzname); + } + + public static JavaClass getClassFrom(String frompath, String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos(frompath); + return repos.loadClass(clazzname); + } + + public static SyntheticRepository createRepos(String cpentry) { + ClassPath cp = new ClassPath(cpentry + File.pathSeparator + System.getProperty("java.class.path")); + return SyntheticRepository.getInstance(cp); + } + + @Aspect + class AnAspect { + + } + + class TestOne { + + } + + class TestOneCGLIB { + + } + + static class TestWeavingContext extends DefaultWeavingContext { + + List testList = new ArrayList(); + + public TestWeavingContext(ClassLoader loader) { + super(loader); + } + + public void addDefinition(Definition d) { + testList.add(d); + } + + @Override + public List getDefinitions(final ClassLoader loader, final WeavingAdaptor adaptor) { + return testList; + } + } } + +// --- testclasses and aspects --- \ No newline at end of file -- 2.39.5