You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClassLoaderWeavingAdaptor.java 42KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. /*******************************************************************************
  2. * Copyright (c) 2005, 2017 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *******************************************************************************/
  9. package org.aspectj.weaver.loadtime;
  10. import java.io.File;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.lang.invoke.MethodHandle;
  14. import java.lang.invoke.MethodHandles;
  15. import java.lang.reflect.AccessibleObject;
  16. import java.lang.reflect.Field;
  17. import java.lang.reflect.Method;
  18. import java.net.MalformedURLException;
  19. import java.net.URL;
  20. import java.security.ProtectionDomain;
  21. import java.util.*;
  22. import java.util.Map.Entry;
  23. import org.aspectj.bridge.AbortException;
  24. import org.aspectj.bridge.Constants;
  25. import org.aspectj.bridge.MessageUtil;
  26. import org.aspectj.util.LangUtil;
  27. import org.aspectj.weaver.IUnwovenClassFile;
  28. import org.aspectj.weaver.Lint;
  29. import org.aspectj.weaver.Lint.Kind;
  30. import org.aspectj.weaver.ResolvedType;
  31. import org.aspectj.weaver.UnresolvedType;
  32. import org.aspectj.weaver.World;
  33. import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
  34. import org.aspectj.weaver.bcel.BcelWeaver;
  35. import org.aspectj.weaver.bcel.BcelWorld;
  36. import org.aspectj.weaver.bcel.Utility;
  37. import org.aspectj.weaver.loadtime.definition.Definition;
  38. import org.aspectj.weaver.loadtime.definition.DocumentParser;
  39. import org.aspectj.weaver.ltw.LTWWorld;
  40. import org.aspectj.weaver.patterns.PatternParser;
  41. import org.aspectj.weaver.patterns.TypePattern;
  42. import org.aspectj.weaver.tools.GeneratedClassHandler;
  43. import org.aspectj.weaver.tools.Trace;
  44. import org.aspectj.weaver.tools.TraceFactory;
  45. import org.aspectj.weaver.tools.WeavingAdaptor;
  46. import org.aspectj.weaver.tools.cache.WeavedClassCache;
  47. import org.objectweb.asm.*;
  48. import org.objectweb.asm.commons.ClassRemapper;
  49. import org.objectweb.asm.commons.Remapper;
  50. import sun.misc.Unsafe;
  51. /**
  52. * @author Alexandre Vasseur
  53. * @author Andy Clement
  54. * @author Abraham Nevado
  55. * @author David Knibb
  56. * @author John Kew
  57. */
  58. public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
  59. private final static String AOP_XML = Constants.AOP_USER_XML + ";" + Constants.AOP_AJC_XML + ";" + Constants.AOP_OSGI_XML;
  60. private boolean initialized;
  61. private List<TypePattern> dumpTypePattern = new ArrayList<>();
  62. private boolean dumpBefore = false;
  63. private boolean dumpDirPerClassloader = false;
  64. private boolean hasExcludes = false;
  65. private List<TypePattern> excludeTypePattern = new ArrayList<>(); // anything
  66. private List<String> excludeStartsWith = new ArrayList<>(); // com.foo..*
  67. private List<String> excludeStarDotDotStar = new ArrayList<>(); // *..*CGLIB*
  68. private List<String> excludeExactName = new ArrayList<>(); // com.foo.Bar
  69. private List<String> excludeEndsWith = new ArrayList<>(); // com.foo.Bar
  70. private List<String[]> excludeSpecial = new ArrayList<>();
  71. private boolean hasIncludes = false;
  72. private List<TypePattern> includeTypePattern = new ArrayList<>();
  73. private List<String> includeStartsWith = new ArrayList<>();
  74. private List<String> includeExactName = new ArrayList<>();
  75. private boolean includeStar = false;
  76. private List<TypePattern> aspectExcludeTypePattern = new ArrayList<>();
  77. private List<String> aspectExcludeStartsWith = new ArrayList<>();
  78. private List<TypePattern> aspectIncludeTypePattern = new ArrayList<>();
  79. private List<String> aspectIncludeStartsWith = new ArrayList<>();
  80. private StringBuffer namespace;
  81. private IWeavingContext weavingContext;
  82. private List<ConcreteAspectCodeGen> concreteAspects = new ArrayList<>();
  83. private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassLoaderWeavingAdaptor.class);
  84. public ClassLoaderWeavingAdaptor() {
  85. super();
  86. if (trace.isTraceEnabled()) {
  87. trace.enter("<init>", this);
  88. }
  89. if (trace.isTraceEnabled()) {
  90. trace.exit("<init>");
  91. }
  92. }
  93. /**
  94. * We don't need a reference to the class loader and using it during construction can cause problems with recursion. It also
  95. * makes sense to supply the weaving context during initialization to.
  96. *
  97. * @deprecated
  98. */
  99. @Deprecated
  100. public ClassLoaderWeavingAdaptor(final ClassLoader deprecatedLoader, final IWeavingContext deprecatedContext) {
  101. super();
  102. if (trace.isTraceEnabled()) {
  103. trace.enter("<init>", this, new Object[] { deprecatedLoader, deprecatedContext });
  104. }
  105. if (trace.isTraceEnabled()) {
  106. trace.exit("<init>");
  107. }
  108. }
  109. class SimpleGeneratedClassHandler implements GeneratedClassHandler {
  110. private BcelWeakClassLoaderReference loaderRef;
  111. SimpleGeneratedClassHandler(ClassLoader loader) {
  112. loaderRef = new BcelWeakClassLoaderReference(loader);
  113. }
  114. /**
  115. * Callback when we need to define a Closure in the JVM
  116. *
  117. */
  118. @Override
  119. public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) {
  120. try {
  121. if (shouldDump(name.replace('/', '.'), false)) {
  122. dump(name, wovenBytes, false);
  123. }
  124. } catch (Throwable throwable) {
  125. throwable.printStackTrace();
  126. }
  127. if (activeProtectionDomain != null) {
  128. defineClass(loaderRef.getClassLoader(), name, wovenBytes, activeProtectionDomain);
  129. } else {
  130. defineClass(loaderRef.getClassLoader(), name, wovenBytes); // could be done lazily using the hook
  131. }
  132. }
  133. }
  134. public void initialize(final ClassLoader classLoader, IWeavingContext context) {
  135. if (initialized) {
  136. return;
  137. }
  138. boolean success = true;
  139. this.weavingContext = context;
  140. if (weavingContext == null) {
  141. weavingContext = new DefaultWeavingContext(classLoader);
  142. }
  143. createMessageHandler();
  144. this.generatedClassHandler = new SimpleGeneratedClassHandler(classLoader);
  145. List<Definition> definitions = weavingContext.getDefinitions(classLoader, this);
  146. if (definitions.isEmpty()) {
  147. disable(); // TODO maw Needed to ensure messages are flushed
  148. if (trace.isTraceEnabled()) {
  149. trace.exit("initialize", definitions);
  150. }
  151. return;
  152. }
  153. // TODO when the world works in terms of the context, we can remove the loader
  154. bcelWorld = new LTWWorld(classLoader, weavingContext, getMessageHandler(), null);
  155. weaver = new BcelWeaver(bcelWorld);
  156. // register the definitions
  157. success = registerDefinitions(weaver, classLoader, definitions);
  158. if (success) {
  159. // after adding aspects
  160. weaver.prepareForWeave();
  161. enable(); // TODO maw Needed to ensure messages are flushed
  162. success = weaveAndDefineConceteAspects();
  163. }
  164. if (success) {
  165. enable();
  166. } else {
  167. disable();
  168. bcelWorld = null;
  169. weaver = null;
  170. }
  171. if (WeavedClassCache.isEnabled()) {
  172. initializeCache(classLoader, getAspectClassNames(definitions), generatedClassHandler, getMessageHandler());
  173. }
  174. initialized = true;
  175. if (trace.isTraceEnabled()) {
  176. trace.exit("initialize", isEnabled());
  177. }
  178. }
  179. /**
  180. * Get the list of all aspects from the defintion list
  181. * @param definitions
  182. * @return
  183. */
  184. List<String> getAspectClassNames(List<Definition> definitions) {
  185. List<String> aspects = new LinkedList<>();
  186. for (Definition def : definitions) {
  187. List<String> defAspects = def.getAspectClassNames();
  188. if (defAspects != null) {
  189. aspects.addAll(defAspects);
  190. }
  191. }
  192. return aspects;
  193. }
  194. /**
  195. * Load and cache the aop.xml/properties according to the classloader visibility rules
  196. *
  197. * @param loader
  198. */
  199. List<Definition> parseDefinitions(final ClassLoader loader) {
  200. if (trace.isTraceEnabled()) {
  201. trace.enter("parseDefinitions", this);
  202. }
  203. List<Definition> definitions = new ArrayList<>();
  204. try {
  205. info("register classloader " + getClassLoaderName(loader));
  206. // TODO av underoptimized: we will parse each XML once per CL that see it
  207. // TODO av dev mode needed ? TBD -Daj5.def=...
  208. if (loader.equals(ClassLoader.getSystemClassLoader())) {
  209. String file = System.getProperty("aj5.def", null);
  210. if (file != null) {
  211. info("using (-Daj5.def) " + file);
  212. definitions.add(DocumentParser.parse((new File(file)).toURI().toURL()));
  213. }
  214. }
  215. String resourcePath = System.getProperty("org.aspectj.weaver.loadtime.configuration", AOP_XML);
  216. if (trace.isTraceEnabled()) {
  217. trace.event("parseDefinitions", this, resourcePath);
  218. }
  219. StringTokenizer st = new StringTokenizer(resourcePath, ";");
  220. while (st.hasMoreTokens()) {
  221. String nextDefinition = st.nextToken();
  222. if (nextDefinition.startsWith("file:")) {
  223. try {
  224. String fpath = new URL(nextDefinition).getFile();
  225. File configFile = new File(fpath);
  226. if (!configFile.exists()) {
  227. warn("configuration does not exist: " + nextDefinition);
  228. } else {
  229. definitions.add(DocumentParser.parse(configFile.toURI().toURL()));
  230. }
  231. } catch (MalformedURLException mue) {
  232. error("malformed definition url: " + nextDefinition);
  233. }
  234. } else {
  235. Enumeration<URL> xmls = weavingContext.getResources(nextDefinition);
  236. // System.out.println("? registerDefinitions: found-aop.xml=" + xmls.hasMoreElements() + ", loader=" + loader);
  237. Set<URL> seenBefore = new HashSet<>();
  238. while (xmls.hasMoreElements()) {
  239. URL xml = xmls.nextElement();
  240. if (trace.isTraceEnabled()) {
  241. trace.event("parseDefinitions", this, xml);
  242. }
  243. if (!seenBefore.contains(xml)) {
  244. info("using configuration " + weavingContext.getFile(xml));
  245. definitions.add(DocumentParser.parse(xml));
  246. seenBefore.add(xml);
  247. } else {
  248. debug("ignoring duplicate definition: " + xml);
  249. }
  250. }
  251. }
  252. }
  253. if (definitions.isEmpty()) {
  254. info("no configuration found. Disabling weaver for class loader " + getClassLoaderName(loader));
  255. }
  256. } catch (Exception e) {
  257. definitions.clear();
  258. warn("parse definitions failed", e);
  259. }
  260. if (trace.isTraceEnabled()) {
  261. trace.exit("parseDefinitions", definitions);
  262. }
  263. return definitions;
  264. }
  265. private boolean registerDefinitions(final BcelWeaver weaver, final ClassLoader loader, List<Definition> definitions) {
  266. if (trace.isTraceEnabled()) {
  267. trace.enter("registerDefinitions", this, definitions);
  268. }
  269. boolean success = true;
  270. try {
  271. registerOptions(weaver, loader, definitions);
  272. registerAspectExclude(weaver, loader, definitions);
  273. registerAspectInclude(weaver, loader, definitions);
  274. success = registerAspects(weaver, loader, definitions);
  275. registerIncludeExclude(weaver, loader, definitions);
  276. registerDump(weaver, loader, definitions);
  277. } catch (Exception ex) {
  278. trace.error("register definition failed", ex);
  279. success = false;
  280. warn("register definition failed", (ex instanceof AbortException) ? null : ex);
  281. }
  282. if (trace.isTraceEnabled()) {
  283. trace.exit("registerDefinitions", success);
  284. }
  285. return success;
  286. }
  287. private String getClassLoaderName(ClassLoader loader) {
  288. return weavingContext.getClassLoaderName();
  289. }
  290. /**
  291. * Configure the weaver according to the option directives TODO av - don't know if it is that good to reuse, since we only allow
  292. * a small subset of options in LTW
  293. *
  294. * @param weaver
  295. * @param loader
  296. * @param definitions
  297. */
  298. private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  299. StringBuilder allOptions = new StringBuilder();
  300. for (Definition definition : definitions) {
  301. allOptions.append(definition.getWeaverOptions()).append(' ');
  302. }
  303. Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader, getMessageHandler());
  304. // configure the weaver and world
  305. // AV - code duplicates AspectJBuilder.initWorldAndWeaver()
  306. World world = weaver.getWorld();
  307. setMessageHandler(weaverOption.messageHandler);
  308. world.setXlazyTjp(weaverOption.lazyTjp);
  309. world.setXHasMemberSupportEnabled(weaverOption.hasMember);
  310. world.setTiming(weaverOption.timers, true);
  311. world.setOptionalJoinpoints(weaverOption.optionalJoinpoints);
  312. world.setPinpointMode(weaverOption.pinpoint);
  313. weaver.setReweavableMode(weaverOption.notReWeavable);
  314. if (weaverOption.loadersToSkip != null && weaverOption.loadersToSkip.length() > 0) {
  315. Aj.loadersToSkip = LangUtil.anySplit(weaverOption.loadersToSkip, ",");
  316. }
  317. if (Aj.loadersToSkip != null) {
  318. MessageUtil.info(world.getMessageHandler(),"no longer creating weavers for these classloaders: "+Aj.loadersToSkip);
  319. }
  320. world.performExtraConfiguration(weaverOption.xSet);
  321. world.setXnoInline(weaverOption.noInline);
  322. // AMC - autodetect as per line below, needed for AtAjLTWTests.testLTWUnweavable
  323. world.setBehaveInJava5Way(true);
  324. world.setAddSerialVerUID(weaverOption.addSerialVersionUID);
  325. /* First load defaults */
  326. bcelWorld.getLint().loadDefaultProperties();
  327. /* Second overlay LTW defaults */
  328. bcelWorld.getLint().adviceDidNotMatch.setKind(null);
  329. /* Third load user file using -Xlintfile so that -Xlint wins */
  330. if (weaverOption.lintFile != null) {
  331. InputStream resource = null;
  332. try {
  333. resource = loader.getResourceAsStream(weaverOption.lintFile);
  334. Exception failure = null;
  335. if (resource != null) {
  336. try {
  337. Properties properties = new Properties();
  338. properties.load(resource);
  339. world.getLint().setFromProperties(properties);
  340. } catch (IOException e) {
  341. failure = e;
  342. }
  343. }
  344. if (failure != null || resource == null) {
  345. warn("Cannot access resource for -Xlintfile:" + weaverOption.lintFile, failure);
  346. // world.getMessageHandler().handleMessage(new Message(
  347. // "Cannot access resource for -Xlintfile:"+weaverOption.lintFile,
  348. // IMessage.WARNING,
  349. // failure,
  350. // null));
  351. }
  352. } finally {
  353. try {
  354. resource.close();
  355. } catch (Throwable t) {
  356. }
  357. }
  358. }
  359. /* Fourth override with -Xlint */
  360. if (weaverOption.lint != null) {
  361. if (weaverOption.lint.equals("default")) {// FIXME should be AjBuildConfig.AJLINT_DEFAULT but yetanother deps..
  362. bcelWorld.getLint().loadDefaultProperties();
  363. } else {
  364. bcelWorld.getLint().setAll(weaverOption.lint);
  365. if (weaverOption.lint.equals("ignore")) {
  366. bcelWorld.setAllLintIgnored();
  367. }
  368. }
  369. }
  370. // TODO proceedOnError option
  371. }
  372. private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  373. String fastMatchInfo = null;
  374. for (Definition definition : definitions) {
  375. for (String exclude : definition.getAspectExcludePatterns()) {
  376. TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
  377. aspectExcludeTypePattern.add(excludePattern);
  378. fastMatchInfo = looksLikeStartsWith(exclude);
  379. if (fastMatchInfo != null) {
  380. aspectExcludeStartsWith.add(fastMatchInfo);
  381. }
  382. }
  383. }
  384. }
  385. private void registerAspectInclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  386. String fastMatchInfo = null;
  387. for (Definition definition : definitions) {
  388. for (String include : definition.getAspectIncludePatterns()) {
  389. TypePattern includePattern = new PatternParser(include).parseTypePattern();
  390. aspectIncludeTypePattern.add(includePattern);
  391. fastMatchInfo = looksLikeStartsWith(include);
  392. if (fastMatchInfo != null) {
  393. aspectIncludeStartsWith.add(fastMatchInfo);
  394. }
  395. }
  396. }
  397. }
  398. protected void lint(String name, String[] infos) {
  399. Lint lint = bcelWorld.getLint();
  400. Kind kind = lint.getLintKind(name);
  401. kind.signal(infos, null, null);
  402. }
  403. @Override
  404. public String getContextId() {
  405. return weavingContext.getId();
  406. }
  407. /**
  408. * Register the aspect, following include / exclude rules
  409. *
  410. * @param weaver
  411. * @param loader
  412. * @param definitions
  413. */
  414. private boolean registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  415. if (trace.isTraceEnabled()) {
  416. trace.enter("registerAspects", this, new Object[] { weaver, loader, definitions });
  417. }
  418. boolean success = true;
  419. // TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ??
  420. // if not, review the getResource so that we track which resource is defined by which CL
  421. // iterate aspectClassNames
  422. // exclude if in any of the exclude list
  423. for (Definition definition : definitions) {
  424. for (String aspectClassName : definition.getAspectClassNames()) {
  425. if (acceptAspect(aspectClassName)) {
  426. info("register aspect " + aspectClassName);
  427. // System.err.println("? ClassLoaderWeavingAdaptor.registerAspects() aspectName=" + aspectClassName +
  428. // ", loader=" + loader + ", bundle=" + weavingContext.getClassLoaderName());
  429. String requiredType = definition.getAspectRequires(aspectClassName);
  430. if (requiredType != null) {
  431. // This aspect expresses that it requires a type to be around, otherwise it should 'switch off'
  432. ((BcelWorld) weaver.getWorld()).addAspectRequires(aspectClassName, requiredType);
  433. }
  434. String definedScope = definition.getScopeForAspect(aspectClassName);
  435. if (definedScope != null) {
  436. ((BcelWorld) weaver.getWorld()).addScopedAspect(aspectClassName, definedScope);
  437. }
  438. // ResolvedType aspect =
  439. weaver.addLibraryAspect(aspectClassName);
  440. // generate key for SC
  441. if (namespace == null) {
  442. namespace = new StringBuffer(aspectClassName);
  443. } else {
  444. namespace = namespace.append(";").append(aspectClassName);
  445. }
  446. } else {
  447. // warn("aspect excluded: " + aspectClassName);
  448. lint("aspectExcludedByConfiguration", new String[] { aspectClassName, getClassLoaderName(loader) });
  449. }
  450. }
  451. }
  452. // iterate concreteAspects
  453. // exclude if in any of the exclude list - note that the user defined name matters for that to happen
  454. for (Definition definition : definitions) {
  455. for (Definition.ConcreteAspect concreteAspect : definition.getConcreteAspects()) {
  456. if (acceptAspect(concreteAspect.name)) {
  457. info("define aspect " + concreteAspect.name);
  458. ConcreteAspectCodeGen gen = new ConcreteAspectCodeGen(concreteAspect, weaver.getWorld());
  459. if (!gen.validate()) {
  460. error("Concrete-aspect '" + concreteAspect.name + "' could not be registered");
  461. success = false;
  462. break;
  463. }
  464. ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes()),
  465. true);
  466. concreteAspects.add(gen);
  467. weaver.addLibraryAspect(concreteAspect.name);
  468. // generate key for SC
  469. if (namespace == null) {
  470. namespace = new StringBuffer(concreteAspect.name);
  471. } else {
  472. namespace = namespace.append(";" + concreteAspect.name);
  473. }
  474. }
  475. }
  476. }
  477. /* We couldn't register one or more aspects so disable the adaptor */
  478. if (!success) {
  479. warn("failure(s) registering aspects. Disabling weaver for class loader " + getClassLoaderName(loader));
  480. }
  481. /* We didn't register any aspects so disable the adaptor */
  482. else if (namespace == null) {
  483. success = false;
  484. info("no aspects registered. Disabling weaver for class loader " + getClassLoaderName(loader));
  485. }
  486. if (trace.isTraceEnabled()) {
  487. trace.exit("registerAspects", success);
  488. }
  489. return success;
  490. }
  491. private boolean weaveAndDefineConceteAspects() {
  492. if (trace.isTraceEnabled()) {
  493. trace.enter("weaveAndDefineConceteAspects", this, concreteAspects);
  494. }
  495. boolean success = true;
  496. for (ConcreteAspectCodeGen gen : concreteAspects) {
  497. String name = gen.getClassName();
  498. byte[] bytes = gen.getBytes();
  499. try {
  500. byte[] newBytes = weaveClass(name, bytes, true);
  501. this.generatedClassHandler.acceptClass(name, bytes, newBytes);
  502. } catch (IOException ex) {
  503. trace.error("weaveAndDefineConceteAspects", ex);
  504. error("exception weaving aspect '" + name + "'", ex);
  505. }
  506. }
  507. if (trace.isTraceEnabled()) {
  508. trace.exit("weaveAndDefineConceteAspects", success);
  509. }
  510. return success;
  511. }
  512. /**
  513. * Register the include / exclude filters. We duplicate simple patterns in startWith filters that will allow faster matching
  514. * without ResolvedType
  515. *
  516. * @param weaver
  517. * @param loader
  518. * @param definitions
  519. */
  520. private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  521. String fastMatchInfo = null;
  522. for (Definition definition : definitions) {
  523. for (String value : definition.getIncludePatterns()) {
  524. hasIncludes = true;
  525. String include = value;
  526. fastMatchInfo = looksLikeStartsWith(include);
  527. if (fastMatchInfo != null) {
  528. includeStartsWith.add(fastMatchInfo);
  529. } else if (include.equals("*")) {
  530. includeStar = true;
  531. } else if ((fastMatchInfo = looksLikeExactName(include)) != null) {
  532. includeExactName.add(fastMatchInfo);
  533. } else {
  534. TypePattern includePattern = new PatternParser(include).parseTypePattern();
  535. includeTypePattern.add(includePattern);
  536. }
  537. }
  538. for (String s : definition.getExcludePatterns()) {
  539. hasExcludes = true;
  540. String exclude = s;
  541. fastMatchInfo = looksLikeStartsWith(exclude);
  542. if (fastMatchInfo != null) {
  543. excludeStartsWith.add(fastMatchInfo);
  544. } else if ((fastMatchInfo = looksLikeStarDotDotStarExclude(exclude)) != null) {
  545. excludeStarDotDotStar.add(fastMatchInfo);
  546. } else if ((fastMatchInfo = looksLikeExactName(exclude)) != null) {
  547. excludeExactName.add(exclude);
  548. } else if ((fastMatchInfo = looksLikeEndsWith(exclude)) != null) {
  549. excludeEndsWith.add(fastMatchInfo);
  550. } else if (exclude
  551. .equals("org.codehaus.groovy..* && !org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController*")) {
  552. // TODO need a more sophisticated analysis here, to allow for similar situations
  553. excludeSpecial.add(new String[]{"org.codehaus.groovy.",
  554. "org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController"});
  555. // for the related test:
  556. // } else if (exclude.equals("testdata..* && !testdata.sub.Oran*")) {
  557. // excludeSpecial.add(new String[] { "testdata.", "testdata.sub.Oran" });
  558. } else {
  559. TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
  560. excludeTypePattern.add(excludePattern);
  561. }
  562. }
  563. }
  564. }
  565. /**
  566. * Checks if the pattern looks like "*..*XXXX*" and if so returns XXXX. This will enable fast name matching of CGLIB exclusion
  567. *
  568. */
  569. private String looksLikeStarDotDotStarExclude(String typePattern) {
  570. if (!typePattern.startsWith("*..*")) {
  571. return null;
  572. }
  573. if (!typePattern.endsWith("*")) {
  574. return null;
  575. }
  576. String subPattern = typePattern.substring(4, typePattern.length() - 1);
  577. if (hasStarDot(subPattern, 0)) {
  578. return null;
  579. }
  580. return subPattern.replace('$', '.');
  581. }
  582. /**
  583. * Checks if the pattern looks like "com.foo.Bar" - an exact name
  584. */
  585. private String looksLikeExactName(String typePattern) {
  586. if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.contains("*")) {
  587. return null;
  588. }
  589. return typePattern.replace('$', '.');
  590. }
  591. /**
  592. * Checks if the pattern looks like "*Exception"
  593. */
  594. private String looksLikeEndsWith(String typePattern) {
  595. if (typePattern.charAt(0) != '*') {
  596. return null;
  597. }
  598. if (hasSpaceAnnotationPlus(typePattern, 1) || hasStarDot(typePattern, 1)) {
  599. return null;
  600. }
  601. return typePattern.substring(1).replace('$', '.');
  602. }
  603. /**
  604. * Determine if something in the string is going to affect our ability to optimize. Checks for: ' ' '@' '+'
  605. */
  606. private boolean hasSpaceAnnotationPlus(String string, int pos) {
  607. for (int i = pos, max = string.length(); i < max; i++) {
  608. char ch = string.charAt(i);
  609. if (ch == ' ' || ch == '@' || ch == '+') {
  610. return true;
  611. }
  612. }
  613. return false;
  614. }
  615. /**
  616. * Determine if something in the string is going to affect our ability to optimize. Checks for: '*' '.'
  617. */
  618. private boolean hasStarDot(String string, int pos) {
  619. for (int i = pos, max = string.length(); i < max; i++) {
  620. char ch = string.charAt(i);
  621. if (ch == '*' || ch == '.') {
  622. return true;
  623. }
  624. }
  625. return false;
  626. }
  627. /**
  628. * Checks if the type pattern looks like "com.foo..*"
  629. */
  630. private String looksLikeStartsWith(String typePattern) {
  631. if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.charAt(typePattern.length() - 1) != '*') {
  632. return null;
  633. }
  634. // now must looks like with "charsss..*" or "cha.rss..*" etc
  635. // note that "*" and "*..*" won't be fast matched
  636. // and that "charsss.*" will not neither
  637. int length = typePattern.length();
  638. if (typePattern.endsWith("..*") && length > 3) {
  639. if (typePattern.indexOf("..") == length - 3 // no ".." before last sequence
  640. && typePattern.indexOf('*') == length - 1) { // no earlier '*'
  641. return typePattern.substring(0, length - 2).replace('$', '.'); // "charsss." or "char.rss." etc
  642. }
  643. }
  644. return null;
  645. }
  646. /**
  647. * Register the dump filter
  648. *
  649. * @param weaver
  650. * @param loader
  651. * @param definitions
  652. */
  653. private void registerDump(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  654. for (Definition definition : definitions) {
  655. for (String dump : definition.getDumpPatterns()) {
  656. TypePattern pattern = new PatternParser(dump).parseTypePattern();
  657. dumpTypePattern.add(pattern);
  658. }
  659. if (definition.shouldDumpBefore()) {
  660. dumpBefore = true;
  661. }
  662. if (definition.createDumpDirPerClassloader()) {
  663. dumpDirPerClassloader = true;
  664. }
  665. }
  666. }
  667. /**
  668. * Determine whether a type should be accepted for weaving, by checking it against any includes/excludes.
  669. *
  670. * @param className the name of the type to possibly accept
  671. * @param bytes the bytecode for the type (in case we need to look inside, eg. annotations)
  672. * @return true if it should be accepted for weaving
  673. */
  674. @Override
  675. protected boolean accept(String className, byte[] bytes) {
  676. if (!hasExcludes && !hasIncludes) {
  677. return true;
  678. }
  679. // still try to avoid ResolvedType if we have simple patterns
  680. String fastClassName = className.replace('/', '.');
  681. for (String excludeStartsWithString : excludeStartsWith) {
  682. if (fastClassName.startsWith(excludeStartsWithString)) {
  683. return false;
  684. }
  685. }
  686. // Fast exclusion of patterns like: "*..*CGLIB*"
  687. if (!excludeStarDotDotStar.isEmpty()) {
  688. for (String namePiece : excludeStarDotDotStar) {
  689. int index = fastClassName.lastIndexOf('.');
  690. if (fastClassName.indexOf(namePiece, index + 1) != -1) {
  691. return false;
  692. }
  693. }
  694. }
  695. fastClassName = fastClassName.replace('$', '.');
  696. if (!excludeEndsWith.isEmpty()) {
  697. for (String lastPiece : excludeEndsWith) {
  698. if (fastClassName.endsWith(lastPiece)) {
  699. return false;
  700. }
  701. }
  702. }
  703. // Fast exclusion of exact names
  704. if (!excludeExactName.isEmpty()) {
  705. for (String name : excludeExactName) {
  706. if (fastClassName.equals(name)) {
  707. return false;
  708. }
  709. }
  710. }
  711. if (!excludeSpecial.isEmpty()) {
  712. for (String[] entry : excludeSpecial) {
  713. String excludeThese = entry[0];
  714. String exceptThese = entry[1];
  715. if (fastClassName.startsWith(excludeThese) && !fastClassName.startsWith(exceptThese)) {
  716. return false;
  717. }
  718. }
  719. }
  720. /*
  721. * Bug 120363 If we have an exclude pattern that cannot be matched using "starts with" then we cannot fast accept
  722. */
  723. boolean didSomeIncludeMatching = false;
  724. if (excludeTypePattern.isEmpty()) {
  725. if (includeStar) {
  726. return true;
  727. }
  728. if (!includeExactName.isEmpty()) {
  729. didSomeIncludeMatching = true;
  730. for (String exactname : includeExactName) {
  731. if (fastClassName.equals(exactname)) {
  732. return true;
  733. }
  734. }
  735. }
  736. boolean fastAccept = false;// defaults to false if no fast include
  737. for (String s : includeStartsWith) {
  738. didSomeIncludeMatching = true;
  739. fastAccept = fastClassName.startsWith(s);
  740. if (fastAccept) {
  741. return true;
  742. }
  743. }
  744. // We may have processed all patterns now... check that and return
  745. if (includeTypePattern.isEmpty()) {
  746. return !didSomeIncludeMatching;
  747. }
  748. }
  749. boolean accept;
  750. try {
  751. ensureDelegateInitialized(className, bytes);
  752. ResolvedType classInfo = delegateForCurrentClass.getResolvedTypeX();
  753. // exclude are "AND"ed
  754. for (TypePattern typePattern : excludeTypePattern) {
  755. if (typePattern.matchesStatically(classInfo)) {
  756. // exclude match - skip
  757. return false;
  758. }
  759. }
  760. // include are "OR"ed
  761. if (includeStar) {
  762. return true;
  763. }
  764. if (!includeExactName.isEmpty()) {
  765. didSomeIncludeMatching = true;
  766. for (String exactname : includeExactName) {
  767. if (fastClassName.equals(exactname)) {
  768. return true;
  769. }
  770. }
  771. }
  772. for (String s : includeStartsWith) {
  773. didSomeIncludeMatching = true;
  774. boolean fastaccept = fastClassName.startsWith(s);
  775. if (fastaccept) {
  776. return true;
  777. }
  778. }
  779. accept = !didSomeIncludeMatching; // only true if no includes at all
  780. for (TypePattern typePattern : includeTypePattern) {
  781. accept = typePattern.matchesStatically(classInfo);
  782. if (accept) {
  783. break;
  784. }
  785. // goes on if this include did not match ("OR"ed)
  786. }
  787. } finally {
  788. this.bcelWorld.demote();
  789. }
  790. return accept;
  791. }
  792. // FIXME we don't use include/exclude of others aop.xml
  793. // this can be nice but very dangerous as well to change that
  794. private boolean acceptAspect(String aspectClassName) {
  795. // avoid ResolvedType if not needed
  796. if (aspectExcludeTypePattern.isEmpty() && aspectIncludeTypePattern.isEmpty()) {
  797. return true;
  798. }
  799. // still try to avoid ResolvedType if we have simple patterns
  800. // EXCLUDE: if one match then reject
  801. String fastClassName = aspectClassName.replace('/', '.').replace('.', '$');
  802. for (String value : aspectExcludeStartsWith) {
  803. if (fastClassName.startsWith(value)) {
  804. return false;
  805. }
  806. }
  807. // INCLUDE: if one match then accept
  808. for (String s : aspectIncludeStartsWith) {
  809. if (fastClassName.startsWith(s)) {
  810. return true;
  811. }
  812. }
  813. // needs further analysis
  814. ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(aspectClassName), true);
  815. // exclude are "AND"ed
  816. for (TypePattern typePattern: aspectExcludeTypePattern) {
  817. if (typePattern.matchesStatically(classInfo)) {
  818. // exclude match - skip
  819. return false;
  820. }
  821. }
  822. // include are "OR"ed
  823. boolean accept = true;// defaults to true if no include
  824. for (TypePattern typePattern: aspectIncludeTypePattern) {
  825. accept = typePattern.matchesStatically(classInfo);
  826. if (accept) {
  827. break;
  828. }
  829. // goes on if this include did not match ("OR"ed)
  830. }
  831. return accept;
  832. }
  833. @Override
  834. protected boolean shouldDump(String className, boolean before) {
  835. // Don't dump before weaving unless asked to
  836. if (before && !dumpBefore) {
  837. return false;
  838. }
  839. // avoid ResolvedType if not needed
  840. if (dumpTypePattern.isEmpty()) {
  841. return false;
  842. }
  843. // TODO AV - optimize for className.startWith only
  844. ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(className), true);
  845. // dump
  846. for (TypePattern typePattern : dumpTypePattern) {
  847. if (typePattern.matchesStatically(classInfo)) {
  848. // dump match
  849. return true;
  850. }
  851. }
  852. return false;
  853. }
  854. @Override
  855. protected String getDumpDir() {
  856. if (dumpDirPerClassloader) {
  857. StringBuilder dir = new StringBuilder();
  858. dir.append("_ajdump").append(File.separator).append(weavingContext.getId());
  859. return dir.toString();
  860. } else {
  861. return super.getDumpDir();
  862. }
  863. }
  864. /*
  865. * shared classes methods
  866. */
  867. /**
  868. * @return Returns the key.
  869. */
  870. public String getNamespace() {
  871. // System.out.println("ClassLoaderWeavingAdaptor.getNamespace() classloader=" + weavingContext.getClassLoaderName() +
  872. // ", namespace=" + namespace);
  873. if (namespace == null) {
  874. return "";
  875. } else {
  876. return new String(namespace);
  877. }
  878. }
  879. /**
  880. * Check to see if any classes are stored in the generated classes cache. Then flush the cache if it is not empty
  881. *
  882. * @param className TODO
  883. * @return true if a class has been generated and is stored in the cache
  884. */
  885. public boolean generatedClassesExistFor(String className) {
  886. // System.err.println("? ClassLoaderWeavingAdaptor.generatedClassesExist() classname=" + className + ", size=" +
  887. // generatedClasses);
  888. if (className == null) {
  889. return !generatedClasses.isEmpty();
  890. } else {
  891. return generatedClasses.containsKey(className);
  892. }
  893. }
  894. /**
  895. * Flush the generated classes cache
  896. */
  897. public void flushGeneratedClasses() {
  898. // System.err.println("? ClassLoaderWeavingAdaptor.flushGeneratedClasses() generatedClasses=" + generatedClasses);
  899. generatedClasses = new HashMap<>();
  900. }
  901. /**
  902. * Remove generated classes based on the supplied className. This will
  903. * remove any entries related to this name - so the class itself plus
  904. * and inner classes.
  905. * @param className a slashed classname (e.g. com/foo/Bar)
  906. */
  907. public void flushGeneratedClassesFor(String className) {
  908. try {
  909. String dottedClassName = className.replace('/', '.');
  910. String dottedClassNameDollar = dottedClassName+"$"; // to pickup inner classes
  911. Iterator<Map.Entry<String, IUnwovenClassFile>> iter = generatedClasses.entrySet().iterator();
  912. while (iter.hasNext()) {
  913. Entry<String, IUnwovenClassFile> next = iter.next();
  914. String existingGeneratedName = next.getKey();
  915. if (existingGeneratedName.equals(dottedClassName) ||
  916. existingGeneratedName.startsWith(dottedClassNameDollar)) {
  917. iter.remove();
  918. }
  919. }
  920. } catch (Throwable t) {
  921. new RuntimeException("Unexpected problem tidying up generated classes for "+className,t).printStackTrace();
  922. }
  923. }
  924. private static final Object lock = new Object();
  925. /**
  926. * Instance of either {@link sun.misc.Unsafe} or {@link jdk.internal.misc.Unsafe}. Luckily, both types have
  927. * {@code defineClass} methods with identical signatures. I.e., method handle {@link #defineClassMethodHandle} can be
  928. * invoked with the same set of parameters for both types.
  929. */
  930. private static Object unsafeInstance = null;
  931. /**
  932. * Method handle for defining new classes in arbitrary class loaders. For invocation, use in connection with
  933. * {@link #unsafeInstance}.
  934. */
  935. private static MethodHandle defineClassMethodHandle;
  936. static {
  937. try {
  938. createDefineClassMethodHandle();
  939. }
  940. catch (Exception initializationError) {
  941. new RuntimeException(
  942. "The aspect weaver cannot determine any valid method to define auxiliary classes in arbitrary class loaders. " +
  943. "Aspect weaving will *not* work, and you will see subsequent errors. Please search for corresponding " +
  944. "issues at https://github.com/eclipse-aspectj/aspectj/issues. If there are none, please create a new one.",
  945. initializationError
  946. ).printStackTrace();
  947. }
  948. }
  949. /**
  950. * Scaffolding for defining classes in arbitrary class loaders
  951. * <p>
  952. * Inspired by and shamelessly adapted from <a href="https://bit.ly/3w10oH5">Byte Buddy's {@code ClassInjector}</a>.
  953. * Special thanks to Byte Buddy (BB) author Rafael Winterhalter, who briefly mentioned this approach in a
  954. * <a href="https://bit.ly/3SjFOZY">GitHub comment</a> related to JDK issue
  955. * <a href="https://bugs.openjdk.org/browse/JDK-8200559">JDK-8200559</a>.
  956. * <p>
  957. * <b>Background:</b> Instead of BB, we use ASM and reflection as follows:
  958. * <ul>
  959. * <li>
  960. * Create a mirror class for {@link AccessibleObject} with a different package name in a separate, throw-away
  961. * class loader.
  962. * </li>
  963. * <li>
  964. * Use the mirror class to calculate the {@link Unsafe#objectFieldOffset(Field)} for boolean field
  965. * {@link AccessibleObject#override}, which is expected to be identical to the offset of the same field in the
  966. * original class.
  967. * </li>
  968. * <li>
  969. * After we have the offset, we can use it to override the field value in the original class, deactivating access
  970. * checks for {@link jdk.internal.misc.Unsafe#defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain)},
  971. * the method we need to execute using a method handle.
  972. * </li>
  973. * </ul>
  974. * All these serve the sole purpose enable LTW without {@code --add-opens java.base/java.lang=ALL-UNNAMED} on the
  975. * JVM command line on JDK 16+, which was necessary for AspectJ 1.9.7 (Java 16) to 1.9.21 (Java 21).
  976. *
  977. * @throws Exception if anything goes wrong, trying to determine a usable {@code defineClass} method handle from any
  978. * of the inspected classes
  979. */
  980. private static synchronized void createDefineClassMethodHandle() throws Exception {
  981. Unsafe publicUnsafeInstance = null;
  982. try {
  983. Field publicUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
  984. publicUnsafeField.setAccessible(true);
  985. publicUnsafeInstance = (Unsafe) publicUnsafeField.get(null);
  986. synchronized (lock) {
  987. unsafeInstance = publicUnsafeInstance;
  988. defineClassMethodHandle = createMethodHandle(
  989. "sun.misc.Unsafe", "defineClass",
  990. String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class
  991. );
  992. }
  993. }
  994. catch (Exception publicUnsafeException) {
  995. if (publicUnsafeInstance == null)
  996. throw publicUnsafeException;
  997. long overrideOffset = getAccessibleObjectOverrideOffset(publicUnsafeInstance);
  998. Class<?> internalUnsafeType = Class.forName("jdk.internal.misc.Unsafe");
  999. Field internalUnsafeField = internalUnsafeType.getDeclaredField("theUnsafe");
  1000. publicUnsafeInstance.putBoolean(internalUnsafeField, overrideOffset, true);
  1001. Method internalUnsafeDefineClassMethod = internalUnsafeType.getMethod(
  1002. "defineClass",
  1003. String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class
  1004. );
  1005. publicUnsafeInstance.putBoolean(internalUnsafeDefineClassMethod, overrideOffset, true);
  1006. synchronized (lock) {
  1007. unsafeInstance = internalUnsafeField.get(null);
  1008. defineClassMethodHandle = createMethodHandle(internalUnsafeDefineClassMethod);
  1009. }
  1010. }
  1011. }
  1012. private static long getAccessibleObjectOverrideOffset(Unsafe unsafe)
  1013. throws IOException, ClassNotFoundException, NoSuchFieldException
  1014. {
  1015. Objects.requireNonNull(unsafe);
  1016. Field overrideField;
  1017. try {
  1018. overrideField = AccessibleObject.class.getDeclaredField("override");
  1019. }
  1020. catch (NoSuchFieldException ignored) {
  1021. // On JDK 12+, field AccessibleObject.override is protected from reflection. The work-around is to create a
  1022. // mirror class with the same field layout by transforming the original class, so we can calculate the field
  1023. // offset of 'override' and set a value in the original class using the now known offset.
  1024. Class<?> mirrorClass = getMirrorClass(
  1025. "java.lang.reflect.AccessibleObject", "org.aspectj.mirror.AccessibleObject", true
  1026. );
  1027. overrideField = mirrorClass.getDeclaredField("override");
  1028. }
  1029. return unsafe.objectFieldOffset(overrideField);
  1030. }
  1031. public static MethodHandle createMethodHandle(String className, String methodName, Class<?>... argumentTypes)
  1032. throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException
  1033. {
  1034. Class<?> clazz = Class.forName(className);
  1035. Method method = clazz.getDeclaredMethod(methodName, argumentTypes);
  1036. return createMethodHandle(method, false);
  1037. }
  1038. public static MethodHandle createMethodHandle(Method method) throws IllegalAccessException {
  1039. return createMethodHandle(method, false);
  1040. }
  1041. public static MethodHandle createMethodHandle(Method method, boolean setAccessible)
  1042. throws IllegalAccessException
  1043. {
  1044. // Use Method::setAccessible to access private methods. Caveat: This does not work for classes in packages not
  1045. // exported to the calling module (for LTW usually the unnamed module).
  1046. if (setAccessible)
  1047. method.setAccessible(true);
  1048. return MethodHandles.lookup().unreflect(method);
  1049. }
  1050. @SuppressWarnings("SameParameterValue")
  1051. private static Class<?> getMirrorClass(String originalClass, String mirrorClass, boolean removeMethods)
  1052. throws IOException, ClassNotFoundException
  1053. {
  1054. Objects.requireNonNull(originalClass);
  1055. Objects.requireNonNull(mirrorClass);
  1056. if (mirrorClass.equals(originalClass))
  1057. throw new IllegalArgumentException("Mirror class name must be different from original " + originalClass);
  1058. byte[] mirrorClassBytes = getMirrorClassBytes(originalClass, mirrorClass, removeMethods);
  1059. ClassLoader mirrorClassLoader = new SingleClassLoader(mirrorClass, mirrorClassBytes);
  1060. return mirrorClassLoader.loadClass(mirrorClass);
  1061. }
  1062. private static byte[] getMirrorClassBytes(String originalClass, String mirrorClass, boolean removeMethods)
  1063. throws IOException, ClassNotFoundException
  1064. {
  1065. Class<?> aClass = Class.forName(originalClass);
  1066. try (InputStream input = aClass.getResourceAsStream(aClass.getSimpleName() + ".class")) {
  1067. Objects.requireNonNull(input);
  1068. ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  1069. ClassRemapper classRemapper = new ClassRemapper(classWriter, new ClassNameRemapper(originalClass, mirrorClass));
  1070. ClassVisitor classVisitor = removeMethods ? new MethodAndConstructorRemover(classRemapper) : classRemapper;
  1071. new ClassReader(input).accept(classVisitor, 0);
  1072. return classWriter.toByteArray();
  1073. }
  1074. }
  1075. private static class ClassNameRemapper extends Remapper {
  1076. private final String originalClass;
  1077. private final String mirrorClass;
  1078. public ClassNameRemapper(String originalClass, String mirrorClass) {
  1079. this.originalClass = originalClass.replace('.', '/');
  1080. this.mirrorClass = mirrorClass.replace('.', '/');
  1081. }
  1082. @Override
  1083. public String map(String internalName) {
  1084. return internalName.equals(originalClass) ? mirrorClass : internalName;
  1085. }
  1086. }
  1087. /**
  1088. * ASM class visitor removing all methods and constructors from the given class, leaving only the original fields
  1089. */
  1090. private static class MethodAndConstructorRemover extends ClassVisitor {
  1091. public MethodAndConstructorRemover(ClassRemapper classRemapper) {
  1092. super(Opcodes.ASM9, classRemapper);
  1093. }
  1094. @Override
  1095. public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
  1096. // Do not visit any methods or constructors, effectively removing them
  1097. return null;
  1098. }
  1099. }
  1100. /**
  1101. * Throw-away child classloader with the sole purpose to define a single {@link Class} from the given bytecode
  1102. */
  1103. private static class SingleClassLoader extends ClassLoader {
  1104. private final String mirrorClass;
  1105. private final byte[] mirrorClassBytes;
  1106. private SingleClassLoader(String mirrorClass, byte[] mirrorClassBytes) {
  1107. super(SingleClassLoader.class.getClassLoader());
  1108. this.mirrorClass = mirrorClass;
  1109. this.mirrorClassBytes = mirrorClassBytes;
  1110. }
  1111. @Override
  1112. public Class<?> loadClass(String name) throws ClassNotFoundException {
  1113. return name.equals(mirrorClass)
  1114. ? super.defineClass(null, mirrorClassBytes, 0, mirrorClassBytes.length)
  1115. : super.loadClass(name);
  1116. }
  1117. }
  1118. private void defineClass(ClassLoader loader, String name, byte[] bytes) {
  1119. defineClass(loader, name, bytes, null);
  1120. }
  1121. private void defineClass(ClassLoader loader, String name, byte[] bytes, ProtectionDomain protectionDomain) {
  1122. if (trace.isTraceEnabled())
  1123. trace.enter("defineClass", this, new Object[] { loader, name, bytes });
  1124. debug("generating class '" + name + "'");
  1125. Class<?> definedClass = null;
  1126. try {
  1127. if (defineClassMethodHandle == null)
  1128. throw new RuntimeException("no valid method to define auxiliary classes -> weaver not working");
  1129. definedClass = (Class<?>) defineClassMethodHandle
  1130. .bindTo(unsafeInstance)
  1131. .invokeWithArguments(name, bytes, 0, bytes.length, loader, protectionDomain);
  1132. }
  1133. catch (Throwable t) {
  1134. t.printStackTrace(System.err);
  1135. warn("define generated class failed", t);
  1136. }
  1137. finally {
  1138. if (trace.isTraceEnabled())
  1139. trace.exit("defineClass", definedClass);
  1140. }
  1141. }
  1142. }