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.

BcelAccessForInlineMunger.java 17KB

4 years ago
13 years ago
4 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
4 years ago
14 years ago
14 years ago
14 years ago
13 years ago
14 years ago
14 years ago
13 years ago
14 years ago
13 years ago
14 years ago
13 years ago
14 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*******************************************************************************
  2. * Copyright (c) 2005 Contributors.
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Alexandre Vasseur initial implementation
  11. *******************************************************************************/
  12. package org.aspectj.weaver.bcel;
  13. import java.lang.reflect.Modifier;
  14. import java.util.ArrayList;
  15. import java.util.HashMap;
  16. import java.util.HashSet;
  17. import java.util.List;
  18. import java.util.Map;
  19. import java.util.Set;
  20. import org.aspectj.apache.bcel.Constants;
  21. import org.aspectj.apache.bcel.classfile.ConstantPool;
  22. import org.aspectj.apache.bcel.generic.FieldInstruction;
  23. import org.aspectj.apache.bcel.generic.Instruction;
  24. import org.aspectj.apache.bcel.generic.InstructionConstants;
  25. import org.aspectj.apache.bcel.generic.InstructionFactory;
  26. import org.aspectj.apache.bcel.generic.InstructionHandle;
  27. import org.aspectj.apache.bcel.generic.InstructionList;
  28. import org.aspectj.apache.bcel.generic.InvokeDynamic;
  29. import org.aspectj.apache.bcel.generic.InvokeInstruction;
  30. import org.aspectj.apache.bcel.generic.Type;
  31. import org.aspectj.weaver.AjAttribute;
  32. import org.aspectj.weaver.AjcMemberMaker;
  33. import org.aspectj.weaver.Member;
  34. import org.aspectj.weaver.NameMangler;
  35. import org.aspectj.weaver.ResolvedMember;
  36. import org.aspectj.weaver.ResolvedType;
  37. import org.aspectj.weaver.Shadow;
  38. import org.aspectj.weaver.UnresolvedType;
  39. /**
  40. * Looks for all access to method or field that are not public within the body of the around advices and replace the invocations to
  41. * a wrapper call so that the around advice can further be inlined.
  42. * <p>
  43. * This munger is used for @AJ aspects for which inlining wrapper is not done at compile time.
  44. * </p>
  45. * <p>
  46. * Specific state and logic is kept in the munger ala ITD so that call/get/set pointcuts can still be matched on the wrapped member
  47. * thanks to the EffectiveSignature attribute.
  48. * </p>
  49. *
  50. * @author Alexandre Vasseur
  51. * @author Andy Clement
  52. */
  53. public class BcelAccessForInlineMunger extends BcelTypeMunger {
  54. private Map<String, ResolvedMember> inlineAccessors;
  55. private LazyClassGen aspectGen;
  56. /**
  57. * The wrapper methods representing any created inlineAccessors
  58. */
  59. private Set<LazyMethodGen> inlineAccessorMethodGens;
  60. public BcelAccessForInlineMunger(ResolvedType aspectType) {
  61. super(null, aspectType);
  62. if (aspectType.getWorld().isXnoInline()) {
  63. throw new Error("This should not happen");
  64. }
  65. }
  66. @Override
  67. public boolean munge(BcelClassWeaver weaver) {
  68. aspectGen = weaver.getLazyClassGen();
  69. inlineAccessors = new HashMap<>(0);
  70. inlineAccessorMethodGens = new HashSet<>();
  71. // look for all @Around advices
  72. for (LazyMethodGen methodGen : aspectGen.getMethodGens()) {
  73. if (methodGen.hasAnnotation(UnresolvedType.forName("org/aspectj/lang/annotation/Around"))) {
  74. openAroundAdvice(methodGen);
  75. }
  76. }
  77. // add the accessors
  78. for (LazyMethodGen lazyMethodGen : inlineAccessorMethodGens) {
  79. aspectGen.addMethodGen(lazyMethodGen);
  80. }
  81. // flush some
  82. inlineAccessorMethodGens = null;
  83. // we keep m_inlineAccessorsResolvedMembers for shadow matching
  84. return true;
  85. }
  86. /**
  87. * Looks in the wrapper we have added so that we can find their effective signature if needed
  88. */
  89. @Override
  90. public ResolvedMember getMatchingSyntheticMember(Member member) {
  91. ResolvedMember rm = inlineAccessors.get(member.getName());// + member.getSignature());
  92. // System.err.println("lookup for " + member.getName() + ":" + member.getSignature() + " = "
  93. // + (rm == null ? "" : rm.getName()));
  94. return rm;
  95. }
  96. @Override
  97. public ResolvedMember getSignature() {
  98. return null;
  99. }
  100. /**
  101. * Match only the aspect for which we act
  102. */
  103. @Override
  104. public boolean matches(ResolvedType onType) {
  105. return aspectType.equals(onType);
  106. }
  107. /**
  108. * Prepare the around advice, flag it as cannot be inlined if it can't be
  109. */
  110. private void openAroundAdvice(LazyMethodGen aroundAdvice) {
  111. InstructionHandle curr = aroundAdvice.getBody().getStart();
  112. InstructionHandle end = aroundAdvice.getBody().getEnd();
  113. ConstantPool cpg = aroundAdvice.getEnclosingClass().getConstantPool();
  114. InstructionFactory factory = aroundAdvice.getEnclosingClass().getFactory();
  115. boolean realizedCannotInline = false;
  116. while (curr != end) {
  117. if (realizedCannotInline) {
  118. // we know we cannot inline this advice so no need for futher handling
  119. break;
  120. }
  121. InstructionHandle next = curr.getNext();
  122. Instruction inst = curr.getInstruction();
  123. // open-up method call
  124. if ((inst instanceof InvokeInstruction)) {
  125. InvokeInstruction invoke = (InvokeInstruction) inst;
  126. if (invoke instanceof InvokeDynamic) {
  127. realizedCannotInline = true;
  128. break;
  129. }
  130. ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg)));
  131. // look in the whole method list and not just declared for super calls and alike
  132. List<ResolvedMember> methods = callee.getMethodsWithoutIterator(false, true, false);
  133. for (ResolvedMember resolvedMember : methods) {
  134. if (invoke.getName(cpg).equals(resolvedMember.getName())
  135. && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) {
  136. if ("<init>".equals(invoke.getName(cpg))
  137. ) {
  138. // If ctor invocation, we care about whether it is targeting exactly the same type
  139. // (ignore non public ctors in supertype of the target) (J13 - AbstractStringBuilder has something
  140. // that trips this up in one testcase)
  141. if (invoke.getClassName(cpg).equals(resolvedMember.getDeclaringType().getPackageName()+
  142. "."+resolvedMember.getDeclaringType().getClassName())) {
  143. // skipping open up for private constructor
  144. // can occur when aspect new a private inner type
  145. // too complex to handle new + dup + .. + invokespecial here.
  146. aroundAdvice.setCanInline(false);
  147. realizedCannotInline = true;
  148. }
  149. } else {
  150. // specific handling for super.foo() calls, where foo is non public
  151. ResolvedType memberType = aspectGen.getWorld().resolve(resolvedMember.getDeclaringType());
  152. if (!aspectType.equals(memberType) && memberType.isAssignableFrom(aspectType)) {
  153. // old test was...
  154. // if (aspectType.getSuperclass() != null
  155. // && aspectType.getSuperclass().getName().equals(resolvedMember.getDeclaringType().getName())) {
  156. ResolvedMember accessor = createOrGetInlineAccessorForSuperDispatch(resolvedMember);
  157. InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
  158. BcelWorld.makeBcelType(accessor.getReturnType()),
  159. BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKEVIRTUAL);
  160. curr.setInstruction(newInst);
  161. } else {
  162. ResolvedMember accessor = createOrGetInlineAccessorForMethod(resolvedMember);
  163. InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
  164. BcelWorld.makeBcelType(accessor.getReturnType()),
  165. BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC);
  166. curr.setInstruction(newInst);
  167. }
  168. }
  169. break;// ok we found a matching callee member and swapped the instruction with the accessor
  170. }
  171. }
  172. } else if (inst instanceof FieldInstruction) {
  173. FieldInstruction invoke = (FieldInstruction) inst;
  174. ResolvedType callee = aspectGen.getWorld().resolve(UnresolvedType.forName(invoke.getClassName(cpg)));
  175. for (int i = 0; i < callee.getDeclaredJavaFields().length; i++) {
  176. ResolvedMember resolvedMember = callee.getDeclaredJavaFields()[i];
  177. if (invoke.getName(cpg).equals(resolvedMember.getName())
  178. && invoke.getSignature(cpg).equals(resolvedMember.getSignature()) && !resolvedMember.isPublic()) {
  179. final ResolvedMember accessor;
  180. if ((inst.opcode == Constants.GETFIELD) || (inst.opcode == Constants.GETSTATIC)) {
  181. accessor = createOrGetInlineAccessorForFieldGet(resolvedMember);
  182. } else {
  183. accessor = createOrGetInlineAccessorForFieldSet(resolvedMember);
  184. }
  185. InvokeInstruction newInst = factory.createInvoke(aspectType.getName(), accessor.getName(),
  186. BcelWorld.makeBcelType(accessor.getReturnType()),
  187. BcelWorld.makeBcelTypes(accessor.getParameterTypes()), Constants.INVOKESTATIC);
  188. curr.setInstruction(newInst);
  189. break;// ok we found a matching callee member and swapped the instruction with the accessor
  190. }
  191. }
  192. }
  193. curr = next;
  194. }
  195. // no reason for not inlining this advice
  196. // since it is used for @AJ advice that cannot be inlined by defauilt
  197. // make sure we set inline to true since we have done this analysis
  198. if (!realizedCannotInline) {
  199. aroundAdvice.setCanInline(true);
  200. }
  201. }
  202. /**
  203. * Find (or add if not yet created) an inline wrapper for a non public method call
  204. */
  205. private ResolvedMember createOrGetInlineAccessorForMethod(ResolvedMember resolvedMember) {
  206. String accessorName = NameMangler.inlineAccessMethodForMethod(resolvedMember.getName(), resolvedMember.getDeclaringType(),
  207. aspectType);
  208. String key = accessorName;// new StringBuilder(accessorName).append(resolvedMember.getSignature()).toString();
  209. ResolvedMember inlineAccessor = inlineAccessors.get(key);
  210. // System.err.println(key + " accessor=" + inlineAccessor);
  211. if (inlineAccessor == null) {
  212. // add static method to aspect
  213. inlineAccessor = AjcMemberMaker.inlineAccessMethodForMethod(aspectType, resolvedMember);
  214. // add new accessor method to aspect bytecode
  215. InstructionFactory factory = aspectGen.getFactory();
  216. LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
  217. method.makeSynthetic();
  218. List<AjAttribute> methodAttributes = new ArrayList<>();
  219. methodAttributes.add(new AjAttribute.AjSynthetic());
  220. methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false));
  221. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
  222. // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut
  223. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
  224. inlineAccessorMethodGens.add(method);
  225. InstructionList il = method.getBody();
  226. int register = 0;
  227. for (int i = 0, max = inlineAccessor.getParameterTypes().length; i < max; i++) {
  228. UnresolvedType ptype = inlineAccessor.getParameterTypes()[i];
  229. Type type = BcelWorld.makeBcelType(ptype);
  230. il.append(InstructionFactory.createLoad(type, register));
  231. register += type.getSize();
  232. }
  233. il.append(Utility.createInvoke(factory, Modifier.isStatic(resolvedMember.getModifiers()) ? Constants.INVOKESTATIC
  234. : Constants.INVOKEVIRTUAL, resolvedMember));
  235. il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
  236. inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
  237. }
  238. return inlineAccessor;
  239. }
  240. /**
  241. * Add an inline wrapper for a non public super.method call
  242. */
  243. private ResolvedMember createOrGetInlineAccessorForSuperDispatch(ResolvedMember resolvedMember) {
  244. String accessor = NameMangler.superDispatchMethod(aspectType, resolvedMember.getName());
  245. String key = accessor;
  246. ResolvedMember inlineAccessor = inlineAccessors.get(key);
  247. if (inlineAccessor == null) {
  248. // add super accessor method to class:
  249. inlineAccessor = AjcMemberMaker.superAccessMethod(aspectType, resolvedMember);
  250. // add new accessor method to aspect bytecode
  251. InstructionFactory factory = aspectGen.getFactory();
  252. LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
  253. // flag it synthetic, AjSynthetic
  254. method.makeSynthetic();
  255. List<AjAttribute> methodAttributes = new ArrayList<>();
  256. methodAttributes.add(new AjAttribute.AjSynthetic());
  257. methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.MethodCall, false));
  258. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
  259. // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut
  260. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
  261. inlineAccessorMethodGens.add(method);
  262. InstructionList il = method.getBody();
  263. il.append(InstructionConstants.ALOAD_0);
  264. int register = 1;
  265. for (int i = 0; i < inlineAccessor.getParameterTypes().length; i++) {
  266. UnresolvedType typeX = inlineAccessor.getParameterTypes()[i];
  267. Type type = BcelWorld.makeBcelType(typeX);
  268. il.append(InstructionFactory.createLoad(type, register));
  269. register += type.getSize();
  270. }
  271. il.append(Utility.createInvoke(factory, Constants.INVOKESPECIAL, resolvedMember));
  272. il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
  273. inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
  274. }
  275. return inlineAccessor;
  276. }
  277. /**
  278. * Add an inline wrapper for a non public field get
  279. */
  280. private ResolvedMember createOrGetInlineAccessorForFieldGet(ResolvedMember resolvedMember) {
  281. String accessor = NameMangler.inlineAccessMethodForFieldGet(resolvedMember.getName(), resolvedMember.getDeclaringType(),
  282. aspectType);
  283. String key = accessor;
  284. ResolvedMember inlineAccessor = inlineAccessors.get(key);
  285. if (inlineAccessor == null) {
  286. // add static method to aspect
  287. inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldGet(aspectType, resolvedMember);
  288. // add new accessor method to aspect bytecode
  289. InstructionFactory factory = aspectGen.getFactory();
  290. LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
  291. // flag it synthetic, AjSynthetic
  292. method.makeSynthetic();
  293. List<AjAttribute> methodAttributes = new ArrayList<>();
  294. methodAttributes.add(new AjAttribute.AjSynthetic());
  295. methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldGet, false));
  296. // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut
  297. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
  298. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
  299. inlineAccessorMethodGens.add(method);
  300. InstructionList il = method.getBody();
  301. if (Modifier.isStatic(resolvedMember.getModifiers())) {
  302. // field accessed is static so no "this" as accessor sole parameter
  303. } else {
  304. il.append(InstructionConstants.ALOAD_0);
  305. }
  306. il.append(Utility.createGet(factory, resolvedMember));
  307. il.append(InstructionFactory.createReturn(BcelWorld.makeBcelType(inlineAccessor.getReturnType())));
  308. inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
  309. }
  310. return inlineAccessor;
  311. }
  312. /**
  313. * Add an inline wrapper for a non public field set
  314. */
  315. private ResolvedMember createOrGetInlineAccessorForFieldSet(ResolvedMember resolvedMember) {
  316. String accessor = NameMangler.inlineAccessMethodForFieldSet(resolvedMember.getName(), resolvedMember.getDeclaringType(),
  317. aspectType);
  318. String key = accessor;
  319. ResolvedMember inlineAccessor = inlineAccessors.get(key);
  320. if (inlineAccessor == null) {
  321. // add static method to aspect
  322. inlineAccessor = AjcMemberMaker.inlineAccessMethodForFieldSet(aspectType, resolvedMember);
  323. // add new accessor method to aspect bytecode
  324. InstructionFactory factory = aspectGen.getFactory();
  325. LazyMethodGen method = makeMethodGen(aspectGen, inlineAccessor);
  326. // flag it synthetic, AjSynthetic
  327. method.makeSynthetic();
  328. List<AjAttribute> methodAttributes = new ArrayList<>();
  329. methodAttributes.add(new AjAttribute.AjSynthetic());
  330. methodAttributes.add(new AjAttribute.EffectiveSignatureAttribute(resolvedMember, Shadow.FieldSet, false));
  331. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(0), aspectGen.getConstantPool()));
  332. // flag the effective signature, so that we can deobfuscate the signature to apply method call pointcut
  333. method.addAttribute(Utility.bcelAttribute(methodAttributes.get(1), aspectGen.getConstantPool()));
  334. inlineAccessorMethodGens.add(method);
  335. InstructionList il = method.getBody();
  336. if (Modifier.isStatic(resolvedMember.getModifiers())) {
  337. // field accessed is static so sole parameter is field value to be set
  338. il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 0));
  339. } else {
  340. il.append(InstructionConstants.ALOAD_0);
  341. il.append(InstructionFactory.createLoad(BcelWorld.makeBcelType(resolvedMember.getReturnType()), 1));
  342. }
  343. il.append(Utility.createSet(factory, resolvedMember));
  344. il.append(InstructionConstants.RETURN);
  345. inlineAccessors.put(key, new BcelMethod(aspectGen.getBcelObjectType(), method.getMethod(), methodAttributes));
  346. }
  347. return inlineAccessor;
  348. }
  349. }