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.

Utility.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.weaver.bcel;
  13. import java.io.ByteArrayInputStream;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.DataOutputStream;
  16. import java.io.IOException;
  17. import java.lang.reflect.Modifier;
  18. import org.aspectj.apache.bcel.Constants;
  19. import org.aspectj.apache.bcel.classfile.ClassParser;
  20. import org.aspectj.apache.bcel.classfile.JavaClass;
  21. import org.aspectj.apache.bcel.classfile.Method;
  22. import org.aspectj.apache.bcel.generic.ArrayType;
  23. import org.aspectj.apache.bcel.generic.BIPUSH;
  24. import org.aspectj.apache.bcel.generic.BasicType;
  25. import org.aspectj.apache.bcel.generic.BranchInstruction;
  26. import org.aspectj.apache.bcel.generic.ConstantPushInstruction;
  27. import org.aspectj.apache.bcel.generic.INSTANCEOF;
  28. import org.aspectj.apache.bcel.generic.Instruction;
  29. import org.aspectj.apache.bcel.generic.InstructionConstants;
  30. import org.aspectj.apache.bcel.generic.InstructionFactory;
  31. import org.aspectj.apache.bcel.generic.InstructionHandle;
  32. import org.aspectj.apache.bcel.generic.InstructionList;
  33. import org.aspectj.apache.bcel.generic.InstructionTargeter;
  34. import org.aspectj.apache.bcel.generic.LDC;
  35. import org.aspectj.apache.bcel.generic.ObjectType;
  36. import org.aspectj.apache.bcel.generic.ReferenceType;
  37. import org.aspectj.apache.bcel.generic.SIPUSH;
  38. import org.aspectj.apache.bcel.generic.SWITCH;
  39. import org.aspectj.apache.bcel.generic.Select;
  40. import org.aspectj.apache.bcel.generic.TargetLostException;
  41. import org.aspectj.apache.bcel.generic.Type;
  42. import org.aspectj.weaver.BCException;
  43. import org.aspectj.weaver.Member;
  44. import org.aspectj.weaver.ResolvedTypeX;
  45. import org.aspectj.weaver.TypeX;
  46. public class Utility {
  47. private Utility() {
  48. super();
  49. }
  50. public static Instruction createSuperInvoke(
  51. InstructionFactory fact,
  52. BcelWorld world,
  53. Member signature) {
  54. short kind;
  55. if (signature.isInterface()) {
  56. throw new RuntimeException("bad");
  57. } else if (signature.isPrivate() || signature.getName().equals("<init>")) {
  58. throw new RuntimeException("unimplemented, possibly bad");
  59. } else if (signature.isStatic()) {
  60. throw new RuntimeException("bad");
  61. } else {
  62. kind = Constants.INVOKESPECIAL;
  63. }
  64. return fact.createInvoke(
  65. signature.getDeclaringType().getName(),
  66. signature.getName(),
  67. BcelWorld.makeBcelType(signature.getReturnType()),
  68. BcelWorld.makeBcelTypes(signature.getParameterTypes()),
  69. kind);
  70. }
  71. // XXX don't need the world now
  72. public static Instruction createInvoke(
  73. InstructionFactory fact,
  74. BcelWorld world,
  75. Member signature) {
  76. short kind;
  77. if (signature.isInterface()) {
  78. kind = Constants.INVOKEINTERFACE;
  79. } else if (signature.isStatic()) {
  80. kind = Constants.INVOKESTATIC;
  81. } else if (signature.isPrivate() || signature.getName().equals("<init>")) {
  82. kind = Constants.INVOKESPECIAL;
  83. } else {
  84. kind = Constants.INVOKEVIRTUAL;
  85. }
  86. return fact.createInvoke(
  87. signature.getDeclaringType().getName(),
  88. signature.getName(),
  89. BcelWorld.makeBcelType(signature.getReturnType()),
  90. BcelWorld.makeBcelTypes(signature.getParameterTypes()),
  91. kind);
  92. }
  93. public static Instruction createGet(InstructionFactory fact, Member signature) {
  94. short kind;
  95. if (signature.isStatic()) {
  96. kind = Constants.GETSTATIC;
  97. } else {
  98. kind = Constants.GETFIELD;
  99. }
  100. return fact.createFieldAccess(
  101. signature.getDeclaringType().getName(),
  102. signature.getName(),
  103. BcelWorld.makeBcelType(signature.getReturnType()),
  104. kind);
  105. }
  106. public static Instruction createSet(InstructionFactory fact, Member signature) {
  107. short kind;
  108. if (signature.isStatic()) {
  109. kind = Constants.PUTSTATIC;
  110. } else {
  111. kind = Constants.PUTFIELD;
  112. }
  113. return fact.createFieldAccess(
  114. signature.getDeclaringType().getName(),
  115. signature.getName(),
  116. BcelWorld.makeBcelType(signature.getReturnType()),
  117. kind);
  118. }
  119. public static Instruction createInvoke(
  120. InstructionFactory fact,
  121. JavaClass declaringClass,
  122. Method newMethod) {
  123. short kind;
  124. if (newMethod.isInterface()) {
  125. kind = Constants.INVOKEINTERFACE;
  126. } else if (newMethod.isStatic()) {
  127. kind = Constants.INVOKESTATIC;
  128. } else if (newMethod.isPrivate() || newMethod.getName().equals("<init>")) {
  129. kind = Constants.INVOKESPECIAL;
  130. } else {
  131. kind = Constants.INVOKEVIRTUAL;
  132. }
  133. return fact.createInvoke(
  134. declaringClass.getClassName(),
  135. newMethod.getName(),
  136. Type.getReturnType(newMethod.getSignature()),
  137. Type.getArgumentTypes(newMethod.getSignature()),
  138. kind);
  139. }
  140. public static byte[] stringToUTF(String s) {
  141. try {
  142. ByteArrayOutputStream out0 = new ByteArrayOutputStream();
  143. DataOutputStream out1 = new DataOutputStream(out0);
  144. out1.writeUTF(s);
  145. return out0.toByteArray();
  146. } catch (IOException e) {
  147. throw new RuntimeException("sanity check");
  148. }
  149. }
  150. public static Instruction createInstanceof(InstructionFactory fact, ReferenceType t) {
  151. int cpoolEntry =
  152. (t instanceof ArrayType)
  153. ? fact.getConstantPool().addArrayClass((ArrayType)t)
  154. : fact.getConstantPool().addClass((ObjectType)t);
  155. return new INSTANCEOF(cpoolEntry);
  156. }
  157. public static Instruction createInvoke(
  158. InstructionFactory fact,
  159. LazyMethodGen m) {
  160. short kind;
  161. if (m.getEnclosingClass().isInterface()) {
  162. kind = Constants.INVOKEINTERFACE;
  163. } else if (m.isStatic()) {
  164. kind = Constants.INVOKESTATIC;
  165. } else if (m.isPrivate() || m.getName().equals("<init>")) {
  166. kind = Constants.INVOKESPECIAL;
  167. } else {
  168. kind = Constants.INVOKEVIRTUAL;
  169. }
  170. return fact.createInvoke(
  171. m.getClassName(),
  172. m.getName(),
  173. m.getReturnType(),
  174. m.getArgumentTypes(),
  175. kind);
  176. }
  177. // ??? these should perhaps be cached. Remember to profile this to see if it's a problem.
  178. public static String[] makeArgNames(int n) {
  179. String[] ret = new String[n];
  180. for (int i=0; i<n; i++) {
  181. ret[i] = "arg" + i;
  182. }
  183. return ret;
  184. }
  185. public static void appendConversion(
  186. InstructionList il,
  187. InstructionFactory fact,
  188. ResolvedTypeX fromType,
  189. ResolvedTypeX toType)
  190. {
  191. if (! toType.isConvertableFrom(fromType)) {
  192. throw new BCException("can't convert from " + fromType + " to " + toType);
  193. }
  194. if (toType.needsNoConversionFrom(fromType)) return;
  195. if (toType.equals(ResolvedTypeX.VOID)) {
  196. // assert fromType.equals(TypeX.OBJECT)
  197. il.append(InstructionFactory.createPop(fromType.getSize()));
  198. } else if (fromType.equals(ResolvedTypeX.VOID)) {
  199. // assert toType.equals(TypeX.OBJECT)
  200. il.append(InstructionFactory.createNull(Type.OBJECT));
  201. return;
  202. } else if (fromType.equals(TypeX.OBJECT)) {
  203. Type to = BcelWorld.makeBcelType(toType);
  204. if (toType.isPrimitive()) {
  205. String name = toType.toString() + "Value";
  206. il.append(
  207. fact.createInvoke(
  208. "org.aspectj.runtime.internal.Conversions",
  209. name,
  210. to,
  211. new Type[] { Type.OBJECT },
  212. Constants.INVOKESTATIC));
  213. } else {
  214. il.append(fact.createCheckCast((ReferenceType)to));
  215. }
  216. } else if (toType.equals(TypeX.OBJECT)) {
  217. // assert fromType.isPrimitive()
  218. Type from = BcelWorld.makeBcelType(fromType);
  219. String name = fromType.toString() + "Object";
  220. il.append(
  221. fact.createInvoke(
  222. "org.aspectj.runtime.internal.Conversions",
  223. name,
  224. Type.OBJECT,
  225. new Type[] { from },
  226. Constants.INVOKESTATIC));
  227. } else if (fromType.isPrimitive()) {
  228. // assert toType.isPrimitive()
  229. Type from = BcelWorld.makeBcelType(fromType);
  230. Type to = BcelWorld.makeBcelType(toType);
  231. try {
  232. il.append(fact.createCast(from, to));
  233. } catch (RuntimeException e) {
  234. il.append(fact.createCast(from, Type.INT));
  235. il.append(fact.createCast(Type.INT, to));
  236. }
  237. } else {
  238. Type to = BcelWorld.makeBcelType(toType);
  239. // assert ! fromType.isPrimitive() && ! toType.isPrimitive()
  240. il.append(fact.createCheckCast((ReferenceType) to));
  241. }
  242. }
  243. public static InstructionList createConversion(
  244. InstructionFactory fact,
  245. Type fromType,
  246. Type toType) {
  247. //System.out.println("cast to: " + toType);
  248. InstructionList il = new InstructionList();
  249. //PR71273
  250. if ((fromType.equals(Type.BYTE) || fromType.equals(Type.CHAR) || fromType.equals(Type.SHORT)) &&
  251. (toType.equals(Type.INT))) {
  252. return il;
  253. }
  254. if (fromType.equals(toType))
  255. return il;
  256. if (toType.equals(Type.VOID)) {
  257. il.append(InstructionFactory.createPop(fromType.getSize()));
  258. return il;
  259. }
  260. if (fromType.equals(Type.VOID)) {
  261. if (toType instanceof BasicType)
  262. throw new BCException("attempting to cast from void to basic type");
  263. il.append(InstructionFactory.createNull(Type.OBJECT));
  264. return il;
  265. }
  266. if (fromType.equals(Type.OBJECT)) {
  267. if (toType instanceof BasicType) {
  268. String name = toType.toString() + "Value";
  269. il.append(
  270. fact.createInvoke(
  271. "org.aspectj.runtime.internal.Conversions",
  272. name,
  273. toType,
  274. new Type[] { Type.OBJECT },
  275. Constants.INVOKESTATIC));
  276. return il;
  277. }
  278. }
  279. if (toType.equals(Type.OBJECT)) {
  280. if (fromType instanceof BasicType) {
  281. String name = fromType.toString() + "Object";
  282. il.append(
  283. fact.createInvoke(
  284. "org.aspectj.runtime.internal.Conversions",
  285. name,
  286. Type.OBJECT,
  287. new Type[] { fromType },
  288. Constants.INVOKESTATIC));
  289. return il;
  290. } else if (fromType instanceof ReferenceType) {
  291. return il;
  292. } else {
  293. throw new RuntimeException();
  294. }
  295. }
  296. if (fromType instanceof ReferenceType
  297. && ((ReferenceType)fromType).isAssignmentCompatibleWith(toType)) {
  298. return il;
  299. }
  300. il.append(fact.createCast(fromType, toType));
  301. return il;
  302. }
  303. public static Instruction createConstant(
  304. InstructionFactory fact,
  305. int i) {
  306. Instruction inst;
  307. switch(i) {
  308. case -1: inst = InstructionConstants.ICONST_M1; break;
  309. case 0: inst = InstructionConstants.ICONST_0; break;
  310. case 1: inst = InstructionConstants.ICONST_1; break;
  311. case 2: inst = InstructionConstants.ICONST_2; break;
  312. case 3: inst = InstructionConstants.ICONST_3; break;
  313. case 4: inst = InstructionConstants.ICONST_4; break;
  314. case 5: inst = InstructionConstants.ICONST_5; break;
  315. }
  316. if (i <= Byte.MAX_VALUE && i >= Byte.MIN_VALUE) {
  317. inst = new BIPUSH((byte)i);
  318. } else if (i <= Short.MAX_VALUE && i >= Short.MIN_VALUE) {
  319. inst = new SIPUSH((short)i);
  320. } else {
  321. inst = new LDC(fact.getClassGen().getConstantPool().addInteger(i));
  322. }
  323. return inst;
  324. }
  325. public static JavaClass makeJavaClass(String filename, byte[] bytes) {
  326. try {
  327. ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), filename);
  328. return parser.parse();
  329. } catch (IOException e) {
  330. throw new BCException("malformed class file");
  331. }
  332. }
  333. public static String arrayToString(int[] a) {
  334. int len = a.length;
  335. if (len == 0) return "[]";
  336. StringBuffer buf = new StringBuffer("[");
  337. buf.append(a[0]);
  338. for (int i = 1; i < len; i++) {
  339. buf.append(", ");
  340. buf.append(a[i]);
  341. }
  342. buf.append("]");
  343. return buf.toString();
  344. }
  345. /**
  346. * replace an instruction handle with another instruction, in this case, a branch instruction.
  347. *
  348. * @param ih the instruction handle to replace.
  349. * @param branchInstruction the branch instruction to replace ih with
  350. * @param enclosingMethod where to find ih's instruction list.
  351. */
  352. public static void replaceInstruction(
  353. InstructionHandle ih,
  354. BranchInstruction branchInstruction,
  355. LazyMethodGen enclosingMethod)
  356. {
  357. InstructionList il = enclosingMethod.getBody();
  358. InstructionHandle fresh = il.append(ih, branchInstruction);
  359. deleteInstruction(ih, fresh, enclosingMethod);
  360. }
  361. /** delete an instruction handle and retarget all targeters of the deleted instruction
  362. * to the next instruction. Obviously, this should not be used to delete
  363. * a control transfer instruction unless you know what you're doing.
  364. *
  365. * @param ih the instruction handle to delete.
  366. * @param enclosingMethod where to find ih's instruction list.
  367. */
  368. public static void deleteInstruction(
  369. InstructionHandle ih,
  370. LazyMethodGen enclosingMethod)
  371. {
  372. deleteInstruction(ih, ih.getNext(), enclosingMethod);
  373. }
  374. /** delete an instruction handle and retarget all targeters of the deleted instruction
  375. * to the provided target.
  376. *
  377. * @param ih the instruction handle to delete
  378. * @param retargetTo the instruction handle to retarget targeters of ih to.
  379. * @param enclosingMethod where to find ih's instruction list.
  380. */
  381. public static void deleteInstruction(
  382. InstructionHandle ih,
  383. InstructionHandle retargetTo,
  384. LazyMethodGen enclosingMethod)
  385. {
  386. InstructionList il = enclosingMethod.getBody();
  387. InstructionTargeter[] targeters = ih.getTargeters();
  388. if (targeters != null) {
  389. for (int i = targeters.length - 1; i >= 0; i--) {
  390. InstructionTargeter targeter = targeters[i];
  391. targeter.updateTarget(ih, retargetTo);
  392. }
  393. ih.removeAllTargeters();
  394. }
  395. try {
  396. il.delete(ih);
  397. } catch (TargetLostException e) {
  398. throw new BCException("this really can't happen");
  399. }
  400. }
  401. /**
  402. * Fix for Bugzilla #39479, #40109 patch contributed by Andy Clement
  403. *
  404. * Need to manually copy Select instructions - if we rely on the the 'fresh' object
  405. * created by copy(), the InstructionHandle array 'targets' inside the Select
  406. * object will not have been deep copied, so modifying targets in fresh will modify
  407. * the original Select - not what we want ! (It is a bug in BCEL to do with cloning
  408. * Select objects).
  409. *
  410. * <pre>
  411. * declare error:
  412. * call(* Instruction.copy()) && within(org.aspectj.weaver)
  413. * && !withincode(* Utility.copyInstruction(Instruction)):
  414. * "use Utility.copyInstruction to work-around bug in Select.copy()";
  415. * </pre>
  416. */
  417. public static Instruction copyInstruction(Instruction i) {
  418. if (i instanceof Select) {
  419. Select freshSelect = (Select)i;
  420. // Create a new targets array that looks just like the existing one
  421. InstructionHandle[] targets = new InstructionHandle[freshSelect.getTargets().length];
  422. for (int ii = 0; ii < targets.length; ii++) {
  423. targets[ii] = freshSelect.getTargets()[ii];
  424. }
  425. // Create a new select statement with the new targets array
  426. SWITCH switchStatement =
  427. new SWITCH(freshSelect.getMatchs(), targets, freshSelect.getTarget());
  428. return (Select)switchStatement.getInstruction();
  429. } else {
  430. return i.copy(); // Use clone for shallow copy...
  431. }
  432. }
  433. /** returns -1 if no source line attribute */
  434. // this naive version overruns the JVM stack size, if only Java understood tail recursion...
  435. // public static int getSourceLine(InstructionHandle ih) {
  436. // if (ih == null) return -1;
  437. //
  438. // InstructionTargeter[] ts = ih.getTargeters();
  439. // if (ts != null) {
  440. // for (int j = ts.length - 1; j >= 0; j--) {
  441. // InstructionTargeter t = ts[j];
  442. // if (t instanceof LineNumberTag) {
  443. // return ((LineNumberTag)t).getLineNumber();
  444. // }
  445. // }
  446. // }
  447. // return getSourceLine(ih.getNext());
  448. // }
  449. public static int getSourceLine(InstructionHandle ih) {
  450. int lookahead=0;
  451. // arbitrary rule that we will never lookahead more than 100 instructions for a line #
  452. while (lookahead++ < 100) {
  453. if (ih == null) return -1;
  454. InstructionTargeter[] ts = ih.getTargeters();
  455. if (ts != null) {
  456. for (int j = ts.length - 1; j >= 0; j--) {
  457. InstructionTargeter t = ts[j];
  458. if (t instanceof LineNumberTag) {
  459. return ((LineNumberTag)t).getLineNumber();
  460. }
  461. }
  462. }
  463. ih = ih.getNext();
  464. }
  465. //System.err.println("no line information available for: " + ih);
  466. return -1;
  467. }
  468. // assumes that there is no already extant source line tag. Otherwise we'll have to be better.
  469. public static void setSourceLine(InstructionHandle ih, int lineNumber) {
  470. ih.addTargeter(new LineNumberTag(lineNumber));
  471. }
  472. public static int makePublic(int i) {
  473. return i & ~(Modifier.PROTECTED | Modifier.PRIVATE) | Modifier.PUBLIC;
  474. }
  475. public static int makePrivate(int i) {
  476. return i & ~(Modifier.PROTECTED | Modifier.PUBLIC) | Modifier.PRIVATE;
  477. }
  478. public static BcelVar[] pushAndReturnArrayOfVars(
  479. ResolvedTypeX[] proceedParamTypes,
  480. InstructionList il,
  481. InstructionFactory fact,
  482. LazyMethodGen enclosingMethod)
  483. {
  484. int len = proceedParamTypes.length;
  485. BcelVar[] ret = new BcelVar[len];
  486. for (int i = len - 1; i >= 0; i--) {
  487. ResolvedTypeX typeX = proceedParamTypes[i];
  488. Type type = BcelWorld.makeBcelType(typeX);
  489. int local = enclosingMethod.allocateLocal(type);
  490. il.append(InstructionFactory.createStore(type, local));
  491. ret[i] = new BcelVar(typeX, local);
  492. }
  493. return ret;
  494. }
  495. public static boolean isConstantPushInstruction(Instruction i) {
  496. return (i instanceof ConstantPushInstruction) || (i instanceof LDC);
  497. }
  498. }