aboutsummaryrefslogtreecommitdiffstats
path: root/loadtime/src/main
diff options
context:
space:
mode:
authorAndy Clement <aclement@pivotal.io>2019-01-25 15:49:50 -0800
committerAndy Clement <aclement@pivotal.io>2019-01-25 15:49:50 -0800
commit38a19ea6a0b864447883328fe1103fd991a308f0 (patch)
tree78736cef5e004315a6cdbe4071de92c10c663353 /loadtime/src/main
parentb30dde96344652cf514342b0ca597e37fe9181a0 (diff)
downloadaspectj-38a19ea6a0b864447883328fe1103fd991a308f0.tar.gz
aspectj-38a19ea6a0b864447883328fe1103fd991a308f0.zip
mavenizing loadtime - wip
Diffstat (limited to 'loadtime/src/main')
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/Aj.java375
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java1188
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassPreProcessor.java30
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java990
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultMessageHandler.java81
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultWeavingContext.java137
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/JRockitAgent.java84
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/Options.java180
-rw-r--r--loadtime/src/main/java/org/aspectj/weaver/loadtime/WeavingURLClassLoader.java232
9 files changed, 3297 insertions, 0 deletions
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/Aj.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/Aj.java
new file mode 100644
index 000000000..652ea0932
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/Aj.java
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Alexandre Vasseur initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.aspectj.bridge.context.CompilationAndWeavingContext;
+import org.aspectj.weaver.Dump;
+import org.aspectj.weaver.tools.Trace;
+import org.aspectj.weaver.tools.TraceFactory;
+import org.aspectj.weaver.tools.WeavingAdaptor;
+import org.aspectj.weaver.tools.cache.SimpleCache;
+import org.aspectj.weaver.tools.cache.SimpleCacheFactory;
+
+/**
+ * Adapter between the generic class pre processor interface and the AspectJ weaver Load time weaving consistency relies on
+ * Bcel.setRepository
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class Aj implements ClassPreProcessor {
+
+ private IWeavingContext weavingContext;
+ public static SimpleCache laCache=SimpleCacheFactory.createSimpleCache();
+
+ /**
+ * References are added to this queue when their associated classloader is removed, and once on here that indicates that we
+ * should tidy up the adaptor map and remove the adaptor (weaver) from the map we are maintaining from adaptorkey > adaptor
+ * (weaver)
+ */
+ private static ReferenceQueue adaptorQueue = new ReferenceQueue();
+
+ private static Trace trace = TraceFactory.getTraceFactory().getTrace(Aj.class);
+
+ public Aj() {
+ this(null);
+ }
+
+ public Aj(IWeavingContext context) {
+ if (trace.isTraceEnabled())
+ trace.enter("<init>", this, new Object[] { context, getClass().getClassLoader() });
+ this.weavingContext = context;
+ if (trace.isTraceEnabled())
+ trace.exit("<init>");
+ }
+
+ /**
+ * Initialization
+ */
+ @Override
+ public void initialize() {
+
+ }
+
+ private final static String deleLoader = "sun.reflect.DelegatingClassLoader";
+ private final static String deleLoader2 = "jdk.internal.reflect.DelegatingClassLoader"; // On JDK11+
+
+ @Override
+ public byte[] preProcess(String className, byte[] bytes, ClassLoader loader, ProtectionDomain protectionDomain) {
+ if (loader == null || className == null ||
+ loader.getClass().getName().equals(deleLoader) || loader.getClass().getName().equals(deleLoader2)) {
+ // skip boot loader, null classes (hibernate), or those from a reflection loader
+ return bytes;
+ }
+
+ if (loadersToSkip != null) {
+ // Check whether to reject it
+ if (loadersToSkip.contains(loader.getClass().getName())) {
+// System.out.println("debug: no weaver created for loader '"+loader.getClass().getName()+"'");
+ return bytes;
+ }
+ }
+
+ if (trace.isTraceEnabled())
+ trace.enter("preProcess", this, new Object[] { className, bytes, loader });
+ if (trace.isTraceEnabled())
+ trace.event("preProcess", this, new Object[] { loader.getParent(), Thread.currentThread().getContextClassLoader() });
+
+ try {
+ synchronized (loader) {
+
+ if (SimpleCacheFactory.isEnabled()) {
+ byte[] cacheBytes= laCache.getAndInitialize(className, bytes,loader,protectionDomain);
+ if (cacheBytes!=null){
+ return cacheBytes;
+ }
+ }
+
+ WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(loader, weavingContext);
+ if (weavingAdaptor == null) {
+ if (trace.isTraceEnabled())
+ trace.exit("preProcess");
+ return bytes;
+ }
+ try {
+ weavingAdaptor.setActiveProtectionDomain(protectionDomain);
+ byte[] newBytes = weavingAdaptor.weaveClass(className, bytes, false);
+ Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true);
+ if (trace.isTraceEnabled())
+ trace.exit("preProcess", newBytes);
+ if (SimpleCacheFactory.isEnabled()) {
+ laCache.put(className, bytes, newBytes);
+ }
+ return newBytes;
+ } finally {
+ weavingAdaptor.setActiveProtectionDomain(null);
+ }
+ }
+
+ /* Don't like to do this but JVMTI swallows all exceptions */
+ } catch (Throwable th) {
+ trace.error(className, th);
+ Dump.dumpWithException(th);
+ // FIXME AV wondering if we should have the option to fail (throw runtime exception) here
+ // would make sense at least in test f.e. see TestHelper.handleMessage()
+ if (trace.isTraceEnabled())
+ trace.exit("preProcess", th);
+ return bytes;
+ } finally {
+ CompilationAndWeavingContext.resetForThread();
+ }
+ }
+
+ /**
+ * An AdaptorKey is a WeakReference wrapping a classloader reference that will enqueue to a specified queue when the classloader
+ * is GC'd. Since the AdaptorKey is used as a key into a hashmap we need to give it a non-varying hashcode/equals
+ * implementation, and we need that hashcode not to vary even when the internal referent has been GC'd. The hashcode is
+ * calculated on creation of the AdaptorKey based on the loader instance that it is wrapping. This means even when the referent
+ * is gone we can still use the AdaptorKey and it will 'point' to the same place as it always did.
+ */
+ private static class AdaptorKey extends WeakReference {
+
+ private final int loaderHashCode, sysHashCode, hashValue;
+ private final String loaderClass;
+
+ public AdaptorKey(ClassLoader loader) {
+ super(loader, adaptorQueue);
+ loaderHashCode = loader.hashCode();
+ sysHashCode = System.identityHashCode(loader);
+ loaderClass = loader.getClass().getName();
+ hashValue = loaderHashCode + sysHashCode + loaderClass.hashCode();
+ }
+
+ public ClassLoader getClassLoader() {
+ ClassLoader instance = (ClassLoader) get();
+ // Assert instance!=null - shouldn't be asked for after a GC of the referent has occurred !
+ return instance;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AdaptorKey)) {
+ return false;
+ }
+ AdaptorKey other = (AdaptorKey) obj;
+ return (other.loaderHashCode == loaderHashCode)
+ && (other.sysHashCode == sysHashCode)
+ && loaderClass.equals(other.loaderClass);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashValue;
+ }
+
+ }
+
+ /**
+ * The reference queue is only processed when a request is made for a weaver adaptor. This means there can be one or two stale
+ * weavers left around. If the user knows they have finished all their weaving, they might wish to call removeStaleAdaptors
+ * which will process anything left on the reference queue containing adaptorKeys for garbage collected classloaders.
+ *
+ * @param displayProgress produce System.err info on the tidying up process
+ * @return number of stale weavers removed
+ */
+ public static int removeStaleAdaptors(boolean displayProgress) {
+ int removed = 0;
+ synchronized (WeaverContainer.weavingAdaptors) {
+ if (displayProgress) {
+ System.err.println("Weaver adaptors before queue processing:");
+ Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> m = WeaverContainer.weavingAdaptors;
+ Set<AdaptorKey> keys = m.keySet();
+ for (Iterator<AdaptorKey> iterator = keys.iterator(); iterator.hasNext();) {
+ Object object = iterator.next();
+ System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
+ }
+ }
+ Object o = adaptorQueue.poll();
+ while (o != null) {
+ if (displayProgress)
+ System.err.println("Processing referencequeue entry " + o);
+ AdaptorKey wo = (AdaptorKey) o;
+ boolean didit = WeaverContainer.weavingAdaptors.remove(wo) != null;
+ if (didit) {
+ removed++;
+ } else {
+ throw new RuntimeException("Eh?? key=" + wo);
+ }
+ if (displayProgress)
+ System.err.println("Removed? " + didit);
+ o = adaptorQueue.poll();
+ }
+ if (displayProgress) {
+ System.err.println("Weaver adaptors after queue processing:");
+ Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> m = WeaverContainer.weavingAdaptors;
+ Set<AdaptorKey> keys = m.keySet();
+ for (Iterator<AdaptorKey> iterator = keys.iterator(); iterator.hasNext();) {
+ Object object = iterator.next();
+ System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
+ }
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * @return the number of entries still in the weavingAdaptors map
+ */
+ public static int getActiveAdaptorCount() {
+ return WeaverContainer.weavingAdaptors.size();
+ }
+
+ /**
+ * Process the reference queue that contains stale AdaptorKeys - the keys are put on the queue when their classloader referent
+ * is garbage collected and so the associated adaptor (weaver) should be removed from the map
+ */
+ public static void checkQ() {
+ synchronized (adaptorQueue) {
+ Object o = adaptorQueue.poll();
+ while (o != null) {
+ AdaptorKey wo = (AdaptorKey) o;
+ // boolean removed =
+ WeaverContainer.weavingAdaptors.remove(wo);
+ // DBG System.err.println("Evicting key " + wo + " = " + didit);
+ o = adaptorQueue.poll();
+ }
+ }
+ }
+
+ public static List<String> loadersToSkip = null;
+
+ static {
+ // pr271840 - touch the types early and outside the locks
+ new ExplicitlyInitializedClassLoaderWeavingAdaptor(new ClassLoaderWeavingAdaptor());
+ try {
+ String loadersToSkipProperty = System.getProperty("aj.weaving.loadersToSkip","");
+ StringTokenizer st = new StringTokenizer(loadersToSkipProperty, ",");
+ if (loadersToSkipProperty != null && loadersToSkip == null) {
+ if (st.hasMoreTokens()) {
+// System.out.println("aj.weaving.loadersToSkip is set. Skipping loaders: '"+loadersToSkipProperty+"'");
+ loadersToSkip = new ArrayList<String>();
+ }
+ while (st.hasMoreTokens()) {
+ String nextLoader = st.nextToken();
+ loadersToSkip.add(nextLoader);
+ }
+ }
+ } catch (Exception e) {
+ // Likely security issue related to property access...
+ }
+ }
+
+ /**
+ * Cache of weaver There is one weaver per classloader
+ */
+ static class WeaverContainer {
+
+ final static Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> weavingAdaptors =
+ Collections.synchronizedMap(new HashMap<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor>());
+
+ static WeavingAdaptor getWeaver(ClassLoader loader, IWeavingContext weavingContext) {
+ ExplicitlyInitializedClassLoaderWeavingAdaptor adaptor = null;
+ AdaptorKey adaptorKey = new AdaptorKey(loader);
+
+ String loaderClassName = loader.getClass().getName();
+
+ synchronized (weavingAdaptors) {
+ checkQ();
+ if (loader.equals(myClassLoader)){
+ adaptor = myClassLoaderAdaptor;
+ } else {
+ adaptor = weavingAdaptors.get(adaptorKey);
+ }
+ if (adaptor == null) {
+ // create it and put it back in the weavingAdaptors map but avoid any kind of instantiation
+ // within the synchronized block
+ ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor();
+ adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor);
+ if(myClassLoaderAdaptor == null && loader.equals(myClassLoader)){
+ myClassLoaderAdaptor = adaptor;
+ } else {
+ weavingAdaptors.put(adaptorKey, adaptor);
+ }
+ }
+ }
+ // perform the initialization
+ return adaptor.getWeavingAdaptor(loader, weavingContext);
+
+
+ }
+ private static final ClassLoader myClassLoader = WeavingAdaptor.class.getClassLoader();
+ private static ExplicitlyInitializedClassLoaderWeavingAdaptor myClassLoaderAdaptor;
+ }
+
+
+ static class ExplicitlyInitializedClassLoaderWeavingAdaptor {
+ private final ClassLoaderWeavingAdaptor weavingAdaptor;
+ private boolean isInitialized;
+
+ public ExplicitlyInitializedClassLoaderWeavingAdaptor(ClassLoaderWeavingAdaptor weavingAdaptor) {
+ this.weavingAdaptor = weavingAdaptor;
+ this.isInitialized = false;
+ }
+
+ private void initialize(ClassLoader loader, IWeavingContext weavingContext) {
+ if (!isInitialized) {
+ isInitialized = true;
+ weavingAdaptor.initialize(loader, weavingContext);
+ }
+ }
+
+ public ClassLoaderWeavingAdaptor getWeavingAdaptor(ClassLoader loader, IWeavingContext weavingContext) {
+ initialize(loader, weavingContext);
+ return weavingAdaptor;
+ }
+ }
+
+ /**
+ * Returns a namespace based on the contest of the aspects available
+ */
+ public String getNamespace(ClassLoader loader) {
+ ClassLoaderWeavingAdaptor weavingAdaptor = (ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext);
+ return weavingAdaptor.getNamespace();
+ }
+
+ /**
+ * Check to see if any classes have been generated for a particular classes loader. Calls
+ * ClassLoaderWeavingAdaptor.generatedClassesExist()
+ *
+ * @param loader the class cloder
+ * @return true if classes have been generated.
+ */
+ public boolean generatedClassesExist(ClassLoader loader) {
+ return ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).generatedClassesExistFor(null);
+ }
+
+ public void flushGeneratedClasses(ClassLoader loader) {
+ ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClasses();
+ }
+
+ @Override
+ public void prepareForRedefinition(ClassLoader loader, String className) {
+ ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClassesFor(className);
+ }
+
+} \ No newline at end of file
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java
new file mode 100644
index 000000000..f67b0f3ad
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassLoaderWeavingAdaptor.java
@@ -0,0 +1,1188 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2017 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.aspectj.bridge.AbortException;
+import org.aspectj.bridge.Constants;
+import org.aspectj.bridge.MessageUtil;
+import org.aspectj.util.LangUtil;
+import org.aspectj.weaver.IUnwovenClassFile;
+import org.aspectj.weaver.Lint;
+import org.aspectj.weaver.Lint.Kind;
+import org.aspectj.weaver.ResolvedType;
+import org.aspectj.weaver.UnresolvedType;
+import org.aspectj.weaver.World;
+import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
+import org.aspectj.weaver.bcel.BcelWeaver;
+import org.aspectj.weaver.bcel.BcelWorld;
+import org.aspectj.weaver.bcel.Utility;
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.weaver.loadtime.definition.DocumentParser;
+import org.aspectj.weaver.ltw.LTWWorld;
+import org.aspectj.weaver.patterns.PatternParser;
+import org.aspectj.weaver.patterns.TypePattern;
+import org.aspectj.weaver.tools.GeneratedClassHandler;
+import org.aspectj.weaver.tools.Trace;
+import org.aspectj.weaver.tools.TraceFactory;
+import org.aspectj.weaver.tools.WeavingAdaptor;
+import org.aspectj.weaver.tools.cache.WeavedClassCache;
+
+import sun.misc.Unsafe;
+
+/**
+ * @author Alexandre Vasseur
+ * @author Andy Clement
+ * @author Abraham Nevado
+ * @author David Knibb
+ * @author John Kew
+ */
+public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
+
+ private final static String AOP_XML = Constants.AOP_USER_XML + ";" + Constants.AOP_AJC_XML + ";" + Constants.AOP_OSGI_XML;
+
+ private boolean initialized;
+
+ private List<TypePattern> dumpTypePattern = new ArrayList<TypePattern>();
+ private boolean dumpBefore = false;
+ private boolean dumpDirPerClassloader = false;
+
+ private boolean hasExcludes = false;
+ private List<TypePattern> excludeTypePattern = new ArrayList<TypePattern>(); // anything
+ private List<String> excludeStartsWith = new ArrayList<String>(); // com.foo..*
+ private List<String> excludeStarDotDotStar = new ArrayList<String>(); // *..*CGLIB*
+ private List<String> excludeExactName = new ArrayList<String>(); // com.foo.Bar
+ private List<String> excludeEndsWith = new ArrayList<String>(); // com.foo.Bar
+ private List<String[]> excludeSpecial = new ArrayList<String[]>();
+
+ private boolean hasIncludes = false;
+ private List<TypePattern> includeTypePattern = new ArrayList<TypePattern>();
+ private List<String> includeStartsWith = new ArrayList<String>();
+ private List<String> includeExactName = new ArrayList<String>();
+ private boolean includeStar = false;
+
+ private List<TypePattern> aspectExcludeTypePattern = new ArrayList<TypePattern>();
+ private List<String> aspectExcludeStartsWith = new ArrayList<String>();
+ private List<TypePattern> aspectIncludeTypePattern = new ArrayList<TypePattern>();
+ private List<String> aspectIncludeStartsWith = new ArrayList<String>();
+
+ private StringBuffer namespace;
+ private IWeavingContext weavingContext;
+
+ private List<ConcreteAspectCodeGen> concreteAspects = new ArrayList<ConcreteAspectCodeGen>();
+
+ private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassLoaderWeavingAdaptor.class);
+
+ public ClassLoaderWeavingAdaptor() {
+ super();
+ if (trace.isTraceEnabled()) {
+ trace.enter("<init>", this);
+ }
+ if (trace.isTraceEnabled()) {
+ trace.exit("<init>");
+ }
+ }
+
+ /**
+ * We don't need a reference to the class loader and using it during construction can cause problems with recursion. It also
+ * makes sense to supply the weaving context during initialization to.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public ClassLoaderWeavingAdaptor(final ClassLoader deprecatedLoader, final IWeavingContext deprecatedContext) {
+ super();
+ if (trace.isTraceEnabled()) {
+ trace.enter("<init>", this, new Object[] { deprecatedLoader, deprecatedContext });
+ }
+ if (trace.isTraceEnabled()) {
+ trace.exit("<init>");
+ }
+ }
+
+ class SimpleGeneratedClassHandler implements GeneratedClassHandler {
+ private BcelWeakClassLoaderReference loaderRef;
+
+ SimpleGeneratedClassHandler(ClassLoader loader) {
+ loaderRef = new BcelWeakClassLoaderReference(loader);
+ }
+
+ /**
+ * Callback when we need to define a Closure in the JVM
+ *
+ */
+ @Override
+ public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) {
+ try {
+ if (shouldDump(name.replace('/', '.'), false)) {
+ dump(name, wovenBytes, false);
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ if (activeProtectionDomain != null) {
+ defineClass(loaderRef.getClassLoader(), name, wovenBytes, activeProtectionDomain);
+ } else {
+ defineClass(loaderRef.getClassLoader(), name, wovenBytes); // could be done lazily using the hook
+
+ }
+ }
+ }
+
+ public void initialize(final ClassLoader classLoader, IWeavingContext context) {
+ if (initialized) {
+ return;
+ }
+
+ boolean success = true;
+
+ this.weavingContext = context;
+ if (weavingContext == null) {
+ weavingContext = new DefaultWeavingContext(classLoader);
+ }
+
+ createMessageHandler();
+
+ this.generatedClassHandler = new SimpleGeneratedClassHandler(classLoader);
+
+ List<Definition> definitions = weavingContext.getDefinitions(classLoader, this);
+ if (definitions.isEmpty()) {
+ disable(); // TODO maw Needed to ensure messages are flushed
+ if (trace.isTraceEnabled()) {
+ trace.exit("initialize", definitions);
+ }
+ return;
+ }
+
+ // 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);
+
+ // register the definitions
+ success = registerDefinitions(weaver, classLoader, definitions);
+ if (success) {
+
+ // after adding aspects
+ weaver.prepareForWeave();
+
+ enable(); // TODO maw Needed to ensure messages are flushed
+ success = weaveAndDefineConceteAspects();
+ }
+
+ if (success) {
+ enable();
+ } else {
+ disable();
+ bcelWorld = null;
+ weaver = null;
+ }
+ if (WeavedClassCache.isEnabled()) {
+ initializeCache(classLoader, getAspectClassNames(definitions), generatedClassHandler, getMessageHandler());
+ }
+
+ initialized = true;
+ if (trace.isTraceEnabled()) {
+ trace.exit("initialize", isEnabled());
+ }
+ }
+
+ /**
+ * Get the list of all aspects from the defintion list
+ * @param definitions
+ * @return
+ */
+ List<String> getAspectClassNames(List<Definition> definitions) {
+ List<String> aspects = new LinkedList<String>();
+ for (Iterator<Definition> it = definitions.iterator(); it.hasNext(); ) {
+ Definition def = it.next();
+ List<String> defAspects = def.getAspectClassNames();
+ if (defAspects != null) {
+ aspects.addAll(defAspects);
+ }
+ }
+ return aspects;
+ }
+
+ /**
+ * Load and cache the aop.xml/properties according to the classloader visibility rules
+ *
+ * @param loader
+ */
+ List<Definition> parseDefinitions(final ClassLoader loader) {
+ if (trace.isTraceEnabled()) {
+ trace.enter("parseDefinitions", this);
+ }
+
+ List<Definition> definitions = new ArrayList<Definition>();
+ try {
+ info("register classloader " + getClassLoaderName(loader));
+ // TODO av underoptimized: we will parse each XML once per CL that see it
+
+ // TODO av dev mode needed ? TBD -Daj5.def=...
+ if (loader.equals(ClassLoader.getSystemClassLoader())) {
+ String file = System.getProperty("aj5.def", null);
+ if (file != null) {
+ info("using (-Daj5.def) " + file);
+ definitions.add(DocumentParser.parse((new File(file)).toURI().toURL()));
+ }
+ }
+
+ String resourcePath = System.getProperty("org.aspectj.weaver.loadtime.configuration", AOP_XML);
+ if (trace.isTraceEnabled()) {
+ trace.event("parseDefinitions", this, resourcePath);
+ }
+
+ StringTokenizer st = new StringTokenizer(resourcePath, ";");
+
+ while (st.hasMoreTokens()) {
+ String nextDefinition = st.nextToken();
+ if (nextDefinition.startsWith("file:")) {
+ try {
+ String fpath = new URL(nextDefinition).getFile();
+ File configFile = new File(fpath);
+ if (!configFile.exists()) {
+ warn("configuration does not exist: " + nextDefinition);
+ } else {
+ definitions.add(DocumentParser.parse(configFile.toURI().toURL()));
+ }
+ } catch (MalformedURLException mue) {
+ error("malformed definition url: " + nextDefinition);
+ }
+ } else {
+ Enumeration<URL> xmls = weavingContext.getResources(nextDefinition);
+ // System.out.println("? registerDefinitions: found-aop.xml=" + xmls.hasMoreElements() + ", loader=" + loader);
+
+ Set<URL> seenBefore = new HashSet<URL>();
+ while (xmls.hasMoreElements()) {
+ URL xml = xmls.nextElement();
+ if (trace.isTraceEnabled()) {
+ trace.event("parseDefinitions", this, xml);
+ }
+ if (!seenBefore.contains(xml)) {
+ info("using configuration " + weavingContext.getFile(xml));
+ definitions.add(DocumentParser.parse(xml));
+ seenBefore.add(xml);
+ } else {
+ debug("ignoring duplicate definition: " + xml);
+ }
+ }
+ }
+ }
+ if (definitions.isEmpty()) {
+ info("no configuration found. Disabling weaver for class loader " + getClassLoaderName(loader));
+ }
+ } catch (Exception e) {
+ definitions.clear();
+ warn("parse definitions failed", e);
+ }
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("parseDefinitions", definitions);
+ }
+ return definitions;
+ }
+
+ private boolean registerDefinitions(final BcelWeaver weaver, final ClassLoader loader, List<Definition> definitions) {
+ if (trace.isTraceEnabled()) {
+ trace.enter("registerDefinitions", this, definitions);
+ }
+ boolean success = true;
+
+ try {
+ registerOptions(weaver, loader, definitions);
+ registerAspectExclude(weaver, loader, definitions);
+ registerAspectInclude(weaver, loader, definitions);
+ success = registerAspects(weaver, loader, definitions);
+ registerIncludeExclude(weaver, loader, definitions);
+ registerDump(weaver, loader, definitions);
+ } catch (Exception ex) {
+ trace.error("register definition failed", ex);
+ success = false;
+ warn("register definition failed", (ex instanceof AbortException) ? null : ex);
+ }
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("registerDefinitions", success);
+ }
+ return success;
+ }
+
+ private String getClassLoaderName(ClassLoader loader) {
+ return weavingContext.getClassLoaderName();
+ }
+
+ /**
+ * Configure the weaver according to the option directives TODO av - don't know if it is that good to reuse, since we only allow
+ * a small subset of options in LTW
+ *
+ * @param weaver
+ * @param loader
+ * @param definitions
+ */
+ private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
+ StringBuffer allOptions = new StringBuffer();
+ for (Definition definition : definitions) {
+ allOptions.append(definition.getWeaverOptions()).append(' ');
+ }
+
+ Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader, getMessageHandler());
+
+ // configure the weaver and world
+ // AV - code duplicates AspectJBuilder.initWorldAndWeaver()
+ World world = weaver.getWorld();
+ setMessageHandler(weaverOption.messageHandler);
+ world.setXlazyTjp(weaverOption.lazyTjp);
+ world.setXHasMemberSupportEnabled(weaverOption.hasMember);
+ world.setTiming(weaverOption.timers, true);
+ world.setOptionalJoinpoints(weaverOption.optionalJoinpoints);
+ world.setPinpointMode(weaverOption.pinpoint);
+ weaver.setReweavableMode(weaverOption.notReWeavable);
+ if (weaverOption.loadersToSkip != null && weaverOption.loadersToSkip.length() > 0) {
+ Aj.loadersToSkip = LangUtil.anySplit(weaverOption.loadersToSkip, ",");
+ }
+ if (Aj.loadersToSkip != null) {
+ MessageUtil.info(world.getMessageHandler(),"no longer creating weavers for these classloaders: "+Aj.loadersToSkip);
+ }
+ world.performExtraConfiguration(weaverOption.xSet);
+ world.setXnoInline(weaverOption.noInline);
+ // AMC - autodetect as per line below, needed for AtAjLTWTests.testLTWUnweavable
+ world.setBehaveInJava5Way(LangUtil.is15VMOrGreater());
+ world.setAddSerialVerUID(weaverOption.addSerialVersionUID);
+
+ /* First load defaults */
+ bcelWorld.getLint().loadDefaultProperties();
+
+ /* Second overlay LTW defaults */
+ bcelWorld.getLint().adviceDidNotMatch.setKind(null);
+
+ /* Third load user file using -Xlintfile so that -Xlint wins */
+ if (weaverOption.lintFile != null) {
+ InputStream resource = null;
+ try {
+ resource = loader.getResourceAsStream(weaverOption.lintFile);
+ Exception failure = null;
+ if (resource != null) {
+ try {
+ Properties properties = new Properties();
+ properties.load(resource);
+ world.getLint().setFromProperties(properties);
+ } catch (IOException e) {
+ failure = e;
+ }
+ }
+ if (failure != null || resource == null) {
+ warn("Cannot access resource for -Xlintfile:" + weaverOption.lintFile, failure);
+ // world.getMessageHandler().handleMessage(new Message(
+ // "Cannot access resource for -Xlintfile:"+weaverOption.lintFile,
+ // IMessage.WARNING,
+ // failure,
+ // null));
+ }
+ } finally {
+ try {
+ resource.close();
+ } catch (Throwable t) {
+ }
+ }
+ }
+
+ /* Fourth override with -Xlint */
+ if (weaverOption.lint != null) {
+ if (weaverOption.lint.equals("default")) {// FIXME should be AjBuildConfig.AJLINT_DEFAULT but yetanother deps..
+ bcelWorld.getLint().loadDefaultProperties();
+ } else {
+ bcelWorld.getLint().setAll(weaverOption.lint);
+ if (weaverOption.lint.equals("ignore")) {
+ bcelWorld.setAllLintIgnored();
+ }
+ }
+ }
+ // TODO proceedOnError option
+ }
+
+ private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
+ String fastMatchInfo = null;
+ for (Definition definition : definitions) {
+ for (String exclude : definition.getAspectExcludePatterns()) {
+ TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
+ aspectExcludeTypePattern.add(excludePattern);
+ fastMatchInfo = looksLikeStartsWith(exclude);
+ if (fastMatchInfo != null) {
+ aspectExcludeStartsWith.add(fastMatchInfo);
+ }
+ }
+ }
+ }
+
+ private void registerAspectInclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
+ String fastMatchInfo = null;
+ for (Definition definition : definitions) {
+ for (String include : definition.getAspectIncludePatterns()) {
+ TypePattern includePattern = new PatternParser(include).parseTypePattern();
+ aspectIncludeTypePattern.add(includePattern);
+ fastMatchInfo = looksLikeStartsWith(include);
+ if (fastMatchInfo != null) {
+ aspectIncludeStartsWith.add(fastMatchInfo);
+ }
+ }
+ }
+ }
+
+ protected void lint(String name, String[] infos) {
+ Lint lint = bcelWorld.getLint();
+ Kind kind = lint.getLintKind(name);
+ kind.signal(infos, null, null);
+ }
+
+ @Override
+ public String getContextId() {
+ return weavingContext.getId();
+ }
+
+ /**
+ * Register the aspect, following include / exclude rules
+ *
+ * @param weaver
+ * @param loader
+ * @param definitions
+ */
+ private boolean registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
+ if (trace.isTraceEnabled()) {
+ trace.enter("registerAspects", this, new Object[] { weaver, loader, definitions });
+ }
+ boolean success = true;
+
+ // TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ??
+ // if not, review the getResource so that we track which resource is defined by which CL
+
+ // iterate aspectClassNames
+ // exclude if in any of the exclude list
+ for (Definition definition : definitions) {
+ for (String aspectClassName : definition.getAspectClassNames()) {
+ if (acceptAspect(aspectClassName)) {
+ info("register aspect " + aspectClassName);
+ // System.err.println("? ClassLoaderWeavingAdaptor.registerAspects() aspectName=" + aspectClassName +
+ // ", loader=" + loader + ", bundle=" + weavingContext.getClassLoaderName());
+ String requiredType = definition.getAspectRequires(aspectClassName);
+ if (requiredType != null) {
+ // This aspect expresses that it requires a type to be around, otherwise it should 'switch off'
+ ((BcelWorld) weaver.getWorld()).addAspectRequires(aspectClassName, requiredType);
+ }
+ String definedScope = definition.getScopeForAspect(aspectClassName);
+ if (definedScope != null) {
+ ((BcelWorld) weaver.getWorld()).addScopedAspect(aspectClassName, definedScope);
+ }
+ // ResolvedType aspect =
+ weaver.addLibraryAspect(aspectClassName);
+
+ // generate key for SC
+ if (namespace == null) {
+ namespace = new StringBuffer(aspectClassName);
+ } else {
+ namespace = namespace.append(";").append(aspectClassName);
+ }
+
+ } else {
+ // warn("aspect excluded: " + aspectClassName);
+ lint("aspectExcludedByConfiguration", new String[] { aspectClassName, getClassLoaderName(loader) });
+ }
+ }
+ }
+
+ // iterate concreteAspects
+ // exclude if in any of the exclude list - note that the user defined name matters for that to happen
+ for (Definition definition : definitions) {
+ for (Definition.ConcreteAspect concreteAspect : definition.getConcreteAspects()) {
+ if (acceptAspect(concreteAspect.name)) {
+ info("define aspect " + concreteAspect.name);
+ ConcreteAspectCodeGen gen = new ConcreteAspectCodeGen(concreteAspect, weaver.getWorld());
+ if (!gen.validate()) {
+ error("Concrete-aspect '" + concreteAspect.name + "' could not be registered");
+ success = false;
+ break;
+ }
+
+ ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes()),
+ true);
+
+ concreteAspects.add(gen);
+
+ weaver.addLibraryAspect(concreteAspect.name);
+
+ // generate key for SC
+ if (namespace == null) {
+ namespace = new StringBuffer(concreteAspect.name);
+ } else {
+ namespace = namespace.append(";" + concreteAspect.name);
+ }
+ }
+ }
+ }
+
+ /* We couldn't register one or more aspects so disable the adaptor */
+ if (!success) {
+ warn("failure(s) registering aspects. Disabling weaver for class loader " + getClassLoaderName(loader));
+ }
+
+ /* We didn't register any aspects so disable the adaptor */
+ else if (namespace == null) {
+ success = false;
+ info("no aspects registered. Disabling weaver for class loader " + getClassLoaderName(loader));
+ }
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("registerAspects", success);
+ }
+ return success;
+ }
+
+ private boolean weaveAndDefineConceteAspects() {
+ if (trace.isTraceEnabled()) {
+ trace.enter("weaveAndDefineConceteAspects", this, concreteAspects);
+ }
+ boolean success = true;
+
+ for (ConcreteAspectCodeGen gen : concreteAspects) {
+ String name = gen.getClassName();
+ byte[] bytes = gen.getBytes();
+
+ try {
+ byte[] newBytes = weaveClass(name, bytes, true);
+ this.generatedClassHandler.acceptClass(name, bytes, newBytes);
+ } catch (IOException ex) {
+ trace.error("weaveAndDefineConceteAspects", ex);
+ error("exception weaving aspect '" + name + "'", ex);
+ }
+ }
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("weaveAndDefineConceteAspects", success);
+ }
+ return success;
+ }
+
+ /**
+ * 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<Definition> definitions) {
+ String fastMatchInfo = null;
+ for (Definition definition : definitions) {
+ for (Iterator<String> iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) {
+ hasIncludes = true;
+ String include = iterator1.next();
+ fastMatchInfo = looksLikeStartsWith(include);
+ if (fastMatchInfo != null) {
+ 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<String> iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) {
+ hasExcludes = true;
+ String exclude = iterator1.next();
+ fastMatchInfo = looksLikeStartsWith(exclude);
+ if (fastMatchInfo != null) {
+ 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 pattern looks like "*..*XXXX*" and if so returns XXXX. This will enable fast name matching of CGLIB exclusion
+ *
+ */
+ 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 (hasSpaceAnnotationPlus(typePattern, 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 earlier '*'
+ return typePattern.substring(0, length - 2).replace('$', '.'); // "charsss." or "char.rss." etc
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Register the dump filter
+ *
+ * @param weaver
+ * @param loader
+ * @param definitions
+ */
+ private void registerDump(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
+ for (Definition definition : definitions) {
+ for (Iterator<String> iterator1 = definition.getDumpPatterns().iterator(); iterator1.hasNext();) {
+ String dump = iterator1.next();
+ TypePattern pattern = new PatternParser(dump).parseTypePattern();
+ dumpTypePattern.add(pattern);
+ }
+ if (definition.shouldDumpBefore()) {
+ dumpBefore = true;
+ }
+ if (definition.createDumpDirPerClassloader()) {
+ dumpDirPerClassloader = true;
+ }
+ }
+ }
+
+ /**
+ * 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) {
+
+ if (!hasExcludes && !hasIncludes) {
+ return true;
+ }
+
+ // still try to avoid ResolvedType if we have simple patterns
+ String fastClassName = className.replace('/', '.');
+ 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;
+ }
+ }
+ }
+ fastClassName = fastClassName.replace('$', '.');
+
+ 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
+ */
+ 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 < includeStartsWith.size(); i++) {
+ didSomeIncludeMatching = true;
+ fastAccept = fastClassName.startsWith(includeStartsWith.get(i));
+ if (fastAccept) {
+ return true;
+ }
+ }
+ // We may have processed all patterns now... check that and return
+ if (includeTypePattern.isEmpty()) {
+ return !didSomeIncludeMatching;
+ }
+ }
+
+ boolean accept;
+ try {
+ ensureDelegateInitialized(className, bytes);
+
+ 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
+ if (includeStar) {
+ return true;
+ }
+ if (!includeExactName.isEmpty()) {
+ didSomeIncludeMatching = true;
+ for (String exactname : includeExactName) {
+ if (fastClassName.equals(exactname)) {
+ return true;
+ }
+ }
+ }
+ for (int i = 0; i < includeStartsWith.size(); i++) {
+ didSomeIncludeMatching = true;
+ boolean fastaccept = fastClassName.startsWith(includeStartsWith.get(i));
+ if (fastaccept) {
+ return true;
+ }
+ }
+ 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)
+ }
+ } finally {
+ this.bcelWorld.demote();
+ }
+ return accept;
+ }
+
+ // FIXME we don't use include/exclude of others aop.xml
+ // this can be nice but very dangerous as well to change that
+ private boolean acceptAspect(String aspectClassName) {
+ // avoid ResolvedType if not needed
+ if (aspectExcludeTypePattern.isEmpty() && aspectIncludeTypePattern.isEmpty()) {
+ return true;
+ }
+
+ // still try to avoid ResolvedType if we have simple patterns
+ // EXCLUDE: if one match then reject
+ String fastClassName = aspectClassName.replace('/', '.').replace('.', '$');
+ for (int i = 0; i < aspectExcludeStartsWith.size(); i++) {
+ if (fastClassName.startsWith(aspectExcludeStartsWith.get(i))) {
+ return false;
+ }
+ }
+ // INCLUDE: if one match then accept
+ for (int i = 0; i < aspectIncludeStartsWith.size(); i++) {
+ if (fastClassName.startsWith(aspectIncludeStartsWith.get(i))) {
+ return true;
+ }
+ }
+
+ // needs further analysis
+ ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(aspectClassName), true);
+ // exclude are "AND"ed
+ for (TypePattern typePattern: aspectExcludeTypePattern) {
+ if (typePattern.matchesStatically(classInfo)) {
+ // exclude match - skip
+ return false;
+ }
+ }
+ // include are "OR"ed
+ boolean accept = true;// defaults to true if no include
+ for (TypePattern typePattern: aspectIncludeTypePattern) {
+ accept = typePattern.matchesStatically(classInfo);
+ if (accept) {
+ break;
+ }
+ // goes on if this include did not match ("OR"ed)
+ }
+ return accept;
+ }
+
+ @Override
+ protected boolean shouldDump(String className, boolean before) {
+ // Don't dump before weaving unless asked to
+ if (before && !dumpBefore) {
+ return false;
+ }
+
+ // avoid ResolvedType if not needed
+ if (dumpTypePattern.isEmpty()) {
+ return false;
+ }
+
+ // TODO AV - optimize for className.startWith only
+ ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(className), true);
+ // dump
+ for (Iterator<TypePattern> iterator = dumpTypePattern.iterator(); iterator.hasNext();) {
+ TypePattern typePattern = iterator.next();
+ if (typePattern.matchesStatically(classInfo)) {
+ // dump match
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected String getDumpDir() {
+ if (dumpDirPerClassloader) {
+ StringBuffer dir = new StringBuffer();
+ dir.append("_ajdump").append(File.separator).append(weavingContext.getId());
+ return dir.toString();
+ } else {
+ return super.getDumpDir();
+ }
+ }
+
+ /*
+ * shared classes methods
+ */
+
+ /**
+ * @return Returns the key.
+ */
+ public String getNamespace() {
+ // System.out.println("ClassLoaderWeavingAdaptor.getNamespace() classloader=" + weavingContext.getClassLoaderName() +
+ // ", namespace=" + namespace);
+ if (namespace == null) {
+ return "";
+ } else {
+ return new String(namespace);
+ }
+ }
+
+ /**
+ * Check to see if any classes are stored in the generated classes cache. Then flush the cache if it is not empty
+ *
+ * @param className TODO
+ * @return true if a class has been generated and is stored in the cache
+ */
+ public boolean generatedClassesExistFor(String className) {
+ // System.err.println("? ClassLoaderWeavingAdaptor.generatedClassesExist() classname=" + className + ", size=" +
+ // generatedClasses);
+ if (className == null) {
+ return !generatedClasses.isEmpty();
+ } else {
+ return generatedClasses.containsKey(className);
+ }
+ }
+
+ /**
+ * Flush the generated classes cache
+ */
+ public void flushGeneratedClasses() {
+ // System.err.println("? ClassLoaderWeavingAdaptor.flushGeneratedClasses() generatedClasses=" + generatedClasses);
+ generatedClasses = new HashMap<String, IUnwovenClassFile>();
+ }
+
+ /**
+ * Remove generated classes based on the supplied className. This will
+ * remove any entries related to this name - so the class itself plus
+ * and inner classes.
+ * @param className a slashed classname (e.g. com/foo/Bar)
+ */
+ public void flushGeneratedClassesFor(String className) {
+ try {
+ String dottedClassName = className.replace('/', '.');
+ String dottedClassNameDollar = dottedClassName+"$"; // to pickup inner classes
+ Iterator<Map.Entry<String, IUnwovenClassFile>> iter = generatedClasses.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry<String, IUnwovenClassFile> next = iter.next();
+ String existingGeneratedName = next.getKey();
+ if (existingGeneratedName.equals(dottedClassName) ||
+ existingGeneratedName.startsWith(dottedClassNameDollar)) {
+ iter.remove();
+ }
+ }
+ } catch (Throwable t) {
+ new RuntimeException("Unexpected problem tidying up generated classes for "+className,t).printStackTrace();
+ }
+ }
+
+ private Unsafe unsafe;
+
+ private Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
+ if (unsafe == null) {
+ Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafeField.setAccessible(true);
+ return (Unsafe) theUnsafeField.get(null);
+ }
+ return unsafe;
+ }
+
+ private static Method bindTo_Method, invokeWithArguments_Method = null;
+ private static Object defineClassMethodHandle = null;
+
+ private static Boolean initializedForJava11 = false;
+
+ // In order to let this code compile on earlier versions of Java (8), use reflection to discover the elements
+ // we need to define classes.
+ private static synchronized void initializeForJava11() {
+ if (initializedForJava11) return;
+ try {
+ // MethodType defineClassMethodType = MethodType.methodType(Class.class, new Class[]{String.class, byte[].class, int.class, int.class, ProtectionDomain.class});
+ Class<?> methodType_Class = Class.forName("java.lang.invoke.MethodType");
+ Method methodTypeMethodOnMethodTypeClass = methodType_Class.getDeclaredMethod("methodType", Class.class,Class[].class);
+ methodTypeMethodOnMethodTypeClass.setAccessible(true);
+ Object defineClassMethodType = methodTypeMethodOnMethodTypeClass.invoke(null, Class.class, new Class[] {String.class,byte[].class,int.class,int.class,ProtectionDomain.class});
+
+ // MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup();
+ Class<?> methodHandles_Class = Class.forName("java.lang.invoke.MethodHandles");
+ Method lookupMethodOnMethodHandlesClass = methodHandles_Class.getDeclaredMethod("lookup");
+ lookupMethodOnMethodHandlesClass.setAccessible(true);
+ Object methodHandlesLookup = lookupMethodOnMethodHandlesClass.invoke(null);
+
+ // MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(ClassLoader.class, baseLookup);
+ Class<?> methodHandlesLookup_Class = Class.forName("java.lang.invoke.MethodHandles$Lookup");
+ Method privateLookupMethodOnMethodHandlesClass = methodHandles_Class.getDeclaredMethod("privateLookupIn",Class.class,methodHandlesLookup_Class);
+ privateLookupMethodOnMethodHandlesClass.setAccessible(true);
+ Object lookup = privateLookupMethodOnMethodHandlesClass.invoke(null, ClassLoader.class, methodHandlesLookup);
+
+ // MethodHandle defineClassMethodHandle = lookup.findVirtual(ClassLoader.class, "defineClass", defineClassMethodType);
+ Method findVirtual_Method = methodHandlesLookup_Class.getDeclaredMethod("findVirtual", Class.class,String.class,methodType_Class);
+ findVirtual_Method.setAccessible(true);
+ defineClassMethodHandle = findVirtual_Method.invoke(lookup, ClassLoader.class, "defineClass",defineClassMethodType);
+
+ // clazz = defineClassMethodHandle.bindTo(loader).invokeWithArguments(name, bytes, 0, bytes.length);
+ Class<?> methodHandle_Class = Class.forName("java.lang.invoke.MethodHandle");
+ bindTo_Method = methodHandle_Class.getDeclaredMethod("bindTo", Object.class);
+ invokeWithArguments_Method = methodHandle_Class.getDeclaredMethod("invokeWithArguments",Object[].class);
+
+ initializedForJava11 = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void defineClass(ClassLoader loader, String name, byte[] bytes, ProtectionDomain protectionDomain) {
+ if (trace.isTraceEnabled()) {
+ trace.enter("defineClass", this, new Object[] { loader, name, bytes });
+ }
+ Object clazz = null;
+ debug("generating class '" + name + "'");
+ if (LangUtil.is11VMOrGreater()) {
+ try {
+ if (!initializedForJava11) {
+ initializeForJava11();
+ }
+ // Do this: clazz = defineClassMethodHandle.bindTo(loader).invokeWithArguments(name, bytes, 0, bytes.length, protectionDomain);
+ Object o = bindTo_Method.invoke(defineClassMethodHandle,loader);
+ clazz = invokeWithArguments_Method.invoke(o, new Object[] {new Object[] {name, bytes, 0, bytes.length, protectionDomain}});
+
+ } catch (Throwable t) {
+ t.printStackTrace(System.err);
+ warn("define generated class failed", t);
+ }
+ } else {
+ try {
+ if (defineClassMethod == null) {
+ synchronized (lock) {
+ getUnsafe();
+ defineClassMethod =
+ Unsafe.class.getDeclaredMethod("defineClass", String.class,byte[].class,Integer.TYPE,Integer.TYPE, ClassLoader.class,ProtectionDomain.class);
+ }
+ }
+ defineClassMethod.setAccessible(true);
+ clazz = defineClassMethod.invoke(getUnsafe(), name,bytes,0,bytes.length,loader,protectionDomain);
+ } catch (LinkageError le) {
+ le.printStackTrace();
+ // likely thrown due to defining something that already exists?
+ // Old comments from before moving to Unsafe.defineClass():
+ // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved)
+ // TODO maw I don't think this is OK and
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ warn("define generated class failed", e);
+ }
+ }
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("defineClass", clazz);
+ }
+ }
+ static Method defineClassMethod;
+ private static String lock = "lock";
+
+
+// /*
+// This method is equivalent to the following code but use reflection to compile on Java 7:
+// MethodHandles.Lookup baseLookup = MethodHandles.lookup();
+// MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(ClassLoader.class, baseLookup);
+// MethodHandle defineClassMethodHandle = lookup.findVirtual(ClassLoader.class, "defineClass", defineClassMethodType);
+// handle.bindTo(classLoader).invokeWithArguments(className, classBytes, 0, classBytes.length));
+// */
+//@Override
+//@SuppressWarnings("unchecked")
+//public <T> Class<T> defineClass(ClassLoader classLoader, String className, byte[] classBytes) {
+// Object baseLookup = methodHandlesLookup.invoke(null);
+// Object lookup = methodHandlesPrivateLookupIn.invoke(null, ClassLoader.class, baseLookup);
+// MethodHandle defineClassMethodHandle = (MethodHandle) lookupFindVirtual.invoke(lookup, ClassLoader.class, "defineClass", defineClassMethodType);
+// try {
+// return Cast.uncheckedCast(defineClassMethodHandle.bindTo(classLoader).invokeWithArguments(className, classBytes, 0, classBytes.length));
+// } catch (Throwable throwable) {
+// throw new RuntimeException(throwable);
+// return (Class) defineClassMethodHandle.bindTo(classLoader).invokeWithArguments(className, classBytes, 0, classBytes.length);
+// } catch (Throwable e) {
+// throw new RuntimeException(e);
+// }
+//}
+
+ private void defineClass(ClassLoader loader, String name, byte[] bytes){
+ defineClass(loader,name,bytes,null);//, ProtectionDomain protectionDomain) {
+ }
+// if (trace.isTraceEnabled()) {
+// trace.enter("defineClass", this, new Object[] { loader, name, bytes, protectionDomain });
+// }
+// Object clazz = null;
+// debug("generating class '" + name + "'");
+// try {
+// getUnsafe().defineClass(name, bytes, 0, bytes.length, loader, protectionDomain);
+// } catch (LinkageError le) {
+// // likely thrown due to defining something that already exists?
+// // Old comments from before moving to Unsafe.defineClass():
+// // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved)
+// // TODO maw I don't think this is OK and
+// } catch (Exception e) {
+// warn("define generated class failed", e);
+// }
+//
+// if (trace.isTraceEnabled()) {
+// trace.exit("defineClass", clazz);
+// }
+// }
+
+} \ No newline at end of file
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassPreProcessor.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassPreProcessor.java
new file mode 100644
index 000000000..97348b281
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ClassPreProcessor.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2005,2018 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.security.ProtectionDomain;
+
+/**
+ * Generic class pre processor interface that allows to separate the AspectJ 5 load time weaving from Java 5 JVMTI interfaces for
+ * further use on Java 1.3 / 1.4
+ *
+ * @author Alexandre Vasseur
+ * @author Andy Clement
+ */
+public interface ClassPreProcessor {
+
+ /**
+ * Post constructor initialization, usually empty
+ */
+ void initialize();
+
+ byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader, ProtectionDomain protectionDomain);
+
+ void prepareForRedefinition(ClassLoader loader, String className);
+} \ No newline at end of file
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java
new file mode 100644
index 000000000..15e1e9fd4
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/ConcreteAspectCodeGen.java
@@ -0,0 +1,990 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Alexandre Vasseur initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.aspectj.apache.bcel.Constants;
+import org.aspectj.apache.bcel.classfile.ConstantPool;
+import org.aspectj.apache.bcel.classfile.JavaClass;
+import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
+import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue;
+import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
+import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
+import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
+import org.aspectj.apache.bcel.generic.FieldGen;
+import org.aspectj.apache.bcel.generic.InstructionConstants;
+import org.aspectj.apache.bcel.generic.InstructionFactory;
+import org.aspectj.apache.bcel.generic.InstructionHandle;
+import org.aspectj.apache.bcel.generic.InstructionList;
+import org.aspectj.apache.bcel.generic.LocalVariableTag;
+import org.aspectj.apache.bcel.generic.ObjectType;
+import org.aspectj.apache.bcel.generic.Type;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.Message;
+import org.aspectj.weaver.AjAttribute;
+import org.aspectj.weaver.AnnotationAJ;
+import org.aspectj.weaver.GeneratedReferenceTypeDelegate;
+import org.aspectj.weaver.ReferenceType;
+import org.aspectj.weaver.ResolvedMember;
+import org.aspectj.weaver.ResolvedType;
+import org.aspectj.weaver.UnresolvedType;
+import org.aspectj.weaver.World;
+import org.aspectj.weaver.bcel.BcelAnnotation;
+import org.aspectj.weaver.bcel.BcelPerClauseAspectAdder;
+import org.aspectj.weaver.bcel.BcelWorld;
+import org.aspectj.weaver.bcel.LazyClassGen;
+import org.aspectj.weaver.bcel.LazyMethodGen;
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.weaver.loadtime.definition.Definition.AdviceKind;
+import org.aspectj.weaver.loadtime.definition.Definition.DeclareAnnotationKind;
+import org.aspectj.weaver.loadtime.definition.Definition.PointcutAndAdvice;
+import org.aspectj.weaver.patterns.BasicTokenSource;
+import org.aspectj.weaver.patterns.DeclareAnnotation;
+import org.aspectj.weaver.patterns.ISignaturePattern;
+import org.aspectj.weaver.patterns.ITokenSource;
+import org.aspectj.weaver.patterns.PatternParser;
+import org.aspectj.weaver.patterns.PerClause;
+import org.aspectj.weaver.patterns.TypePattern;
+
+/**
+ * Generates bytecode for concrete-aspect.
+ * <p>
+ * The concrete aspect is generated annotation style aspect (so traditional Java constructs annotated with our AspectJ annotations).
+ * As it is built during aop.xml definitions registration we perform the type munging for perclause, ie. aspectOf() artifact
+ * directly, instead of waiting for it to go thru the weaver (that we are in the middle of configuring).
+ *
+ * @author Alexandre Vasseur
+ * @author Andy Clement
+ */
+public class ConcreteAspectCodeGen {
+
+ private final static String[] EMPTY_STRINGS = new String[0];
+ private final static Type[] EMPTY_TYPES = new Type[0];
+
+ /**
+ * Concrete aspect definition we build for
+ */
+ private final Definition.ConcreteAspect concreteAspect;
+
+ /**
+ * World for which we build for
+ */
+ private final World world;
+
+ /**
+ * Set to true when all is checks are verified
+ */
+ private boolean isValid = false;
+
+ /**
+ * The parent aspect, not concretized
+ */
+ private ResolvedType parent;
+
+ /**
+ * Aspect perClause, used for direct munging of aspectOf artifacts
+ */
+ private PerClause perclause;
+
+ /**
+ * Bytecode for the generated class
+ */
+ private byte[] bytes;
+
+ /**
+ * Create a new generator for a concrete aspect
+ *
+ * @param concreteAspect the aspect definition
+ * @param world the related world (for type resolution, etc)
+ */
+ ConcreteAspectCodeGen(Definition.ConcreteAspect concreteAspect, World world) {
+ this.concreteAspect = concreteAspect;
+ this.world = world;
+ }
+
+ /**
+ * Checks that concrete aspect is valid.
+ *
+ * @return true if ok, false otherwise
+ */
+ public boolean validate() {
+ if (!(world instanceof BcelWorld)) {
+ reportError("Internal error: world must be of type BcelWorld");
+ return false;
+ }
+
+ // name must be undefined so far
+ // TODO only convert the name to signature once, probably earlier than this
+ ResolvedType current = world.lookupBySignature(UnresolvedType.forName(concreteAspect.name).getSignature());
+
+ if (current != null && !current.isMissing()) {
+ reportError("Attempt to concretize but chosen aspect name already defined: " + stringify());
+ return false;
+ }
+
+ if (concreteAspect.pointcutsAndAdvice.size() != 0) {
+ isValid = true;
+ return true;
+ }
+
+ if (concreteAspect.declareAnnotations.size()!=0) {
+ isValid = true;
+ return true;
+ }
+
+ // it can happen that extends is null, for precedence only declaration
+ if (concreteAspect.extend == null && concreteAspect.precedence != null) {
+ if (concreteAspect.pointcuts.isEmpty()) {
+ isValid = true;
+ // m_perClause = new PerSingleton();
+ parent = null;
+ return true;// no need to checks more in that special case
+ } else {
+ reportError("Attempt to use nested pointcuts without extends clause: " + stringify());
+ return false;
+ }
+ }
+
+ String parentAspectName = concreteAspect.extend;
+
+ if (parentAspectName.indexOf("<") != -1) {
+ // yikes, generic parent
+ parent = world.resolve(UnresolvedType.forName(parentAspectName), true);
+ if (parent.isMissing()) {
+ reportError("Unable to resolve type reference: " + stringify());
+ return false;
+ }
+ if (parent.isParameterizedType()) {
+ UnresolvedType[] typeParameters = parent.getTypeParameters();
+ for (int i = 0; i < typeParameters.length; i++) {
+ UnresolvedType typeParameter = typeParameters[i];
+ if (typeParameter instanceof ResolvedType && ((ResolvedType) typeParameter).isMissing()) {
+ reportError("Unablet to resolve type parameter '" + typeParameter.getName() + "' from " + stringify());
+ return false;
+ }
+ }
+ }
+ } else {
+ parent = world.resolve(concreteAspect.extend, true);
+ }
+ // handle inner classes
+ if (parent.isMissing()) {
+ // fallback on inner class lookup mechanism
+ String fixedName = concreteAspect.extend;
+ int hasDot = fixedName.lastIndexOf('.');
+ while (hasDot > 0) {
+ char[] fixedNameChars = fixedName.toCharArray();
+ fixedNameChars[hasDot] = '$';
+ fixedName = new String(fixedNameChars);
+ hasDot = fixedName.lastIndexOf('.');
+ parent = world.resolve(UnresolvedType.forName(fixedName), true);
+ if (!parent.isMissing()) {
+ break;
+ }
+ }
+ }
+
+ if (parent.isMissing()) {
+ reportError("Cannot find parent aspect for: " + stringify());
+ return false;
+ }
+
+ // extends must be abstract (allow for special case of Object where just using aspect for deows)
+ if (!(parent.isAbstract() || parent.equals(ResolvedType.OBJECT))) {
+ reportError("Attempt to concretize a non-abstract aspect: " + stringify());
+ return false;
+ }
+
+ // m_parent must be aspect (allow for special case of Object where just using aspect for deows)
+ if (!(parent.isAspect() || parent.equals(ResolvedType.OBJECT))) {
+ reportError("Attempt to concretize a non aspect: " + stringify());
+ return false;
+ }
+
+ // must have all abstractions defined
+ List<String> elligibleAbstractions = new ArrayList<String>();
+
+ Collection<ResolvedMember> abstractMethods = getOutstandingAbstractMethods(parent);
+ for (ResolvedMember method : abstractMethods) {
+ if ("()V".equals(method.getSignature())) {
+ String n = method.getName();
+ // Allow for the abstract pointcut being from a code style
+ // aspect compiled with -1.5 (see test for 128744)
+ if (n.startsWith("ajc$pointcut")) {
+ n = n.substring(14);
+ n = n.substring(0, n.indexOf("$"));
+ elligibleAbstractions.add(n);
+ } else if (hasPointcutAnnotation(method)) {
+ elligibleAbstractions.add(method.getName());
+ } else {
+ // error, an outstanding abstract method that can't be
+ // concretized in XML
+ reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
+ return false;
+ }
+ } else {
+ if (method.getName().startsWith("ajc$pointcut") || hasPointcutAnnotation(method)) {
+ // it may be a pointcut but it doesn't meet the requirements for XML concretization
+ reportError("Abstract method '"
+ + method.toString()
+ + "' cannot be concretized as a pointcut (illegal signature, must have no arguments, must return void): "
+ + stringify());
+ return false;
+ } else {
+ // error, an outstanding abstract method that can't be
+ // concretized in XML
+ reportError("Abstract method '" + method.toString() + "' cannot be concretized in XML: " + stringify());
+ return false;
+ }
+ }
+ }
+ List<String> pointcutNames = new ArrayList<String>();
+ for (Definition.Pointcut abstractPc : concreteAspect.pointcuts) {
+ pointcutNames.add(abstractPc.name);
+ }
+ for (String elligiblePc : elligibleAbstractions) {
+ if (!pointcutNames.contains(elligiblePc)) {
+ reportError("Abstract pointcut '" + elligiblePc + "' not configured: " + stringify());
+ return false;
+ }
+ }
+
+ if (concreteAspect.perclause != null) {
+ String perclauseString = concreteAspect.perclause;
+ if (perclauseString.startsWith("persingleton")) {
+ } else if (perclauseString.startsWith("percflow")) {
+ } else if (perclauseString.startsWith("pertypewithin")) {
+ } else if (perclauseString.startsWith("perthis")) {
+ } else if (perclauseString.startsWith("pertarget")) {
+ } else if (perclauseString.startsWith("percflowbelow")) {
+ } else {
+ reportError("Unrecognized per clause specified " + stringify());
+ return false;
+ }
+ }
+ isValid = true;
+ return isValid;
+ }
+
+ private Collection<ResolvedMember> getOutstandingAbstractMethods(ResolvedType type) {
+ Map<String, ResolvedMember> collector = new HashMap<String, ResolvedMember>();
+ // let's get to the top of the hierarchy and then walk down ...
+ // recording abstract methods then removing
+ // them if they get defined further down the hierarchy
+ getOutstandingAbstractMethodsHelper(type, collector);
+ return collector.values();
+ }
+
+ // We are trying to determine abstract methods left over at the bottom of a
+ // hierarchy that have not been concretized.
+ private void getOutstandingAbstractMethodsHelper(ResolvedType type, Map<String, ResolvedMember> collector) {
+ if (type == null) {
+ return;
+ }
+ // Get to the top
+ if (!type.equals(ResolvedType.OBJECT)) {
+ if (type.getSuperclass() != null) {
+ getOutstandingAbstractMethodsHelper(type.getSuperclass(), collector);
+ }
+ }
+ ResolvedMember[] rms = type.getDeclaredMethods();
+ if (rms != null) {
+ for (int i = 0; i < rms.length; i++) {
+ ResolvedMember member = rms[i];
+ String key = member.getName() + member.getSignature();
+ if (member.isAbstract()) {
+ collector.put(key, member);
+ } else {
+ collector.remove(key);
+ }
+ }
+ }
+ }
+
+ /**
+ * Rebuild the XML snip that defines this concrete aspect, for log error purpose
+ *
+ * @return string repr.
+ */
+ private String stringify() {
+ StringBuffer sb = new StringBuffer("<concrete-aspect name='");
+ sb.append(concreteAspect.name);
+ sb.append("' extends='");
+ sb.append(concreteAspect.extend);
+ sb.append("' perclause='");
+ sb.append(concreteAspect.perclause);
+ sb.append("'/> in aop.xml");
+ // TODO needs the extra state from the definition (concretized pointcuts and advice definitions)
+ return sb.toString();
+ }
+
+ private boolean hasPointcutAnnotation(ResolvedMember member) {
+ AnnotationAJ[] as = member.getAnnotations();
+ 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;
+ }
+ }
+ return false;
+ }
+
+ public String getClassName() {
+ return concreteAspect.name;
+ }
+
+ /**
+ * @return the bytecode for the concrete aspect
+ */
+ public byte[] getBytes() {
+ if (!isValid) {
+ throw new RuntimeException("Must validate first");
+ }
+ if (bytes != null) {
+ return bytes;
+ }
+ PerClause.Kind perclauseKind = PerClause.SINGLETON;
+ PerClause parentPerClause = (parent != null ? parent.getPerClause() : null);
+ if (parentPerClause != null) {
+ perclauseKind = parentPerClause.getKind();
+ }
+ String perclauseString = null;
+
+ if (concreteAspect.perclause != null) {
+ perclauseString = concreteAspect.perclause;
+ if (perclauseString.startsWith("persingleton")) {
+ perclauseKind = PerClause.SINGLETON;
+ } else if (perclauseString.startsWith("percflow")) {
+ perclauseKind = PerClause.PERCFLOW;
+ } else if (perclauseString.startsWith("pertypewithin")) {
+ perclauseKind = PerClause.PERTYPEWITHIN;
+ } else if (perclauseString.startsWith("perthis")) {
+ perclauseKind = PerClause.PEROBJECT;
+ } else if (perclauseString.startsWith("pertarget")) {
+ perclauseKind = PerClause.PEROBJECT;
+ } else if (perclauseString.startsWith("percflowbelow")) {
+ perclauseKind = PerClause.PERCFLOW;
+ }
+ }
+
+ // @Aspect //inherit clause from m_parent
+ // @DeclarePrecedence("....") // if any
+ // public class xxxName [extends xxxExtends] {
+ // [@Pointcut(xxxExpression-n)
+ // public void xxxName-n() {}]
+ // }
+ String parentName = "java/lang/Object";
+ if (parent != null) {
+ if (parent.isParameterizedType()) {
+ parentName = parent.getGenericType().getName().replace('.', '/');
+ } else {
+ parentName = parent.getName().replace('.', '/');
+ }
+ }
+ // @Aspect public class ...
+ // TODO AV - we could point to the aop.xml that defines it and use JSR-45
+ LazyClassGen cg = new LazyClassGen(concreteAspect.name.replace('.', '/'), parentName, null, Modifier.PUBLIC
+ + Constants.ACC_SUPER, EMPTY_STRINGS, world);
+ if (parent != null && parent.isParameterizedType()) {
+ cg.setSuperClass(parent);
+ }
+ if (perclauseString == null) {
+ AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"),
+ Collections.<NameValuePair> emptyList(), true, cg.getConstantPool());
+ cg.addAnnotation(ag);
+ } else {
+ // List elems = new ArrayList();
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ elems.add(new NameValuePair("value",
+ new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), perclauseString), cg.getConstantPool()));
+ AnnotationGen ag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Aspect"), elems, true,
+ cg.getConstantPool());
+ cg.addAnnotation(ag);
+ }
+ if (concreteAspect.precedence != null) {
+ SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), concreteAspect.precedence);
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
+ AnnotationGen agprec = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/DeclarePrecedence"), elems, true,
+ cg.getConstantPool());
+ cg.addAnnotation(agprec);
+ }
+
+ // default constructor
+ LazyMethodGen init = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, "<init>", EMPTY_TYPES, EMPTY_STRINGS, cg);
+ InstructionList cbody = init.getBody();
+ cbody.append(InstructionConstants.ALOAD_0);
+
+ cbody.append(cg.getFactory().createInvoke(parentName, "<init>", Type.VOID, EMPTY_TYPES, Constants.INVOKESPECIAL));
+ cbody.append(InstructionConstants.RETURN);
+ cg.addMethodGen(init);
+
+ for (Iterator<Definition.Pointcut> it = concreteAspect.pointcuts.iterator(); it.hasNext();) {
+ Definition.Pointcut abstractPc = (Definition.Pointcut) it.next();
+ // TODO AV - respect visibility instead of opening up as public?
+ LazyMethodGen mg = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, abstractPc.name, EMPTY_TYPES, EMPTY_STRINGS, cg);
+ SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), abstractPc.expression);
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
+ AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Pointcut"), elems, true,
+ cg.getConstantPool());
+ AnnotationAJ max = new BcelAnnotation(mag, world);
+ mg.addAnnotation(max);
+
+ InstructionList body = mg.getBody();
+ body.append(InstructionConstants.RETURN);
+ cg.addMethodGen(mg);
+ }
+
+ // Construct any defined declare error/warnings
+ if (concreteAspect.deows.size() > 0) {
+ int counter = 1;
+ for (Definition.DeclareErrorOrWarning deow : concreteAspect.deows) {
+ // Building this:
+ // @DeclareWarning("call(* javax.sql..*(..)) && !within(org.xyz.daos..*)")
+ // static final String aMessage = "Only DAOs should be calling JDBC.";
+
+ FieldGen field = new FieldGen(Modifier.FINAL, ObjectType.STRING, "rule" + (counter++), cg.getConstantPool());
+ SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), deow.pointcut);
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
+ AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/Declare"
+ + (deow.isError ? "Error" : "Warning")), elems, true, cg.getConstantPool());
+ field.addAnnotation(mag);
+
+ field.setValue(deow.message);
+ cg.addField(field, null);
+ }
+ }
+
+ if (concreteAspect.pointcutsAndAdvice.size() > 0) {
+ int adviceCounter = 1;
+ for (PointcutAndAdvice paa : concreteAspect.pointcutsAndAdvice) {
+ generateAdviceMethod(paa, adviceCounter, cg);
+ adviceCounter++;
+ }
+ }
+
+ if (concreteAspect.declareAnnotations.size()>0) {
+ int decCounter = 1;
+ for (Definition.DeclareAnnotation da: concreteAspect.declareAnnotations) {
+ generateDeclareAnnotation(da,decCounter++,cg);
+ }
+ }
+
+ // handle the perClause
+ ReferenceType rt = new ReferenceType(ResolvedType.forName(concreteAspect.name).getSignature(), world);
+ GeneratedReferenceTypeDelegate grtd = new GeneratedReferenceTypeDelegate(rt);
+ grtd.setSuperclass(parent);
+ rt.setDelegate(grtd);
+
+ BcelPerClauseAspectAdder perClauseMunger = new BcelPerClauseAspectAdder(rt, perclauseKind);
+ perClauseMunger.forceMunge(cg, false);
+
+ // TODO AV - unsafe cast
+ // 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, true);
+
+ bytes = jc.getBytes();
+ return bytes;
+ }
+
+ /**
+ * The DeclareAnnotation object encapsulates an method/field/type descriptor and an annotation. This uses a DeclareAnnotation object
+ * captured from the XML (something like '<declare-annotation field="* field1(..)" annotation="@Annot(a='a',fred=false,'abc')"/>')
+ * and builds the same construct that would have existed if the code style variant was used. This involves creating a member upon
+ * which to hang the real annotation and then creating a classfile level attribute indicating a declare annotation is present
+ * (that includes the signature pattern and a pointer to the real member holding the annotation).
+ *
+ */
+ private void generateDeclareAnnotation(Definition.DeclareAnnotation da, int decCounter, LazyClassGen cg) {
+
+ // Here is an example member from a code style declare annotation:
+ //void ajc$declare_at_method_1();
+ // RuntimeInvisibleAnnotations: length = 0x6
+ // 00 01 00 1B 00 00
+ // RuntimeVisibleAnnotations: length = 0x15
+ // 00 01 00 1D 00 03 00 1E 73 00 1F 00 20 73 00 21
+ // 00 22 73 00 23
+ // org.aspectj.weaver.MethodDeclarationLineNumber: length = 0x8
+ // 00 00 00 02 00 00 00 16
+ // org.aspectj.weaver.AjSynthetic: length = 0x
+ //
+ // Code:
+ // Stack=0, Locals=1, Args_size=1
+ // 0: return
+
+ // and at the class level a Declare attribute:
+ // org.aspectj.weaver.Declare: length = 0x51
+ // 05 00 00 00 03 01 00 05 40 41 6E 6E 6F 01 00 17
+ // 61 6A 63 24 64 65 63 6C 61 72 65 5F 61 74 5F 6D
+ // 65 74 68 6F 64 5F 31 01 01 00 00 00 00 05 05 00
+ // 08 73 61 79 48 65 6C 6C 6F 00 01 04 00 00 00 00
+ // 07 00 00 00 27 00 00 00 34 00 00 00 16 00 00 00
+ // 3C
+
+ AnnotationAJ constructedAnnotation = buildDeclareAnnotation_actualAnnotation(cg, da);
+ if (constructedAnnotation==null) {
+ return; // error occurred (and was reported), do not continue
+ }
+
+ String nameComponent = da.declareAnnotationKind.name().toLowerCase();
+ String declareName = new StringBuilder("ajc$declare_at_").append(nameComponent).append("_").append(decCounter).toString();
+ LazyMethodGen declareMethod = new LazyMethodGen(Modifier.PUBLIC, Type.VOID, declareName, Type.NO_ARGS, EMPTY_STRINGS, cg);
+ InstructionList declareMethodBody = declareMethod.getBody();
+ declareMethodBody.append(InstructionFactory.RETURN);
+ declareMethod.addAnnotation(constructedAnnotation);
+
+ DeclareAnnotation deca = null;
+ ITokenSource tokenSource = BasicTokenSource.makeTokenSource(da.pattern,null);
+ PatternParser pp = new PatternParser(tokenSource);
+
+ if (da.declareAnnotationKind==DeclareAnnotationKind.Method || da.declareAnnotationKind==DeclareAnnotationKind.Field) {
+ ISignaturePattern isp = (da.declareAnnotationKind==DeclareAnnotationKind.Method?pp.parseCompoundMethodOrConstructorSignaturePattern(true):pp.parseCompoundFieldSignaturePattern());
+ deca = new DeclareAnnotation(da.declareAnnotationKind==DeclareAnnotationKind.Method?DeclareAnnotation.AT_METHOD:DeclareAnnotation.AT_FIELD, isp);
+ } else if (da.declareAnnotationKind==DeclareAnnotationKind.Type) {
+ TypePattern tp = pp.parseTypePattern();
+ deca = new DeclareAnnotation(DeclareAnnotation.AT_TYPE,tp);
+ }
+
+ deca.setAnnotationMethod(declareName);
+ deca.setAnnotationString(da.annotation);
+ AjAttribute attribute = new AjAttribute.DeclareAttribute(deca);
+ cg.addAttribute(attribute);
+ cg.addMethodGen(declareMethod);
+ }
+
+ /**
+ * Construct the annotation that the declare wants to add to the target.
+ */
+ private AnnotationAJ buildDeclareAnnotation_actualAnnotation(LazyClassGen cg, Definition.DeclareAnnotation da) {
+ AnnotationGen anno = buildAnnotationFromString(cg.getConstantPool(),cg.getWorld(),da.annotation);
+ if (anno==null) {
+ return null;
+ } else {
+ AnnotationAJ bcelAnnotation = new BcelAnnotation(anno, world);
+ return bcelAnnotation;
+ }
+ }
+
+ // TODO support array values
+ // TODO support annotation values
+ /**
+ * Build an AnnotationGen object based on a string, for example "@Foo(35,message='string')". Notice single quotes are fine for
+ * strings as they don't need special handling in the XML.
+ */
+ private AnnotationGen buildAnnotationFromString(ConstantPool cp, World w, String annotationString) {
+ int paren = annotationString.indexOf('(');
+ if (paren==-1) {
+ // the easy case, no values
+ AnnotationGen aaj = buildBaseAnnotationType(cp,world,annotationString);
+ return aaj;
+ } else {
+ // Discover the name and name/value pairs
+ String name = annotationString.substring(0,paren);
+ // break the rest into pieces based on the commas
+ List<String> values = new ArrayList<String>();
+ int pos = paren+1;
+ int depth = 0;
+ int len = annotationString.length();
+ int start = pos;
+ while (pos<len) {
+ char ch = annotationString.charAt(pos);
+ if (ch==')' && depth==0) {
+ break; // reached the end
+ }
+ if (ch=='(' || ch=='[') {
+ depth++;
+ } else if (ch==')' || ch==']') {
+ depth--;
+ }
+ if (ch==',' && depth==0) {
+ // found a comma at the right level to end a name/value pair
+ values.add(annotationString.substring(start,pos).trim());
+ start=pos+1;
+ }
+ pos++;
+ }
+ if (start != pos) {
+ // there is a last bit to add
+ values.add(annotationString.substring(start,pos).trim());
+ }
+ AnnotationGen aaj = buildBaseAnnotationType(cp,world,name);
+ if (aaj==null) {
+ return null; // a check failed
+ }
+ String typename = aaj.getTypeName();
+ ResolvedType type = UnresolvedType.forName(typename).resolve(world); // shame it isn't retrievable from the anno
+ ResolvedMember[] rms = type.getDeclaredMethods();
+ // Add the values
+ for (String value: values) {
+ int equalsIndex = value.indexOf("=");
+ String key = "value";
+ if (value.charAt(0)!='\"' && equalsIndex!=-1) {
+ key = value.substring(0,equalsIndex).trim();
+ value = value.substring(equalsIndex+1).trim();
+ }
+ boolean keyIsOk = false;
+ for (int m=0;m<rms.length;m++) {
+ NameValuePair nvp = null;
+ if (rms[m].getName().equals(key)) {
+ // found it!
+ keyIsOk=true;
+ UnresolvedType rt = rms[m].getReturnType();
+ if (rt.isPrimitiveType()) {
+ switch (rt.getSignature().charAt(0)) {
+ case 'J': // long
+ try {
+ long longValue = Long.parseLong(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_LONG,cp,longValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a long");
+ return null;
+ }
+ break;
+ case 'S': // short
+ try {
+ short shortValue = Short.parseShort(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_SHORT,cp,shortValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a short");
+ return null;
+ }
+ break;
+ case 'F': // float
+ try {
+ float floatValue = Float.parseFloat(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_FLOAT,cp,floatValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a float");
+ return null;
+ }
+ break;
+ case 'D': // double
+ try {
+ double doubleValue = Double.parseDouble(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_DOUBLE,cp,doubleValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a double");
+ return null;
+ }
+ break;
+ case 'I': // integer
+ try {
+ int intValue = Integer.parseInt(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_INT,cp,intValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as an integer");
+ return null;
+ }
+ break;
+ case 'B': // byte
+ try {
+ byte byteValue = Byte.parseByte(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BYTE,cp,byteValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a byte");
+ return null;
+ }
+ break;
+ case 'C': // char
+ if (value.length()<2) {
+ reportError("unable to interpret annotation value '"+value+"' as a char");
+ return null;
+ }
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_CHAR,cp,value.charAt(1)),cp);
+ break;
+ case 'Z': // boolean
+ try {
+ boolean booleanValue = Boolean.parseBoolean(value);
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.PRIMITIVE_BOOLEAN,cp,booleanValue),cp);
+ } catch (NumberFormatException nfe) {
+ reportError("unable to interpret annotation value '"+value+"' as a boolean");
+ return null;
+ }
+ break;
+ default:
+ reportError("not yet supporting XML setting of annotation values of type "+rt.getName());
+ return null;
+ }
+ } else if (UnresolvedType.JL_STRING.equals(rt)) {
+ if (value.length()<2) {
+ reportError("Invalid string value specified in annotation string: "+annotationString);
+ return null;
+ }
+ value = value.substring(1,value.length()-1); // trim the quotes off
+ nvp = new NameValuePair(key,new SimpleElementValue(ElementValue.STRING,cp,value),cp);
+ } else if (UnresolvedType.JL_CLASS.equals(rt)) {
+ // format of class string:
+ // Foo.class
+ // java.lang.Foo.class
+ if (value.length()<6) {
+ reportError("Not a well formed class value for an annotation '"+value+"'");
+ return null;
+ }
+ String clazz = value.substring(0,value.length()-6);
+ boolean qualified = clazz.indexOf(".")!=-1;
+ if (!qualified) {
+ // if not qualified, have to assume java.lang
+ clazz = "java.lang."+clazz;
+ }
+ nvp = new NameValuePair(key,new ClassElementValue(new ObjectType(clazz),cp),cp);
+ }
+ }
+ if (nvp!=null) {
+ aaj.addElementNameValuePair(nvp);
+ }
+ }
+ if (!keyIsOk) {
+ reportError("annotation @"+typename+" does not have a value named "+key);
+ return null;
+ }
+ }
+ return aaj;
+ }
+ }
+
+ private AnnotationGen buildBaseAnnotationType(ConstantPool cp,World world, String typename) {
+ String annoname = typename;
+ if (annoname.startsWith("@")) {
+ annoname= annoname.substring(1);
+ }
+ ResolvedType annotationType = UnresolvedType.forName(annoname).resolve(world);
+ if (!annotationType.isAnnotation()) {
+ reportError("declare is not specifying an annotation type :"+typename);
+ return null;
+ }
+ if (!annotationType.isAnnotationWithRuntimeRetention()) {
+ reportError("declare is using an annotation type that does not have runtime retention: "+typename);
+ return null;
+ }
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ return new AnnotationGen(new ObjectType(annoname), elems, true, cp);
+ }
+
+ /**
+ * Construct the annotation that indicates this is a declare
+ */
+
+ /**
+ * The PointcutAndAdvice object encapsulates an advice kind, a pointcut and names a Java method in a particular class. Generate
+ * an annotation style advice that has that pointcut whose implementation delegates to the Java method.
+ */
+ private void generateAdviceMethod(PointcutAndAdvice paa, int adviceCounter, LazyClassGen cg) {
+
+ // Check: Verify the class to be called does exist:
+ ResolvedType delegateClass = world.resolve(UnresolvedType.forName(paa.adviceClass));
+ if (delegateClass.isMissing()) {
+ reportError("Class to invoke cannot be found: '" + paa.adviceClass + "'");
+ return;
+ }
+
+ // Generate a name for this advice, includes advice kind plus a counter
+ String adviceName = new StringBuilder("generated$").append(paa.adviceKind.toString().toLowerCase()).append("$advice$")
+ .append(adviceCounter).toString();
+
+ // Build the annotation that encapsulates the pointcut
+ AnnotationAJ aaj = buildAdviceAnnotation(cg, paa);
+
+ // Chop up the supplied advice method string into its pieces.
+ // Example: foo(JoinPoint jp, java.lang.String string)
+ // JoinPoint and friends are recognized (so dont need fq package)
+ String method = paa.adviceMethod;
+
+ int paren = method.indexOf("(");
+ String methodName = method.substring(0, paren);
+ String signature = method.substring(paren);
+
+ // Check: signature looks ok
+ if (signature.charAt(0) != '(' || !signature.endsWith(")")) {
+ reportError("Badly formatted parameter signature: '" + method + "'");
+ return;
+ }
+
+ // Extract parameter types and names
+ List<Type> paramTypes = new ArrayList<Type>();
+ List<String> paramNames = new ArrayList<String>();
+ if (signature.charAt(1) != ')') {
+ // there are parameters to convert into a signature
+ StringBuilder convertedSignature = new StringBuilder("(");
+ boolean paramsBroken = false;
+ int pos = 1;
+ while (pos < signature.length() && signature.charAt(pos) != ')' && !paramsBroken) {
+ int nextChunkEndPos = signature.indexOf(',', pos);
+ if (nextChunkEndPos == -1) {
+ nextChunkEndPos = signature.indexOf(')', pos);
+ }
+ // chunk will be a type plus a space plus a name
+ String nextChunk = signature.substring(pos, nextChunkEndPos).trim();
+ int space = nextChunk.indexOf(" ");
+ ResolvedType resolvedParamType = null;
+ if (space == -1) {
+ // There is no parameter name, hopefully not needed!
+ if (nextChunk.equals("JoinPoint")) {
+ nextChunk = "org.aspectj.lang.JoinPoint";
+ } else if (nextChunk.equals("JoinPoint.StaticPart")) {
+ nextChunk = "org.aspectj.lang.JoinPoint$StaticPart";
+ } else if (nextChunk.equals("ProceedingJoinPoint")) {
+ nextChunk = "org.aspectj.lang.ProceedingJoinPoint";
+ }
+ UnresolvedType unresolvedParamType = UnresolvedType.forName(nextChunk);
+ resolvedParamType = world.resolve(unresolvedParamType);
+ } else {
+ String typename = nextChunk.substring(0, space);
+ if (typename.equals("JoinPoint")) {
+ typename = "org.aspectj.lang.JoinPoint";
+ } else if (typename.equals("JoinPoint.StaticPart")) {
+ typename = "org.aspectj.lang.JoinPoint$StaticPart";
+ } else if (typename.equals("ProceedingJoinPoint")) {
+ typename = "org.aspectj.lang.ProceedingJoinPoint";
+ }
+ UnresolvedType unresolvedParamType = UnresolvedType.forName(typename);
+ resolvedParamType = world.resolve(unresolvedParamType);
+ String paramname = nextChunk.substring(space).trim();
+ paramNames.add(paramname);
+ }
+ if (resolvedParamType.isMissing()) {
+ reportError("Cannot find type specified as parameter: '" + nextChunk + "' from signature '" + signature + "'");
+ paramsBroken = true;
+ }
+ paramTypes.add(Type.getType(resolvedParamType.getSignature()));
+ convertedSignature.append(resolvedParamType.getSignature());
+ pos = nextChunkEndPos + 1;
+ }
+ convertedSignature.append(")");
+ signature = convertedSignature.toString();
+ if (paramsBroken) {
+ return;
+ }
+ }
+
+ Type returnType = Type.VOID;
+
+ // If around advice we must find the actual delegate method and use its return type
+ if (paa.adviceKind == AdviceKind.Around) {
+ ResolvedMember[] methods = delegateClass.getDeclaredMethods();
+ ResolvedMember found = null;
+ for (ResolvedMember candidate : methods) {
+ if (candidate.getName().equals(methodName)) {
+ UnresolvedType[] cparms = candidate.getParameterTypes();
+ if (cparms.length == paramTypes.size()) {
+ boolean paramsMatch = true;
+ for (int i = 0; i < cparms.length; i++) {
+ if (!cparms[i].getSignature().equals(paramTypes.get(i).getSignature())) {
+ paramsMatch = false;
+ break;
+ }
+ }
+ if (paramsMatch) {
+ found = candidate;
+ break;
+ }
+ }
+ }
+ }
+ if (found != null) {
+ returnType = Type.getType(found.getReturnType().getSignature());
+ } else {
+ reportError("Unable to find method to invoke. In class: " + delegateClass.getName() + " cant find "
+ + paa.adviceMethod);
+ return;
+ }
+ }
+
+ // Time to construct the method itself:
+ LazyMethodGen advice = new LazyMethodGen(Modifier.PUBLIC, returnType, adviceName, paramTypes.toArray(new Type[paramTypes
+ .size()]), EMPTY_STRINGS, cg);
+
+ InstructionList adviceBody = advice.getBody();
+
+ // Generate code to load the parameters
+ int pos = 1; // first slot after 'this'
+ for (int i = 0; i < paramTypes.size(); i++) {
+ adviceBody.append(InstructionFactory.createLoad(paramTypes.get(i), pos));
+ pos += paramTypes.get(i).getSize();
+ }
+
+ // Generate the delegate call
+ adviceBody.append(cg.getFactory().createInvoke(paa.adviceClass, methodName, signature + returnType.getSignature(),
+ Constants.INVOKESTATIC));
+
+ // Generate the right return
+ if (returnType == Type.VOID) {
+ adviceBody.append(InstructionConstants.RETURN);
+ } else {
+ if (returnType.getSignature().length() < 2) {
+ String sig = returnType.getSignature();
+ if (sig.equals("F")) {
+ adviceBody.append(InstructionConstants.FRETURN);
+ } else if (sig.equals("D")) {
+ adviceBody.append(InstructionConstants.DRETURN);
+ } else if (sig.equals("J")) {
+ adviceBody.append(InstructionConstants.LRETURN);
+ } else {
+ adviceBody.append(InstructionConstants.IRETURN);
+ }
+ } else {
+ adviceBody.append(InstructionConstants.ARETURN);
+ }
+ }
+ // Add the annotation
+ advice.addAnnotation(aaj);
+ InstructionHandle start = adviceBody.getStart();
+
+ // Setup the local variable targeters so that the binding will work
+ String sig = concreteAspect.name.replace('.', '/');
+ start.addTargeter(new LocalVariableTag("L" + sig + ";", "this", 0, start.getPosition()));
+ if (paramNames.size() > 0) {
+ for (int i = 0; i < paramNames.size(); i++) {
+ start.addTargeter(new LocalVariableTag(paramTypes.get(i).getSignature(), paramNames.get(i), i + 1, start
+ .getPosition()));
+ }
+ }
+
+ // Record the new method in the class
+ cg.addMethodGen(advice);
+ }
+
+ /**
+ * For the given PointcutAndAdvice build the correct advice annotation.
+ */
+ private AnnotationAJ buildAdviceAnnotation(LazyClassGen cg, PointcutAndAdvice paa) {
+ SimpleElementValue svg = new SimpleElementValue(ElementValue.STRING, cg.getConstantPool(), paa.pointcut);
+ List<NameValuePair> elems = new ArrayList<NameValuePair>();
+ elems.add(new NameValuePair("value", svg, cg.getConstantPool()));
+ AnnotationGen mag = new AnnotationGen(new ObjectType("org/aspectj/lang/annotation/" + paa.adviceKind.toString()), elems,
+ true, cg.getConstantPool());
+ AnnotationAJ aaj = new BcelAnnotation(mag, world);
+ return aaj;
+ }
+
+ /**
+ * Error reporting
+ *
+ * @param message
+ */
+ private void reportError(String message) {
+ world.getMessageHandler().handleMessage(new Message(message, IMessage.ERROR, null, null));
+ }
+}
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultMessageHandler.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultMessageHandler.java
new file mode 100644
index 000000000..6589663fd
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultMessageHandler.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Alexandre Vasseur initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import org.aspectj.bridge.IMessageHandler;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.AbortException;
+
+/**
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class DefaultMessageHandler implements IMessageHandler {
+
+ boolean isVerbose = false;
+ boolean isDebug = false;
+ boolean showWeaveInfo = false;
+ boolean showWarn = true;
+
+ public boolean handleMessage(IMessage message) throws AbortException {
+ if (isIgnoring(message.getKind())) {
+ return false;
+ } else {
+ /*
+ * TODO maw We ship this class but don't use or document it. Changed
+ * to use stderr instead of stdout to allow improvements to LTW tests.
+ * Currently many pass whether or not LTW occurs because they are
+ * already woven. Some changed to check for appropriate weaving messages
+ * as well as absence of warnings or errors.
+ */
+ return SYSTEM_ERR.handleMessage(message);
+// if (message.getKind().isSameOrLessThan(IMessage.INFO)) {
+// return SYSTEM_OUT.handleMessage(message);
+// } else {
+// return SYSTEM_ERR.handleMessage(message);
+// }
+ }
+ }
+
+ public boolean isIgnoring(IMessage.Kind kind) {
+ if (kind.equals(IMessage.WEAVEINFO)) {
+ return !showWeaveInfo;
+ }
+ if (kind.isSameOrLessThan(IMessage.INFO)) {
+ return !isVerbose;
+ }
+ if (kind.isSameOrLessThan(IMessage.DEBUG)) {
+ return !isDebug;
+ }
+ return !showWarn;
+ }
+
+ public void dontIgnore(IMessage.Kind kind) {
+ if (kind.equals(IMessage.WEAVEINFO)) {
+ showWeaveInfo = true;
+ } else if (kind.equals(IMessage.DEBUG)) {
+ isVerbose = true;
+ } else if (kind.equals(IMessage.WARNING)) {
+ showWarn = false;
+ }
+ }
+
+ public void ignore(IMessage.Kind kind) {
+ if (kind.equals(IMessage.WEAVEINFO)) {
+ showWeaveInfo = false;
+ } else if (kind.equals(IMessage.DEBUG)) {
+ isVerbose = false;
+ } else if (kind.equals(IMessage.WARNING)) {
+ showWarn = true;
+ }
+ }
+
+}
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultWeavingContext.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultWeavingContext.java
new file mode 100644
index 000000000..8e8d9df77
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/DefaultWeavingContext.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * David Knibb initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
+import org.aspectj.weaver.loadtime.definition.Definition;
+import org.aspectj.weaver.tools.Trace;
+import org.aspectj.weaver.tools.TraceFactory;
+import org.aspectj.weaver.tools.WeavingAdaptor;
+
+/**
+ * Use in non-OSGi environment
+ *
+ * @author David Knibb
+ */
+public class DefaultWeavingContext implements IWeavingContext {
+
+ protected BcelWeakClassLoaderReference loaderRef;
+ private String shortName;
+
+ private static Trace trace = TraceFactory.getTraceFactory().getTrace(DefaultWeavingContext.class);
+
+ /**
+ * Construct a new WeavingContext to use the specified ClassLoader This is the constructor which should be used.
+ *
+ * @param loader
+ */
+ public DefaultWeavingContext(ClassLoader loader) {
+ super();
+ this.loaderRef = new BcelWeakClassLoaderReference(loader);
+ }
+
+ /**
+ * Same as ClassLoader.getResources()
+ */
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return getClassLoader().getResources(name);
+ }
+
+ /**
+ * @return null as we are not in an OSGi environment (therefore no bundles)
+ */
+ public String getBundleIdFromURL(URL url) {
+ return "";
+ }
+
+ /**
+ * @return classname@hashcode
+ */
+ public String getClassLoaderName() {
+ ClassLoader loader = getClassLoader();
+ return ((loader != null) ? loader.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(loader))
+ : "null");
+ }
+
+ public ClassLoader getClassLoader() {
+ return loaderRef.getClassLoader();
+ }
+
+ /**
+ * @return filename
+ */
+ public String getFile(URL url) {
+ return url.getFile();
+ }
+
+ /**
+ * @return unqualifiedclassname@hashcode
+ */
+ public String getId() {
+ if (shortName == null) {
+ shortName = getClassLoaderName().replace('$', '.');
+ int index = shortName.lastIndexOf(".");
+ if (index != -1) {
+ shortName = shortName.substring(index + 1);
+ }
+ }
+ return shortName;
+ }
+
+ public String getSuffix() {
+ return getClassLoaderName();
+ }
+
+ public boolean isLocallyDefined(String classname) {
+ String asResource = classname.replace('.', '/').concat(".class");
+ ClassLoader loader = getClassLoader();
+ URL localURL = loader.getResource(asResource);
+ if (localURL == null) {
+ return false;
+ }
+
+ boolean isLocallyDefined = true;
+
+ ClassLoader parent = loader.getParent();
+ if (parent != null) {
+ URL parentURL = parent.getResource(asResource);
+ if (localURL.equals(parentURL)) {
+ isLocallyDefined = false;
+ }
+ }
+ return isLocallyDefined;
+ }
+
+ /**
+ * Simply call weaving adaptor back to parse aop.xml
+ *
+ * @param weaver
+ * @param loader
+ */
+ public List<Definition> getDefinitions(final ClassLoader loader, final WeavingAdaptor adaptor) {
+ if (trace.isTraceEnabled()) {
+ trace.enter("getDefinitions", this, new Object[] { "goo", adaptor });
+ }
+
+ List<Definition> definitions = ((ClassLoaderWeavingAdaptor) adaptor).parseDefinitions(loader);
+
+ if (trace.isTraceEnabled()) {
+ trace.exit("getDefinitions", definitions);
+ }
+ return definitions;
+ }
+}
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/JRockitAgent.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/JRockitAgent.java
new file mode 100644
index 000000000..56ad0e958
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/JRockitAgent.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthew Webster - initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.util.Stack;
+
+import com.bea.jvm.ClassLibrary;
+import com.bea.jvm.JVMFactory;
+
+/**
+ * BEA JRocket JMAPI agent.
+ *
+ * Use "-Xmanagement:class=org.aspectj.weaver.loadtime.JRockitAgent"
+ */
+public class JRockitAgent implements com.bea.jvm.ClassPreProcessor {
+
+ private ClassPreProcessor preProcessor;
+
+ /*
+ * This is used to implement the recursion protection offered by JVMTI but not by JRockit JMAPI. I we are called to preProcess a
+ * class while already preProcessing another we will return immediately
+ */
+ private static ThreadLocalStack stack = new ThreadLocalStack();
+
+ public JRockitAgent() {
+ this.preProcessor = new Aj();
+
+ ClassLibrary cl = JVMFactory.getJVM().getClassLibrary();
+ cl.setClassPreProcessor(this);
+ }
+
+ public byte[] preProcess(ClassLoader loader, String className, byte[] bytes) {
+ byte[] newBytes = bytes;
+
+ if (stack.empty()) {
+ stack.push(className);
+ newBytes = preProcessor.preProcess(className, bytes, loader, null);
+ stack.pop();
+ }
+
+ return newBytes;
+ }
+
+ private static class ThreadLocalStack extends ThreadLocal {
+
+ public boolean empty() {
+ Stack stack = (Stack) get();
+ return stack.empty();
+ }
+
+ public Object peek() {
+ Object obj = null;
+ Stack stack = (Stack) get();
+ if (!stack.empty())
+ obj = stack.peek();
+ return obj;
+ }
+
+ public void push(Object obj) {
+ Stack stack = (Stack) get();
+ if (!stack.empty() && obj == stack.peek())
+ throw new RuntimeException(obj.toString());
+ stack.push(obj);
+ }
+
+ public Object pop() {
+ Stack stack = (Stack) get();
+ return stack.pop();
+ }
+
+ protected Object initialValue() {
+ return new Stack();
+ }
+ }
+
+}
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/Options.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/Options.java
new file mode 100644
index 000000000..0c9611e4f
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/Options.java
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2005 Contributors.
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Alexandre Vasseur initial implementation
+ *******************************************************************************/
+package org.aspectj.weaver.loadtime;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.IMessageHandler;
+import org.aspectj.bridge.Message;
+import org.aspectj.util.LangUtil;
+
+/**
+ * A class that hanldes LTW options. Note: AV - I choosed to not reuse AjCompilerOptions and alike since those implies too many
+ * dependancies on jdt and ajdt modules.
+ *
+ * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
+ */
+public class Options {
+
+ private final static String OPTION_15 = "-1.5";
+ private final static String OPTION_lazyTjp = "-XlazyTjp";
+ private final static String OPTION_noWarn = "-nowarn";
+ private final static String OPTION_noWarnNone = "-warn:none";
+ private final static String OPTION_proceedOnError = "-proceedOnError";
+ private final static String OPTION_verbose = "-verbose";
+ private final static String OPTION_debug = "-debug";
+ private final static String OPTION_reweavable = "-Xreweavable";// notReweavable is default for LTW
+ private final static String OPTION_noinline = "-Xnoinline";
+ private final static String OPTION_addSerialVersionUID = "-XaddSerialVersionUID";
+ private final static String OPTION_hasMember = "-XhasMember";
+ private final static String OPTION_pinpoint = "-Xdev:pinpoint";
+ private final static String OPTION_showWeaveInfo = "-showWeaveInfo";
+ private final static String OPTIONVALUED_messageHandler = "-XmessageHandlerClass:";
+ private static final String OPTIONVALUED_Xlintfile = "-Xlintfile:";
+ private static final String OPTIONVALUED_Xlint = "-Xlint:";
+ private static final String OPTIONVALUED_joinpoints = "-Xjoinpoints:";
+ private static final String OPTIONVALUED_Xset = "-Xset:";
+ private static final String OPTION_timers = "-timers";
+ private static final String OPTIONVALUED_loadersToSkip = "-loadersToSkip:";
+
+ public static WeaverOption parse(String options, ClassLoader laoder, IMessageHandler imh) {
+ WeaverOption weaverOption = new WeaverOption(imh);
+
+ if (LangUtil.isEmpty(options)) {
+ return weaverOption;
+ }
+ // the first option wins
+ List<String> flags = LangUtil.anySplit(options, " ");
+ Collections.reverse(flags);
+
+ // do a first round on the message handler since it will report the options themselves
+ for (Iterator<String> iterator = flags.iterator(); iterator.hasNext();) {
+ String arg = iterator.next();
+ if (arg.startsWith(OPTIONVALUED_messageHandler)) {
+ if (arg.length() > OPTIONVALUED_messageHandler.length()) {
+ String handlerClass = arg.substring(OPTIONVALUED_messageHandler.length()).trim();
+ try {
+ Class handler = Class.forName(handlerClass, false, laoder);
+ weaverOption.messageHandler = ((IMessageHandler) handler.newInstance());
+ } catch (Throwable t) {
+ weaverOption.messageHandler.handleMessage(new Message("Cannot instantiate message handler " + handlerClass,
+ IMessage.ERROR, t, null));
+ }
+ }
+ }
+ }
+
+ // configure the other options
+ for (Iterator<String> iterator = flags.iterator(); iterator.hasNext();) {
+ String arg = (String) iterator.next();
+ if (arg.equals(OPTION_15)) {
+ weaverOption.java5 = true;
+ } else if (arg.equalsIgnoreCase(OPTION_lazyTjp)) {
+ weaverOption.lazyTjp = true;
+ } else if (arg.equalsIgnoreCase(OPTION_noinline)) {
+ weaverOption.noInline = true;
+ } else if (arg.equalsIgnoreCase(OPTION_addSerialVersionUID)) {
+ weaverOption.addSerialVersionUID = true;
+ } else if (arg.equalsIgnoreCase(OPTION_noWarn) || arg.equalsIgnoreCase(OPTION_noWarnNone)) {
+ weaverOption.noWarn = true;
+ } else if (arg.equalsIgnoreCase(OPTION_proceedOnError)) {
+ weaverOption.proceedOnError = true;
+ } else if (arg.equalsIgnoreCase(OPTION_reweavable)) {
+ weaverOption.notReWeavable = false;
+ } else if (arg.equalsIgnoreCase(OPTION_showWeaveInfo)) {
+ weaverOption.showWeaveInfo = true;
+ } else if (arg.equalsIgnoreCase(OPTION_hasMember)) {
+ weaverOption.hasMember = true;
+ } else if (arg.startsWith(OPTIONVALUED_joinpoints)) {
+ if (arg.length() > OPTIONVALUED_joinpoints.length()) {
+ weaverOption.optionalJoinpoints = arg.substring(OPTIONVALUED_joinpoints.length()).trim();
+ }
+ } else if (arg.equalsIgnoreCase(OPTION_verbose)) {
+ weaverOption.verbose = true;
+ } else if (arg.equalsIgnoreCase(OPTION_debug)) {
+ weaverOption.debug = true;
+ } else if (arg.equalsIgnoreCase(OPTION_pinpoint)) {
+ weaverOption.pinpoint = true;
+ } else if (arg.startsWith(OPTIONVALUED_messageHandler)) {
+ // handled in first round
+ } else if (arg.startsWith(OPTIONVALUED_Xlintfile)) {
+ if (arg.length() > OPTIONVALUED_Xlintfile.length()) {
+ weaverOption.lintFile = arg.substring(OPTIONVALUED_Xlintfile.length()).trim();
+ }
+ } else if (arg.startsWith(OPTIONVALUED_Xlint)) {
+ if (arg.length() > OPTIONVALUED_Xlint.length()) {
+ weaverOption.lint = arg.substring(OPTIONVALUED_Xlint.length()).trim();
+ }
+ } else if (arg.startsWith(OPTIONVALUED_Xset)) {
+ if (arg.length() > OPTIONVALUED_Xlint.length()) {
+ weaverOption.xSet = arg.substring(OPTIONVALUED_Xset.length()).trim();
+ }
+ } else if (arg.equalsIgnoreCase(OPTION_timers)) {
+ weaverOption.timers = true;
+ } else if (arg.startsWith(OPTIONVALUED_loadersToSkip)) {
+ if (arg.length() > OPTIONVALUED_loadersToSkip.length()) {
+ String value = arg.substring(OPTIONVALUED_loadersToSkip.length()).trim();
+ weaverOption.loadersToSkip = value;
+ }
+ } else {
+ weaverOption.messageHandler.handleMessage(new Message("Cannot configure weaver with option '" + arg
+ + "': unknown option", IMessage.WARNING, null, null));
+ }
+ }
+
+ // refine message handler configuration
+ if (weaverOption.noWarn) {
+ weaverOption.messageHandler.ignore(IMessage.WARNING);
+ }
+ if (weaverOption.verbose) {
+ weaverOption.messageHandler.dontIgnore(IMessage.INFO);
+ }
+ if (weaverOption.debug) {
+ weaverOption.messageHandler.dontIgnore(IMessage.DEBUG);
+ }
+ if (weaverOption.showWeaveInfo) {
+ weaverOption.messageHandler.dontIgnore(IMessage.WEAVEINFO);
+ }
+
+ return weaverOption;
+ }
+
+ public static class WeaverOption {
+ boolean java5;
+ boolean lazyTjp;
+ boolean hasMember;
+ boolean timers = false;
+ String optionalJoinpoints;
+ boolean noWarn;
+ boolean proceedOnError;
+ boolean verbose;
+ boolean debug;
+ boolean notReWeavable = true;// default to notReweavable for LTW (faster)
+ boolean noInline;
+ boolean addSerialVersionUID;
+ boolean showWeaveInfo;
+ boolean pinpoint;
+ IMessageHandler messageHandler;
+ String lint;
+ String lintFile;
+ String xSet;
+ String loadersToSkip;
+
+ public WeaverOption(IMessageHandler imh) {
+ // messageHandler = new DefaultMessageHandler();//default
+ this.messageHandler = imh;
+ }
+ }
+}
diff --git a/loadtime/src/main/java/org/aspectj/weaver/loadtime/WeavingURLClassLoader.java b/loadtime/src/main/java/org/aspectj/weaver/loadtime/WeavingURLClassLoader.java
new file mode 100644
index 000000000..f6b013373
--- /dev/null
+++ b/loadtime/src/main/java/org/aspectj/weaver/loadtime/WeavingURLClassLoader.java
@@ -0,0 +1,232 @@
+/* *******************************************************************
+ * Copyright (c) 2004 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthew Webster, Adrian Colyer,
+ * Martin Lippert initial implementation
+ * Andy Clement
+ * Abraham Nevado
+ * ******************************************************************/
+
+package org.aspectj.weaver.loadtime;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.aspectj.bridge.AbortException;
+import org.aspectj.weaver.bcel.ExtensibleURLClassLoader;
+import org.aspectj.weaver.tools.Trace;
+import org.aspectj.weaver.tools.TraceFactory;
+import org.aspectj.weaver.tools.WeavingAdaptor;
+import org.aspectj.weaver.tools.WeavingClassLoader;
+
+public class WeavingURLClassLoader extends ExtensibleURLClassLoader implements WeavingClassLoader {
+
+ public static final String WEAVING_CLASS_PATH = "aj.class.path";
+ public static final String WEAVING_ASPECT_PATH = "aj.aspect.path";
+
+ private URL[] aspectURLs;
+ private WeavingAdaptor adaptor;
+ private boolean initializingAdaptor;
+ private Map generatedClasses = new HashMap(); /* String -> byte[] */
+
+ private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingURLClassLoader.class);
+
+ /*
+ * This constructor is needed when using "-Djava.system.class.loader".
+ */
+ public WeavingURLClassLoader(ClassLoader parent) {
+ this(getURLs(getClassPath()), getURLs(getAspectPath()), parent);
+ // System.out.println("? WeavingURLClassLoader.WeavingURLClassLoader()");
+ }
+
+ public WeavingURLClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ if (trace.isTraceEnabled())
+ trace.enter("<init>", this, new Object[] { urls, parent });
+ // System.out.println("WeavingURLClassLoader.WeavingURLClassLoader()");
+ if (trace.isTraceEnabled())
+ trace.exit("<init>");
+ }
+
+ public WeavingURLClassLoader(URL[] classURLs, URL[] aspectURLs, ClassLoader parent) {
+ super(classURLs, parent);
+ // System.out.println("> WeavingURLClassLoader.WeavingURLClassLoader() classURLs=" + Arrays.asList(classURLs));
+ this.aspectURLs = aspectURLs;
+
+ /*
+ * If either we nor our parent is using an ASPECT_PATH use a new-style adaptor
+ */
+ if (this.aspectURLs.length > 0 || getParent() instanceof WeavingClassLoader) {
+ try {
+ adaptor = new WeavingAdaptor(this);
+ } catch (ExceptionInInitializerError ex) {
+ ex.printStackTrace(System.out);
+ throw ex;
+ }
+ }
+ // System.out.println("< WeavingURLClassLoader.WeavingURLClassLoader() adaptor=" + adaptor);
+ }
+
+ private static String getAspectPath() {
+ return System.getProperty(WEAVING_ASPECT_PATH, "");
+ }
+
+ private static String getClassPath() {
+ return System.getProperty(WEAVING_CLASS_PATH, "");
+ }
+
+ private static URL[] getURLs(String path) {
+ List urlList = new ArrayList();
+ for (StringTokenizer t = new StringTokenizer(path, File.pathSeparator); t.hasMoreTokens();) {
+ File f = new File(t.nextToken().trim());
+ try {
+ if (f.exists()) {
+ URL url = f.toURL();
+ if (url != null)
+ urlList.add(url);
+ }
+ } catch (MalformedURLException e) {
+ }
+ }
+
+ URL[] urls = new URL[urlList.size()];
+ urlList.toArray(urls);
+ return urls;
+ }
+
+ protected void addURL(URL url) {
+ if (adaptor == null) {
+ createAdaptor();
+ }
+ adaptor.addURL(url);
+ super.addURL(url);
+ }
+
+ /**
+ * Override to weave class using WeavingAdaptor
+ */
+ protected Class defineClass(String name, byte[] b, CodeSource cs) throws IOException {
+ if (trace.isTraceEnabled())
+ trace.enter("defineClass", this, new Object[] { name, b, cs });
+ // System.err.println("? WeavingURLClassLoader.defineClass(" + name + ", [" + b.length + "])");
+ byte orig[] = b;
+ /* Avoid recursion during adaptor initialization */
+ if (!initializingAdaptor) {
+
+ /* Need to defer creation because of possible recursion during constructor execution */
+ if (adaptor == null && !initializingAdaptor) {
+ createAdaptor();
+ }
+
+ try {
+ b = adaptor.weaveClass(name, b, false);
+ } catch (AbortException ex) {
+ trace.error("defineClass", ex);
+ throw ex;
+ } catch (Throwable th) {
+ trace.error("defineClass", th);
+ }
+ }
+ Class clazz;
+
+ // On error, define the original form of the class and log the issue
+ try {
+ clazz= super.defineClass(name, b, cs);
+ } catch (Throwable th) {
+ trace.error("Weaving class problem. Original class has been returned. The error was caused because of: " + th, th);
+ clazz= super.defineClass(name, orig, cs);
+ }
+ if (trace.isTraceEnabled())
+ trace.exit("defineClass", clazz);
+ return clazz;
+ }
+
+ private void createAdaptor() {
+ DefaultWeavingContext weavingContext = new DefaultWeavingContext(this) {
+
+ /* Ensures consistent LTW messages for testing */
+ public String getClassLoaderName() {
+ ClassLoader loader = getClassLoader();
+ return loader.getClass().getName();
+ }
+
+ };
+
+ ClassLoaderWeavingAdaptor clwAdaptor = new ClassLoaderWeavingAdaptor();
+ initializingAdaptor = true;
+ clwAdaptor.initialize(this, weavingContext);
+ initializingAdaptor = false;
+ adaptor = clwAdaptor;
+ }
+
+ /**
+ * Override to find classes generated by WeavingAdaptor
+ */
+ protected byte[] getBytes(String name) throws IOException {
+ byte[] bytes = super.getBytes(name);
+
+ if (bytes == null) {
+ // return adaptor.findClass(name);
+ return (byte[]) generatedClasses.remove(name);
+ }
+
+ return bytes;
+ }
+
+ /**
+ * Implement method from WeavingClassLoader
+ */
+ public URL[] getAspectURLs() {
+ return aspectURLs;
+ }
+
+ public void acceptClass (String name, byte[] classBytes, byte[] weavedBytes) {
+ generatedClasses.put(name, weavedBytes);
+ }
+
+ // protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ // System.err.println("> WeavingURLClassLoader.loadClass() name=" + name);
+ // Class clazz= super.loadClass(name, resolve);
+ // System.err.println("< WeavingURLClassLoader.loadClass() clazz=" + clazz + ", loader=" + clazz.getClassLoader());
+ // return clazz;
+ // }
+
+ // private interface ClassPreProcessorAdaptor extends ClassPreProcessor {
+ // public void addURL(URL url);
+ // }
+ //
+ // private class WeavingAdaptorPreProcessor implements ClassPreProcessorAdaptor {
+ //
+ // private WeavingAdaptor adaptor;
+ //
+ // public WeavingAdaptorPreProcessor (WeavingClassLoader wcl) {
+ // adaptor = new WeavingAdaptor(wcl);
+ // }
+ //
+ // public void initialize() {
+ // }
+ //
+ // public byte[] preProcess(String className, byte[] bytes, ClassLoader classLoader) {
+ // return adaptor.weaveClass(className,bytes);
+ // }
+ //
+ // public void addURL(URL url) {
+ //
+ // }
+ // }
+
+}