]> source.dussan.org Git - aspectj.git/commitdiff
detects starttWith pattern in aop.xml include/exclude and do fast match
authoravasseur <avasseur>
Wed, 26 Oct 2005 12:43:37 +0000 (12:43 +0000)
committeravasseur <avasseur>
Wed, 26 Oct 2005 12:43:37 +0000 (12:43 +0000)
avoid type resolve to do aop.xml include/exclude regular match but use the passed in bytecode (else issue with Stubs not on disk as f.e. in WLS)

loadtime/src/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java
tests/java5/ataspectj/ataspectj/UnweavableTest.java
tests/java5/ataspectj/ataspectj/aop-unweavabletest.xml
weaver/src/org/aspectj/weaver/bcel/BcelWorld.java
weaver/src/org/aspectj/weaver/tools/WeavingAdaptor.java

index cdccc0e09e1ffa7f2cc8cc42d47913e0c9445503..3c36bee68749a948819af47bd83c4cc40cd2dd6f 100644 (file)
@@ -23,6 +23,8 @@ import org.aspectj.weaver.UnresolvedType;
 import org.aspectj.weaver.World;
 import org.aspectj.weaver.bcel.BcelWeaver;
 import org.aspectj.weaver.bcel.BcelWorld;
+import org.aspectj.weaver.bcel.Utility;
+import org.aspectj.weaver.bcel.BcelObjectType;
 import org.aspectj.weaver.loadtime.definition.Definition;
 import org.aspectj.weaver.loadtime.definition.DocumentParser;
 import org.aspectj.weaver.patterns.PatternParser;
@@ -52,8 +54,11 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
     private List m_dumpTypePattern = new ArrayList();
     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 List m_aspectExcludeTypePattern = new ArrayList();
-    
+    private List m_aspectExcludeStartsWith = new ArrayList();
+
     private StringBuffer namespace;
     private IWeavingContext weavingContext;
 
@@ -235,12 +240,17 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
     }
 
     private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        String fastMatchInfo = null;
         for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
             Definition definition = (Definition) iterator.next();
             for (Iterator iterator1 = definition.getAspectExcludePatterns().iterator(); iterator1.hasNext();) {
                 String exclude = (String) iterator1.next();
                 TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
                 m_aspectExcludeTypePattern.add(excludePattern);
+                fastMatchInfo = looksLikeStartsWith(exclude);
+                if (fastMatchInfo != null) {
+                    m_aspectExcludeStartsWith.add(fastMatchInfo);
+                }
             }
         }
     }
@@ -314,27 +324,67 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
 
     /**
      * Register the include / exclude filters
+     * We duplicate simple patterns in startWith filters that will allow faster matching without ResolvedType
      *
      * @param weaver
      * @param loader
      * @param definitions
      */
     private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
+        String fastMatchInfo = null;
         for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
             Definition definition = (Definition) iterator.next();
             for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) {
                 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);
+                }
             }
             for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) {
                 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);
+                }
             }
         }
     }
 
+    /**
+     * 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?
+     *
+     * @param typePattern
+     * @return null if not possible, or the startWith sequence to test against
+     */
+    private String looksLikeStartsWith(String typePattern) {
+        if (typePattern.indexOf('@') >= 0
+            || typePattern.indexOf('+') >= 0
+            || typePattern.indexOf(' ') >= 0
+            || typePattern.charAt(typePattern.length()-1) != '*') {
+            return null;
+        }
+        // now must looks like with "charsss..*" or "cha.rss..*" etc
+        // note that "*" and "*..*" won't be fast matched
+        // and that "charsss.*" will not neither
+        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
+            }
+        }
+        return null;
+    }
+
     /**
      * Register the dump filter
      *
@@ -353,13 +403,41 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
         }
     }
 
-    protected boolean accept(String className) {
+    protected boolean accept(String className, byte[] bytes) {
         // avoid ResolvedType if not needed
         if (m_excludeTypePattern.isEmpty() && m_includeTypePattern.isEmpty()) {
             return true;
         }
-        //TODO AV - optimize for className.startWith only
-        ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(className), 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))) {
+                return false;
+            }
+        }
+        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));
+            if (fastAccept) {
+                break;
+            }
+        }
+        if (fastAccept) {
+            return true;
+        }
+
+        // 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()...
+        BcelObjectType bct = ((BcelWorld)weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(null, bytes));
+        ResolvedType classInfo = bct.getResolvedTypeX();//BAD: weaver.getWorld().resolve(UnresolvedType.forName(className), true);
+
         //exclude are "AND"ed
         for (Iterator iterator = m_excludeTypePattern.iterator(); iterator.hasNext();) {
             TypePattern typePattern = (TypePattern) iterator.next();
@@ -386,7 +464,16 @@ public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
         if (m_aspectExcludeTypePattern.isEmpty()) {
             return true;
         }
-        //TODO AV - optimize for className.startWith only
+
+        // still try to avoid ResolvedType if we have simple patterns
+        String fastClassName = aspectClassName.replace('/', '.').replace('.', '$');
+        for (int i = 0; i < m_aspectExcludeStartsWith.size(); i++) {
+            if (fastClassName.startsWith((String)m_aspectExcludeStartsWith.get(i))) {
+                return false;
+            }
+        }
+
+        // needs further analysis
         ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(aspectClassName), true);
         //exclude are "AND"ed
         for (Iterator iterator = m_aspectExcludeTypePattern.iterator(); iterator.hasNext();) {
index 4ce689723f56b43d3e985744eb4074d48e00e2fd..a731bb886f0f85d475be981f2d72bf3bd4604780 100644 (file)
@@ -19,6 +19,7 @@ import java.lang.reflect.Method;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.ProtectionDomain;
+import java.io.Serializable;
 
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
@@ -76,6 +77,12 @@ public class UnweavableTest extends TestCase {
         assertEquals(1, TestAspect2.I);
     }
 
+    public void testJitNotMatched() {
+        // just load a jit to make sure the weaver does not complains for classes coming from nowhere
+        Serializable serial = getJitNoMatch();
+        assertEquals(0, serial.getClass().getDeclaredMethods().length);
+    }
+
     @Retention(RetentionPolicy.RUNTIME)
     static @interface ASome {}
 
@@ -108,6 +115,29 @@ public class UnweavableTest extends TestCase {
         }
     }
 
+    Serializable getJitNoMatch() {
+        ClassWriter cw = new ClassWriter(true, true);
+        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "ataspectj/unmatched/Gen", null, "java/lang/Object", new String[]{"java/io/Serializable"});
+
+        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, new String[0]);
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+        mv.visitInsn(Opcodes.RETURN);
+        mv.visitMaxs(0, 0);
+        cw.visitEnd();
+
+        try {
+            ClassLoader loader = this.getClass().getClassLoader();
+            Method def = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
+            def.setAccessible(true);
+            Class gen = (Class) def.invoke(loader, "ataspectj.unmatched.Gen", cw.toByteArray(), 0, cw.toByteArray().length);
+            return (Serializable) gen.newInstance();
+        } catch (Throwable t) {
+            fail(t.toString());
+            return null;
+        }
+    }
+
     @Aspect
     public static class TestAspect2 {
         static int I = 0;
index 7ed6d2905064bf7d8662cb5fae4b9eb8580989c3..2cb2c62c781af6182f6b77dc37a049d4fbfe63a1 100644 (file)
@@ -1,6 +1,11 @@
 <?xml version="1.0"?>
 <aspectj>
-    <weaver options="-verbose -showWeaveInfo"/>
+    <weaver options="-verbose -showWeaveInfo">
+        <!-- the pattern below will be fastmatched and it will exclude a jit class -->
+        <!-- as a consequence we will ask if this class is an @Aspect for special case of aspectof munging -->
+        <!-- and there we will do a raw BCEL parse and @Aspect annotation lookup insteead of going thru type resolve -->
+        <exclude within="someataspectj.unmatched..*"/>
+    </weaver>
     <aspects>
         <aspect name="ataspectj.UnweavableTest.TestAspect"/>
         <aspect name="ataspectj.UnweavableTest.TestAspect2"/>
index 0fa7fea1690e2782d8b505967d81b25711a719cb..0495515b327585a8cda07ab21dd9fd35311ab282 100644 (file)
@@ -16,6 +16,9 @@ package org.aspectj.weaver.bcel;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -25,6 +28,7 @@ import java.util.StringTokenizer;
 import org.aspectj.apache.bcel.classfile.ClassParser;
 import org.aspectj.apache.bcel.classfile.JavaClass;
 import org.aspectj.apache.bcel.classfile.Method;
+import org.aspectj.apache.bcel.classfile.annotation.Annotation;
 import org.aspectj.apache.bcel.generic.ConstantPoolGen;
 import org.aspectj.apache.bcel.generic.FieldInstruction;
 import org.aspectj.apache.bcel.generic.GETSTATIC;
@@ -467,7 +471,7 @@ public class BcelWorld extends World implements Repository {
      *
      * @param aspect
      * @param kind
-     * @return
+     * @return munger
      */
     public ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind) {
         return new BcelPerClauseAspectAdder(aspect, kind);
@@ -541,4 +545,35 @@ public class BcelWorld extends World implements Repository {
 //                     e.printStackTrace();
 //             }
        }
+
+    /**
+     * Checks if given bytecode is an @AspectJ aspect
+     *
+     * @param name
+     * @param bytes
+     * @return true if so
+     */
+    public boolean isAnnotationStyleAspect(String name, byte[] bytes) {
+        try {
+            ClassParser cp = new ClassParser(new ByteArrayInputStream(bytes), null);
+            JavaClass jc = cp.parse();
+            if (!jc.isClass()) {
+                return false;
+            }
+            Annotation anns[] = jc.getAnnotations();
+            if (anns.length == 0) {
+                return false;
+            }
+            for (int i = 0; i < anns.length; i++) {
+                Annotation ann = anns[i];
+                if ("Lorg/aspectj/lang/annotation/Aspect;".equals(ann.getTypeSignature())) {
+                    return true;
+                }
+            }
+            return false;
+        } catch (IOException e) {
+            // assume it is one as a best effort
+            return true;
+        }
+    }
 }
index c98773e3b714ab9cd8684facaaa9e8925c957503..eac30f3836808b28400ce701b1a4e18d8a2b4df3 100644 (file)
@@ -33,8 +33,8 @@ import org.aspectj.bridge.IMessage.Kind;
 import org.aspectj.util.FileUtil;
 import org.aspectj.weaver.IClassFileProvider;
 import org.aspectj.weaver.IWeaveRequestor;
-import org.aspectj.weaver.ResolvedType;
 import org.aspectj.weaver.UnresolvedType;
+import org.aspectj.weaver.ResolvedType;
 import org.aspectj.weaver.bcel.BcelWeaver;
 import org.aspectj.weaver.bcel.BcelWorld;
 import org.aspectj.weaver.bcel.UnwovenClassFile;
@@ -169,11 +169,11 @@ public class WeavingAdaptor {
      * @exception IOException weave failed
         */
        public byte[] weaveClass (String name, byte[] bytes) throws IOException {
-               if (shouldWeave(name)) {
+               if (shouldWeave(name, bytes)) {
             //System.out.println("WeavingAdaptor.weaveClass " + name);
                        info("weaving '" + name + "'");
                        bytes = getWovenBytes(name, bytes);
-               } else if (shouldWeaveAtAspect(name)) {
+               } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) {
             // an @AspectJ aspect needs to be at least munged by the aspectOf munger
             info("weaving '" + name + "'");
             bytes = getAtAspectJAspectBytes(name, bytes);
@@ -186,19 +186,19 @@ public class WeavingAdaptor {
      * @param name
      * @return true if should weave (but maybe we still need to munge it for @AspectJ aspectof support)
      */
-    private boolean shouldWeave (String name) {
+    private boolean shouldWeave (String name, byte[] bytes) {
                name = name.replace('/','.');
                boolean b = enabled && !generatedClasses.containsKey(name) && shouldWeaveName(name);
-        return b && accept(name);
-//        && shouldWeaveAtAspect(name);
-//        // we recall shouldWeaveAtAspect as we need to add aspectOf methods for @Aspect anyway
+        return b && accept(name, bytes);
+//        && shouldWeaveAnnotationStyleAspect(name);
+//        // we recall shouldWeaveAnnotationStyleAspect as we need to add aspectOf methods for @Aspect anyway
 //        //FIXME AV - this is half ok as the aspect will be weaved by others. In theory if the aspect
 //        // is excluded from include/exclude config we should only weave late type mungers for aspectof
-//        return b && (accept(name) || shouldWeaveAtAspect(name));
+//        return b && (accept(name) || shouldWeaveAnnotationStyleAspect(name));
        }
 
     //ATAJ
-    protected boolean accept(String name) {
+    protected boolean accept(String name, byte[] bytes) {
         return true;
     }
 
@@ -220,11 +220,13 @@ public class WeavingAdaptor {
      * (and not part of the source compilation)
      *
      * @param name
+     * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve
      * @return true if @Aspect
      */
-       private boolean shouldWeaveAtAspect(String name) {
-               ResolvedType type = bcelWorld.resolve(UnresolvedType.forName(name), true);
-        return (type == null || type.isAnnotationStyleAspect());
+       private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) {
+               // AV: instead of doing resolve that would lookup stuff on disk thru BCEL ClassLoaderRepository
+        // we reuse bytes[] here to do a fast lookup for @Aspect annotation
+        return bcelWorld.isAnnotationStyleAspect(name, bytes);
        }
 
        /**