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)
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;
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;
}
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);
+ }
}
}
}
/**
* 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
*
}
}
- 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();
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();) {
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;
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 {}
}
}
+ 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;
<?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"/>
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;
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;
*
* @param aspect
* @param kind
- * @return
+ * @return munger
*/
public ConcreteTypeMunger makePerClauseAspect(ResolvedType aspect, PerClause.Kind kind) {
return new BcelPerClauseAspectAdder(aspect, kind);
// 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;
+ }
+ }
}
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;
* @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);
* @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;
}
* (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);
}
/**