/* ******************************************************************* * 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 v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * Matthew Webster, Adrian Colyer, John Kew + Lyor Goldstein (caching) * Martin Lippert initial implementation * ******************************************************************/ package org.aspectj.weaver.tools; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; 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.Properties; import java.util.Set; import java.util.StringTokenizer; import org.aspectj.bridge.AbortException; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.IMessage.Kind; import org.aspectj.bridge.IMessageContext; import org.aspectj.bridge.IMessageHandler; import org.aspectj.bridge.IMessageHolder; import org.aspectj.bridge.Message; import org.aspectj.bridge.MessageHandler; import org.aspectj.bridge.MessageUtil; import org.aspectj.bridge.MessageWriter; import org.aspectj.bridge.Version; import org.aspectj.bridge.WeaveMessage; import org.aspectj.util.FileUtil; import org.aspectj.util.LangUtil; import org.aspectj.weaver.IClassFileProvider; import org.aspectj.weaver.IUnwovenClassFile; import org.aspectj.weaver.IWeaveRequestor; import org.aspectj.weaver.World; import org.aspectj.weaver.bcel.BcelObjectType; import org.aspectj.weaver.bcel.BcelWeaver; import org.aspectj.weaver.bcel.BcelWorld; import org.aspectj.weaver.bcel.UnwovenClassFile; import org.aspectj.weaver.tools.cache.CachedClassEntry; import org.aspectj.weaver.tools.cache.CachedClassReference; import org.aspectj.weaver.tools.cache.SimpleCache; import org.aspectj.weaver.tools.cache.SimpleCacheFactory; import org.aspectj.weaver.tools.cache.WeavedClassCache; // OPTIMIZE add guards for all the debug/info/etc /** * This adaptor allows the AspectJ compiler to be embedded in an existing system to facilitate load-time weaving. It provides an * interface for a weaving class loader to provide a classpath to be woven by a set of aspects. A callback is supplied to allow a * class loader to define classes generated by the compiler during the weaving process. *

* A weaving class loader should create a WeavingAdaptor before any classes are defined, typically during construction. * The set of aspects passed to the adaptor is fixed for the lifetime of the adaptor although the classpath can be augmented. A * system property can be set to allow verbose weaving messages to be written to the console. * */ public class WeavingAdaptor implements IMessageContext { /** * System property used to turn on verbose weaving messages */ public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose"; public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo"; public static final String TRACE_MESSAGES_PROPERTY = "org.aspectj.tracing.messages"; private final static String ASPECTJ_BASE_PACKAGE = "org.aspectj."; private final static String PACKAGE_INITIAL_CHARS = ASPECTJ_BASE_PACKAGE.charAt(0) + "sj"; private boolean enabled = false; protected boolean verbose = getVerbose(); protected BcelWorld bcelWorld; protected BcelWeaver weaver; private IMessageHandler messageHandler; private WeavingAdaptorMessageHolder messageHolder; private boolean abortOnError = false; protected GeneratedClassHandler generatedClassHandler; protected Map generatedClasses = new HashMap<>(); public BcelObjectType delegateForCurrentClass; // lazily initialized, should be used to prevent parsing bytecode multiple // times protected ProtectionDomain activeProtectionDomain; private boolean haveWarnedOnJavax = false; protected WeavedClassCache cache; private int weavingSpecialTypes = 0; private static final int INITIALIZED = 0x1; private static final int WEAVE_JAVA_PACKAGE = 0x2; private static final int WEAVE_JAVAX_PACKAGE = 0x4; private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingAdaptor.class); protected WeavingAdaptor() { } /** * Construct a WeavingAdaptor with a reference to a weaving class loader. The adaptor will automatically search the class loader * hierarchy to resolve classes. The adaptor will also search the hierarchy for WeavingClassLoader instances to determine the * set of aspects to be used for weaving. * * @param loader instance of ClassLoader */ public WeavingAdaptor(WeavingClassLoader loader) { // System.err.println("? WeavingAdaptor.(" + loader +"," + aspectURLs.length + ")"); generatedClassHandler = loader; init((ClassLoader)loader, getFullClassPath((ClassLoader) loader), getFullAspectPath((ClassLoader) loader/* ,aspectURLs */)); } /** * Construct a WeavingAdaptor with a reference to a GeneratedClassHandler, a full search path for resolving classes * and a complete set of aspects. The search path must include classes loaded by the class loader constructing the * WeavingAdaptor and all its parents in the hierarchy. * * @param handler GeneratedClassHandler * @param classURLs the URLs from which to resolve classes * @param aspectURLs the aspects used to weave classes defined by this class loader */ public WeavingAdaptor(GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) { // System.err.println("? WeavingAdaptor.()"); generatedClassHandler = handler; init(null, FileUtil.makeClasspath(classURLs), FileUtil.makeClasspath(aspectURLs)); } protected List getFullClassPath(ClassLoader loader) { List list = new LinkedList<>(); for (; loader != null; loader = loader.getParent()) { if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) loader).getURLs(); list.addAll(0, FileUtil.makeClasspath(urls)); } else { warn("cannot determine classpath"); } } // On Java9 it is possible to fail to find a URLClassLoader from which to derive a suitable classpath // For now we can determine it from the java.class.path: if (LangUtil.isVMGreaterOrEqual(9)) { list.add(0, LangUtil.getJrtFsFilePath()); List javaClassPathEntries = makeClasspath(System.getProperty("java.class.path")); for (int i=javaClassPathEntries.size()-1;i>=0;i--) { String javaClassPathEntry = javaClassPathEntries.get(i); if (!list.contains(javaClassPathEntry)) { list.add(0,javaClassPathEntry); } } } // On Java9 the sun.boot.class.path won't be set. System classes accessible through JRT filesystem list.addAll(0, makeClasspath(System.getProperty("sun.boot.class.path"))); return list; } private List getFullAspectPath(ClassLoader loader) { List list = new LinkedList<>(); for (; loader != null; loader = loader.getParent()) { if (loader instanceof WeavingClassLoader) { URL[] urls = ((WeavingClassLoader) loader).getAspectURLs(); list.addAll(0, FileUtil.makeClasspath(urls)); } } return list; } private static boolean getVerbose() { try { return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE); } catch (Throwable t) { // security exception return false; } } /** * Initialize the WeavingAdapter * @param loader ClassLoader used by this adapter; which can be null * @param classPath classpath of this adapter * @param aspectPath list of aspect paths */ private void init(ClassLoader loader, List classPath, List aspectPath) { abortOnError = true; createMessageHandler(); info("using classpath: " + classPath); info("using aspectpath: " + aspectPath); bcelWorld = new BcelWorld(classPath, messageHandler, null); bcelWorld.setXnoInline(false); bcelWorld.getLint().loadDefaultProperties(); bcelWorld.setBehaveInJava5Way(true); weaver = new BcelWeaver(bcelWorld); registerAspectLibraries(aspectPath); initializeCache(loader, aspectPath, null, getMessageHandler()); enabled = true; } /** * If the cache is enabled, initialize it and swap out the existing classhandler * for the caching one - * * @param loader classloader for this adapter, may be null * @param aspects List of strings representing aspects managed by the adapter; these could be urls or classnames * @param existingClassHandler current class handler * @param myMessageHandler current message handler */ protected void initializeCache(ClassLoader loader, List aspects, GeneratedClassHandler existingClassHandler, IMessageHandler myMessageHandler) { if (WeavedClassCache.isEnabled()) { cache = WeavedClassCache.createCache(loader, aspects, existingClassHandler, myMessageHandler); // Wrap the existing class handler so that any generated classes are also cached if (cache != null) { this.generatedClassHandler = cache.getCachingClassHandler(); } } } protected void createMessageHandler() { messageHolder = new WeavingAdaptorMessageHolder(new PrintWriter(System.err)); messageHandler = messageHolder; if (verbose) { messageHandler.dontIgnore(IMessage.INFO); } if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) { messageHandler.dontIgnore(IMessage.WEAVEINFO); } info("AspectJ Weaver Version " + Version.getText() + " built on " + Version.getTimeText()); //$NON-NLS-1$ } protected IMessageHandler getMessageHandler() { return messageHandler; } public IMessageHolder getMessageHolder() { return messageHolder; } protected void setMessageHandler(IMessageHandler mh) { if (mh instanceof ISupportsMessageContext) { ISupportsMessageContext smc = (ISupportsMessageContext) mh; smc.setMessageContext(this); } if (mh != messageHolder) { messageHolder.setDelegate(mh); } messageHolder.flushMessages(); } protected void disable() { if (trace.isTraceEnabled()) { trace.enter("disable", this); } enabled = false; messageHolder.flushMessages(); if (trace.isTraceEnabled()) { trace.exit("disable"); } } protected void enable() { enabled = true; messageHolder.flushMessages(); } protected boolean isEnabled() { return enabled; } /** * Appends URL to path used by the WeavingAdptor to resolve classes * * @param url to be appended to search path */ public void addURL(URL url) { File libFile = new File(url.getPath()); try { weaver.addLibraryJarFile(libFile); } catch (IOException ex) { warn("bad library: '" + libFile + "'"); } } /** * Weave a class using aspects previously supplied to the adaptor. * * @param name the name of the class * @param bytes the class bytes * @return the woven bytes * @exception IOException weave failed */ public byte[] weaveClass(String name, byte[] bytes) throws IOException { return weaveClass(name, bytes, false); } // Track if the weaver is already running on this thread - don't allow re-entrant calls private ThreadLocal weaverRunning = new ThreadLocal() { @Override protected Boolean initialValue() { return Boolean.FALSE; } }; /** * Weave a class using aspects previously supplied to the adaptor. * * @param name the name of the class in the internal form of fully qualified class and interface names as defined * in The Java Virtual Machine Specification. For example, "java/util/List". * @param bytes the input byte buffer in class file format - must not be modified * @param mustWeave if true then this class must get woven (used for concrete aspects generated from XML) * * @return a well-formed class file buffer (the weaving result), or {@code null} if no weaving was performed * * @throws IOException weave failed * * @see java.lang.instrument.ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte[]) */ public byte[] weaveClass(String name, final byte[] bytes, boolean mustWeave) throws IOException { if (trace == null) { // Pr231945: we are likely to be under tomcat and ENABLE_CLEAR_REFERENCES hasn't been set System.err .println("AspectJ Weaver cannot continue to weave, static state has been cleared. Are you under Tomcat? In order to weave '" + name + "' during shutdown, 'org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false' must be set (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=231945)."); return null; } if (weaverRunning.get()) { // System.out.println("AJC: avoiding re-entrant call to transform " + name); return null; } try { byte[] newBytes = null; weaverRunning.set(true); if (trace.isTraceEnabled()) { trace.enter("weaveClass", this, new Object[] { name, bytes }); } if (!enabled) { if (trace.isTraceEnabled()) { trace.exit("weaveClass", false); } return null; } boolean debugOn = !messageHandler.isIgnoring(Message.DEBUG); name = name.replace('/', '.'); byte[] wovenBytes = wovenWithGeneratedClass(name); if (wovenBytes != null) { if (debugOn) { debug("returning woven bytes for '" + name + "' that were generated by a previous weaving process"); } return wovenBytes; } try { delegateForCurrentClass = null; if (shouldWeaveName(name)) { if (accept(name, bytes)) { // Determine if we have the weaved class cached CachedClassReference cacheKey = null; if (cache != null && !mustWeave) { cacheKey = cache.createCacheKey(name, bytes); CachedClassEntry entry = cache.get(cacheKey, bytes); if (entry != null) { // If the entry has been explicitly ignored // return the original bytes if (entry.isIgnored()) { return null; } return entry.getBytes(); } } // TODO @AspectJ problem // Annotation style aspects need to be included regardless in order to get // a valid aspectOf()/hasAspect() generated in them. However - if they are excluded // (via include/exclude in aop.xml) they really should only get aspectOf()/hasAspect() // and not be included in the full set of aspects being applied by 'this' weaver if (debugOn) { debug("weaving '" + name + "'"); } newBytes = getWovenBytes(name, bytes); // TODO: Is this OK performance-wise? if (Arrays.equals(bytes, newBytes)) { // null means unchanged in java.lang.instrument.ClassFileTransformer::transform newBytes = null; } // temporarily out - searching for @Aspect annotated types is a slow thing to do - we should // expect the user to name them if they want them woven - just like code style // } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) { // if (mustWeave) { // if (bcelWorld.getLint().mustWeaveXmlDefinedAspects.isEnabled()) { // bcelWorld.getLint().mustWeaveXmlDefinedAspects.signal(name, null); // } // } // // an @AspectJ aspect needs to be at least munged by the aspectOf munger // if (debugOn) { // debug("weaving '" + name + "'"); // } // bytes = getAtAspectJAspectBytes(name, bytes); // Add the weaved class to the cache only if there // has been an actual change // JVK: Is there a better way to check if the class has // been transformed without carrying up some value // from the depths? if (cacheKey != null) { // If no transform has been applied, mark the class // as ignored. if (newBytes == null) { cache.ignore(cacheKey, bytes); } else { cache.put(cacheKey, bytes, newBytes); } } } else if (debugOn) { debug("not weaving '" + name + "'"); } } else if (debugOn) { debug("cannot weave '" + name + "'"); } } finally { delegateForCurrentClass = null; } if (trace.isTraceEnabled()) { trace.exit("weaveClass", newBytes); } return newBytes; } finally { weaverRunning.remove(); } } /** * Return the bytes from a (parallel?) weaving process that generated an inner class, e.g. to support Around closures. * This is done instead of weaving again, as weaving would generate another inner class. * @param name * @return the cached bytes of a previously woven class, or null if not found */ private byte[] wovenWithGeneratedClass(String name) { IUnwovenClassFile woven = generatedClasses.get(name); if (woven == null) { return null; } return woven.getBytes(); } // ATAJ protected boolean accept(String name, byte[] bytes) { return true; } protected boolean shouldDump(String name, boolean before) { return false; } private boolean shouldWeaveName(String name) { if (PACKAGE_INITIAL_CHARS.indexOf(name.charAt(0)) != -1) { if ((weavingSpecialTypes & INITIALIZED) == 0) { weavingSpecialTypes |= INITIALIZED; // initialize it Properties p = weaver.getWorld().getExtraConfiguration(); if (p != null) { boolean b = p.getProperty(World.xsetWEAVE_JAVA_PACKAGES, "false").equalsIgnoreCase("true"); if (b) { weavingSpecialTypes |= WEAVE_JAVA_PACKAGE; } b = p.getProperty(World.xsetWEAVE_JAVAX_PACKAGES, "false").equalsIgnoreCase("true"); if (b) { weavingSpecialTypes |= WEAVE_JAVAX_PACKAGE; } } } if (name.startsWith(ASPECTJ_BASE_PACKAGE)) { return false; } if (name.startsWith("sun.reflect.")) {// JDK reflect return false; } if (name.startsWith("javax.")) { if ((weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) != 0) { return true; } else { if (!haveWarnedOnJavax) { haveWarnedOnJavax = true; warn("javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified"); } return false; } } if (name.startsWith("java.")) { if ((weavingSpecialTypes & WEAVE_JAVA_PACKAGE) != 0) { return true; } else { return false; } } } // boolean should = !(name.startsWith("org.aspectj.") // || (name.startsWith("java.") && (weavingSpecialTypes & WEAVE_JAVA_PACKAGE) == 0) // || (name.startsWith("javax.") && (weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) == 0) // // || name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy... // || name.startsWith("sun.reflect.")); return true; } /** * We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving (and not part of the source compilation) * * @param name * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve * @return true if @Aspect */ private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) { if (delegateForCurrentClass == null) { // if (weaver.getWorld().isASMAround()) return asmCheckAnnotationStyleAspect(bytes); // else ensureDelegateInitialized(name, bytes); } return (delegateForCurrentClass.isAnnotationStyleAspect()); } // private boolean asmCheckAnnotationStyleAspect(byte[] bytes) { // IsAtAspectAnnotationVisitor detector = new IsAtAspectAnnotationVisitor(); // // ClassReader cr = new ClassReader(bytes); // try { // cr.accept(detector, true);//, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); // } catch (Exception spe) { // // if anything goes wrong, e.g., an NPE, then assume it's NOT an @AspectJ aspect... // System.err.println("Unexpected problem parsing bytes to discover @Aspect annotation"); // spe.printStackTrace(); // return false; // } // // return detector.isAspect(); // } protected void ensureDelegateInitialized(String name, byte[] bytes) { if (delegateForCurrentClass == null) { BcelWorld world = (BcelWorld) weaver.getWorld(); delegateForCurrentClass = world.addSourceObjectType(name, bytes, false); } } /** * Weave a set of bytes defining a class. * * @param name the name of the class being woven * @param bytes the bytes that define the class * @return byte[] the woven bytes for the class * @throws IOException */ private byte[] getWovenBytes(String name, byte[] bytes) throws IOException { WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); weaver.weave(wcp); return wcp.getBytes(); } /** * Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect in a usefull form ie with aspectOf * method - see #113587 * * @param name the name of the class being woven * @param bytes the bytes that define the class * @return byte[] the woven bytes for the class * @throws IOException */ private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException { WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes); wcp.setApplyAtAspectJMungersOnly(); weaver.weave(wcp); return wcp.getBytes(); } private void registerAspectLibraries(List aspectPath) { // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")"); for (String libName : aspectPath) { addAspectLibrary(libName); } weaver.prepareForWeave(); } /* * Register an aspect library with this classloader for use during weaving. This class loader will also return (unmodified) any * of the classes in the library in response to a findClass() request. The library is not required to be on the * weavingClasspath given when this classloader was constructed. * * @param aspectLibraryJarFile a jar file representing an aspect library * * @throws IOException */ private void addAspectLibrary(String aspectLibraryName) { File aspectLibrary = new File(aspectLibraryName); if (aspectLibrary.isDirectory() || (FileUtil.isZipFile(aspectLibrary))) { try { info("adding aspect library: '" + aspectLibrary + "'"); weaver.addLibraryJarFile(aspectLibrary); } catch (IOException ex) { error("exception adding aspect library: '" + ex + "'"); } } else { error("bad aspect library: '" + aspectLibrary + "'"); } } private static List makeClasspath(String cp) { List ret = new ArrayList<>(); if (cp != null) { StringTokenizer tok = new StringTokenizer(cp, File.pathSeparator); while (tok.hasMoreTokens()) { ret.add(tok.nextToken()); } } return ret; } protected boolean debug(String message) { return MessageUtil.debug(messageHandler, message); } protected boolean info(String message) { return MessageUtil.info(messageHandler, message); } protected boolean warn(String message) { return MessageUtil.warn(messageHandler, message); } protected boolean warn(String message, Throwable th) { return messageHandler.handleMessage(new Message(message, IMessage.WARNING, th, null)); } protected boolean error(String message) { return MessageUtil.error(messageHandler, message); } protected boolean error(String message, Throwable th) { return messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null)); } public String getContextId() { return "WeavingAdaptor"; } /** * Dump the given bytcode in _dump/... (dev mode) * * @param name * @param b * @param before whether we are dumping before weaving */ protected void dump(String name, byte[] b, boolean before) { String dirName = getDumpDir(); if (before) { dirName = dirName + File.separator + "_before"; } String className = name.replace('.', '/'); final File dir; if (className.indexOf('/') > 0) { dir = new File(dirName + File.separator + className.substring(0, className.lastIndexOf('/'))); } else { dir = new File(dirName); } dir.mkdirs(); String fileName = dirName + File.separator + className + ".class"; try { // System.out.println("WeavingAdaptor.dump() fileName=" + new File(fileName).getAbsolutePath()); FileOutputStream os = new FileOutputStream(fileName); os.write(b); os.close(); } catch (IOException ex) { warn("unable to dump class " + name + " in directory " + dirName, ex); } } /** * @return the directory in which to dump - default is _ajdump but it */ protected String getDumpDir() { return "_ajdump"; } /** * Processes messages arising from weaver operations. Tell weaver to abort on any message more severe than warning. */ protected class WeavingAdaptorMessageHolder extends MessageHandler { private IMessageHandler delegate; private List savedMessages; protected boolean traceMessages = Boolean.getBoolean(TRACE_MESSAGES_PROPERTY); public WeavingAdaptorMessageHolder(PrintWriter writer) { this.delegate = new WeavingAdaptorMessageWriter(writer); super.dontIgnore(IMessage.WEAVEINFO); } private void traceMessage(IMessage message) { if (message instanceof WeaveMessage) { trace.debug(render(message)); } else if (message.isDebug()) { trace.debug(render(message)); } else if (message.isInfo()) { trace.info(render(message)); } else if (message.isWarning()) { trace.warn(render(message), message.getThrown()); } else if (message.isError()) { trace.error(render(message), message.getThrown()); } else if (message.isFailed()) { trace.fatal(render(message), message.getThrown()); } else if (message.isAbort()) { trace.fatal(render(message), message.getThrown()); } else { trace.error(render(message), message.getThrown()); } } protected String render(IMessage message) { return "[" + getContextId() + "] " + message.toString(); } public void flushMessages() { if (savedMessages == null) { savedMessages = new ArrayList<>(); savedMessages.addAll(super.getUnmodifiableListView()); clearMessages(); for (IMessage message : savedMessages) { delegate.handleMessage(message); } } // accumulating = false; // messages.clear(); } public void setDelegate(IMessageHandler messageHandler) { delegate = messageHandler; } /* * IMessageHandler */ @Override public boolean handleMessage(IMessage message) throws AbortException { if (traceMessages) { traceMessage(message); } super.handleMessage(message); if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { throw new AbortException(message); } // if (accumulating) { // boolean result = addMessage(message); // if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { // throw new AbortException(message); // } // return result; // } // else return delegate.handleMessage(message); if (savedMessages != null) { delegate.handleMessage(message); } return true; } @Override public boolean isIgnoring(Kind kind) { return delegate.isIgnoring(kind); } @Override public void dontIgnore(IMessage.Kind kind) { if (null != kind && delegate != null) { delegate.dontIgnore(kind); } } @Override public void ignore(Kind kind) { if (null != kind && delegate != null) { delegate.ignore(kind); } } /* * IMessageHolder */ @Override public List getUnmodifiableListView() { // System.err.println("? WeavingAdaptorMessageHolder.getUnmodifiableListView() savedMessages=" + savedMessages); List allMessages = new ArrayList<>(); allMessages.addAll(savedMessages); allMessages.addAll(super.getUnmodifiableListView()); return allMessages; } } protected class WeavingAdaptorMessageWriter extends MessageWriter { private final Set ignoring = new HashSet<>(); private final IMessage.Kind failKind; public WeavingAdaptorMessageWriter(PrintWriter writer) { super(writer, true); ignore(IMessage.WEAVEINFO); ignore(IMessage.DEBUG); ignore(IMessage.INFO); this.failKind = IMessage.ERROR; } @Override public boolean handleMessage(IMessage message) throws AbortException { // boolean result = super.handleMessage(message); if (abortOnError && 0 <= message.getKind().compareTo(failKind)) { throw new AbortException(message); } return true; } @Override public boolean isIgnoring(Kind kind) { return ((null != kind) && (ignoring.contains(kind))); } /** * Set a message kind to be ignored from now on */ @Override public void ignore(IMessage.Kind kind) { if ((null != kind) && (!ignoring.contains(kind))) { ignoring.add(kind); } } /** * Remove a message kind from the list of those ignored from now on. */ @Override public void dontIgnore(IMessage.Kind kind) { if (null != kind) { ignoring.remove(kind); } } @Override protected String render(IMessage message) { return "[" + getContextId() + "] " + super.render(message); } } private class WeavingClassFileProvider implements IClassFileProvider { private final UnwovenClassFile unwovenClass; private final List unwovenClasses = new ArrayList<>(); private IUnwovenClassFile wovenClass; private boolean isApplyAtAspectJMungersOnly = false; public WeavingClassFileProvider(String name, byte[] bytes) { ensureDelegateInitialized(name, bytes); this.unwovenClass = new UnwovenClassFile(name, delegateForCurrentClass.getResolvedTypeX().getName(), bytes); this.unwovenClasses.add(unwovenClass); if (shouldDump(name.replace('/', '.'), true)) { dump(name, bytes, true); } } public void setApplyAtAspectJMungersOnly() { isApplyAtAspectJMungersOnly = true; } public boolean isApplyAtAspectJMungersOnly() { return isApplyAtAspectJMungersOnly; } public byte[] getBytes() { if (wovenClass != null) { return wovenClass.getBytes(); } else { return unwovenClass.getBytes(); } } public Iterator getClassFileIterator() { return unwovenClasses.iterator(); } public IWeaveRequestor getRequestor() { return new WeaveRequestor(); } private class WeaveRequestor implements IWeaveRequestor { @Override public void acceptResult(IUnwovenClassFile result) { if (wovenClass == null) { wovenClass = result; String name = result.getClassName(); if (shouldDump(name.replace('/', '.'), false)) { dump(name, result.getBytes(), false); } } else { // Classes generated by weaver e.g. around closure advice String className = result.getClassName(); byte[] resultBytes = result.getBytes(); if (SimpleCacheFactory.isEnabled()) { SimpleCache lacache=SimpleCacheFactory.createSimpleCache(); lacache.put(result.getClassName(), wovenClass.getBytes(), result.getBytes()); lacache.addGeneratedClassesNames(wovenClass.getClassName(), wovenClass.getBytes(), result.getClassName()); } generatedClasses.put(className, result); generatedClasses.put(wovenClass.getClassName(), wovenClass); generatedClassHandler.acceptClass(className, null, resultBytes); } } public void processingReweavableState() {} public void addingTypeMungers() {} public void weavingAspects() {} public void weavingClasses() {} public void weaveCompleted() { // ResolvedType.resetPrimitives(); if (delegateForCurrentClass != null) { delegateForCurrentClass.weavingCompleted(); } // ResolvedType.resetPrimitives(); // bcelWorld.discardType(typeBeingProcessed.getResolvedTypeX()); // work in progress } } } public void setActiveProtectionDomain(ProtectionDomain protectionDomain) { activeProtectionDomain = protectionDomain; } }