12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262 |
- /*******************************************************************************
- * 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 v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *******************************************************************************/
- package org.aspectj.weaver.loadtime;
-
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.invoke.MethodHandle;
- import java.lang.invoke.MethodHandles;
- import java.lang.reflect.AccessibleObject;
- 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.*;
- import java.util.Map.Entry;
-
- 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 org.objectweb.asm.*;
- import org.objectweb.asm.commons.ClassRemapper;
- import org.objectweb.asm.commons.Remapper;
- 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<>();
- private boolean dumpBefore = false;
- private boolean dumpDirPerClassloader = false;
-
- private boolean hasExcludes = false;
- private List<TypePattern> excludeTypePattern = new ArrayList<>(); // anything
- private List<String> excludeStartsWith = new ArrayList<>(); // com.foo..*
- private List<String> excludeStarDotDotStar = new ArrayList<>(); // *..*CGLIB*
- private List<String> excludeExactName = new ArrayList<>(); // com.foo.Bar
- private List<String> excludeEndsWith = new ArrayList<>(); // com.foo.Bar
- private List<String[]> excludeSpecial = new ArrayList<>();
-
- private boolean hasIncludes = false;
- private List<TypePattern> includeTypePattern = new ArrayList<>();
- private List<String> includeStartsWith = new ArrayList<>();
- private List<String> includeExactName = new ArrayList<>();
- private boolean includeStar = false;
-
- private List<TypePattern> aspectExcludeTypePattern = new ArrayList<>();
- private List<String> aspectExcludeStartsWith = new ArrayList<>();
- private List<TypePattern> aspectIncludeTypePattern = new ArrayList<>();
- private List<String> aspectIncludeStartsWith = new ArrayList<>();
-
- private StringBuffer namespace;
- private IWeavingContext weavingContext;
-
- private List<ConcreteAspectCodeGen> concreteAspects = new ArrayList<>();
-
- 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<>();
- for (Definition def : definitions) {
- 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<>();
- 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<>();
- 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) {
- StringBuilder allOptions = new StringBuilder();
- 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(true);
- 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 == null ? 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 (String value : definition.getIncludePatterns()) {
- hasIncludes = true;
- String include = value;
- 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 (String s : definition.getExcludePatterns()) {
- hasExcludes = true;
- String exclude = s;
- 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.contains("*")) {
- 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 (String dump : definition.getDumpPatterns()) {
- 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 (String s : includeStartsWith) {
- didSomeIncludeMatching = true;
- fastAccept = fastClassName.startsWith(s);
- 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 (String s : includeStartsWith) {
- didSomeIncludeMatching = true;
- boolean fastaccept = fastClassName.startsWith(s);
- 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 (String value : aspectExcludeStartsWith) {
- if (fastClassName.startsWith(value)) {
- return false;
- }
- }
- // INCLUDE: if one match then accept
- for (String s : aspectIncludeStartsWith) {
- if (fastClassName.startsWith(s)) {
- 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 (TypePattern typePattern : dumpTypePattern) {
- if (typePattern.matchesStatically(classInfo)) {
- // dump match
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected String getDumpDir() {
- if (dumpDirPerClassloader) {
- StringBuilder dir = new StringBuilder();
- 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<>();
- }
-
- /**
- * 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 pick up inner classes
- generatedClasses.entrySet().removeIf(entry -> {
- String generatedClassName = entry.getKey();
- return generatedClassName.equals(dottedClassName) || generatedClassName.startsWith(dottedClassNameDollar);
- });
- } catch (Throwable t) {
- new RuntimeException("Unexpected problem tidying up generated classes for " + className, t).printStackTrace();
- }
- }
-
- private static final Object lock = new Object();
-
- /**
- * Instance of either {@link sun.misc.Unsafe} or {@link jdk.internal.misc.Unsafe}. Luckily, both types have
- * {@code defineClass} methods with identical signatures. I.e., method handle {@link #defineClassMethodHandle} can be
- * invoked with the same set of parameters for both types.
- */
- private static Object unsafeInstance = null;
-
- /**
- * Method handle for defining new classes in arbitrary class loaders. For invocation, use in connection with
- * {@link #unsafeInstance}.
- */
- private static MethodHandle defineClassMethodHandle;
-
- static {
- try {
- createDefineClassMethodHandle();
- }
- catch (Exception initializationError) {
- new RuntimeException(
- "The aspect weaver cannot determine any valid method to define auxiliary classes in arbitrary class loaders. " +
- "Aspect weaving will *not* work, and you will see subsequent errors. Please search for corresponding " +
- "issues at https://github.com/eclipse-aspectj/aspectj/issues. If there are none, please create a new one.",
- initializationError
- ).printStackTrace();
- }
- }
-
- /**
- * Scaffolding for defining classes in arbitrary class loaders
- * <p>
- * Inspired by and shamelessly adapted from <a href="https://bit.ly/3w10oH5">Byte Buddy's {@code ClassInjector}</a>.
- * Special thanks to Byte Buddy (BB) author Rafael Winterhalter, who briefly mentioned this approach in a
- * <a href="https://bit.ly/3SjFOZY">GitHub comment</a> related to JDK issue
- * <a href="https://bugs.openjdk.org/browse/JDK-8200559">JDK-8200559</a>.
- * <p>
- * <b>Background:</b> Instead of BB, we use ASM and reflection as follows:
- * <ul>
- * <li>
- * Create a mirror class for {@link AccessibleObject} with a different package name in a separate, throw-away
- * class loader.
- * </li>
- * <li>
- * Use the mirror class to calculate the {@link Unsafe#objectFieldOffset(Field)} for boolean field
- * {@link AccessibleObject#override}, which is expected to be identical to the offset of the same field in the
- * original class.
- * </li>
- * <li>
- * After we have the offset, we can use it to override the field value in the original class, deactivating access
- * checks for {@link jdk.internal.misc.Unsafe#defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain)},
- * the method we need to execute using a method handle.
- * </li>
- * </ul>
- * All these serve the sole purpose enable LTW without {@code --add-opens java.base/java.lang=ALL-UNNAMED} on the
- * JVM command line on JDK 16+, which was necessary for AspectJ 1.9.7 (Java 16) to 1.9.21 (Java 21).
- *
- * @throws Exception if anything goes wrong, trying to determine a usable {@code defineClass} method handle from any
- * of the inspected classes
- */
- private static synchronized void createDefineClassMethodHandle() throws Exception {
- Unsafe publicUnsafeInstance = null;
- try {
- Field publicUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
- publicUnsafeField.setAccessible(true);
- publicUnsafeInstance = (Unsafe) publicUnsafeField.get(null);
- synchronized (lock) {
- unsafeInstance = publicUnsafeInstance;
- defineClassMethodHandle = createMethodHandle(
- "sun.misc.Unsafe", "defineClass",
- String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class
- );
- }
- }
- catch (Exception publicUnsafeException) {
- if (publicUnsafeInstance == null)
- throw publicUnsafeException;
- long overrideOffset = getAccessibleObjectOverrideOffset(publicUnsafeInstance);
- Class<?> internalUnsafeType = Class.forName("jdk.internal.misc.Unsafe");
- Field internalUnsafeField = internalUnsafeType.getDeclaredField("theUnsafe");
- publicUnsafeInstance.putBoolean(internalUnsafeField, overrideOffset, true);
- Method internalUnsafeDefineClassMethod = internalUnsafeType.getMethod(
- "defineClass",
- String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class
- );
- publicUnsafeInstance.putBoolean(internalUnsafeDefineClassMethod, overrideOffset, true);
- synchronized (lock) {
- unsafeInstance = internalUnsafeField.get(null);
- defineClassMethodHandle = createMethodHandle(internalUnsafeDefineClassMethod);
- }
- }
- }
-
- private static long getAccessibleObjectOverrideOffset(Unsafe unsafe)
- throws IOException, ClassNotFoundException, NoSuchFieldException
- {
- Objects.requireNonNull(unsafe);
- Field overrideField;
- try {
- overrideField = AccessibleObject.class.getDeclaredField("override");
- }
- catch (NoSuchFieldException ignored) {
- // On JDK 12+, field AccessibleObject.override is protected from reflection. The work-around is to create a
- // mirror class with the same field layout by transforming the original class, so we can calculate the field
- // offset of 'override' and set a value in the original class using the now known offset.
- Class<?> mirrorClass = getMirrorClass(
- "java.lang.reflect.AccessibleObject", "org.aspectj.mirror.AccessibleObject", true
- );
- overrideField = mirrorClass.getDeclaredField("override");
- }
- return unsafe.objectFieldOffset(overrideField);
- }
-
- public static MethodHandle createMethodHandle(String className, String methodName, Class<?>... argumentTypes)
- throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException
- {
- Class<?> clazz = Class.forName(className);
- Method method = clazz.getDeclaredMethod(methodName, argumentTypes);
- return createMethodHandle(method, false);
- }
-
- public static MethodHandle createMethodHandle(Method method) throws IllegalAccessException {
- return createMethodHandle(method, false);
- }
-
- public static MethodHandle createMethodHandle(Method method, boolean setAccessible)
- throws IllegalAccessException
- {
- // Use Method::setAccessible to access private methods. Caveat: This does not work for classes in packages not
- // exported to the calling module (for LTW usually the unnamed module).
- if (setAccessible)
- method.setAccessible(true);
- return MethodHandles.lookup().unreflect(method);
- }
-
- @SuppressWarnings("SameParameterValue")
- private static Class<?> getMirrorClass(String originalClass, String mirrorClass, boolean removeMethods)
- throws IOException, ClassNotFoundException
- {
- Objects.requireNonNull(originalClass);
- Objects.requireNonNull(mirrorClass);
- if (mirrorClass.equals(originalClass))
- throw new IllegalArgumentException("Mirror class name must be different from original " + originalClass);
- byte[] mirrorClassBytes = getMirrorClassBytes(originalClass, mirrorClass, removeMethods);
- ClassLoader mirrorClassLoader = new SingleClassLoader(mirrorClass, mirrorClassBytes);
- return mirrorClassLoader.loadClass(mirrorClass);
- }
-
- private static byte[] getMirrorClassBytes(String originalClass, String mirrorClass, boolean removeMethods)
- throws IOException, ClassNotFoundException
- {
- Class<?> aClass = Class.forName(originalClass);
- try (InputStream input = aClass.getResourceAsStream(aClass.getSimpleName() + ".class")) {
- Objects.requireNonNull(input);
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassRemapper classRemapper = new ClassRemapper(classWriter, new ClassNameRemapper(originalClass, mirrorClass));
- ClassVisitor classVisitor = removeMethods ? new MethodAndConstructorRemover(classRemapper) : classRemapper;
- new ClassReader(input).accept(classVisitor, 0);
- return classWriter.toByteArray();
- }
- }
-
- private static class ClassNameRemapper extends Remapper {
- private final String originalClass;
- private final String mirrorClass;
-
- public ClassNameRemapper(String originalClass, String mirrorClass) {
- this.originalClass = originalClass.replace('.', '/');
- this.mirrorClass = mirrorClass.replace('.', '/');
- }
-
- @Override
- public String map(String internalName) {
- return internalName.equals(originalClass) ? mirrorClass : internalName;
- }
- }
-
- /**
- * ASM class visitor removing all methods and constructors from the given class, leaving only the original fields
- */
- private static class MethodAndConstructorRemover extends ClassVisitor {
- public MethodAndConstructorRemover(ClassRemapper classRemapper) {
- super(Opcodes.ASM9, classRemapper);
- }
-
- @Override
- public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
- // Do not visit any methods or constructors, effectively removing them
- return null;
- }
- }
-
- /**
- * Throw-away child classloader with the sole purpose to define a single {@link Class} from the given bytecode
- */
- private static class SingleClassLoader extends ClassLoader {
- private final String mirrorClass;
- private final byte[] mirrorClassBytes;
-
- private SingleClassLoader(String mirrorClass, byte[] mirrorClassBytes) {
- super(SingleClassLoader.class.getClassLoader());
- this.mirrorClass = mirrorClass;
- this.mirrorClassBytes = mirrorClassBytes;
- }
-
- @Override
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- return name.equals(mirrorClass)
- ? super.defineClass(null, mirrorClassBytes, 0, mirrorClassBytes.length)
- : super.loadClass(name);
- }
- }
-
- protected void defineClass(ClassLoader loader, String name, byte[] bytes) {
- defineClass(loader, name, bytes, null);
- }
-
- protected void defineClass(ClassLoader loader, String name, byte[] bytes, ProtectionDomain protectionDomain) {
- if (trace.isTraceEnabled())
- trace.enter("defineClass", this, new Object[] { loader, name, bytes });
- debug("generating class '" + name + "'");
- Class<?> definedClass = null;
- try {
- if (defineClassMethodHandle == null)
- throw new RuntimeException("no valid method to define auxiliary classes -> weaver not working");
- definedClass = (Class<?>) defineClassMethodHandle
- .bindTo(unsafeInstance)
- .invokeWithArguments(name, bytes, 0, bytes.length, loader, protectionDomain);
- }
- catch (Throwable t) {
- t.printStackTrace(System.err);
- warn("define generated class failed", t);
- }
- finally {
- if (trace.isTraceEnabled())
- trace.exit("defineClass", definedClass);
- }
- }
-
- }
|