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.

ExprEditor.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.expr;
  17. import javassist.CannotCompileException;
  18. import javassist.CtClass;
  19. import javassist.bytecode.BadBytecode;
  20. import javassist.bytecode.CodeAttribute;
  21. import javassist.bytecode.CodeIterator;
  22. import javassist.bytecode.ExceptionTable;
  23. import javassist.bytecode.MethodInfo;
  24. import javassist.bytecode.Opcode;
  25. /**
  26. * A translator of method bodies.
  27. *
  28. * <p>The users can define a subclass of this class to customize how to
  29. * modify a method body. The overall architecture is similar to the
  30. * strategy pattern.
  31. *
  32. * <p>If <code>instrument()</code> is called in
  33. * <code>CtMethod</code>, the method body is scanned from the beginning
  34. * to the end.
  35. * Whenever an expression, such as a method call and a <code>new</code>
  36. * expression (object creation),
  37. * is found, <code>edit()</code> is called in <code>ExprEdit</code>.
  38. * <code>edit()</code> can inspect and modify the given expression.
  39. * The modification is reflected on the original method body. If
  40. * <code>edit()</code> does nothing, the original method body is not
  41. * changed.
  42. *
  43. * <p>The following code is an example:
  44. *
  45. * <pre>
  46. * CtMethod cm = ...;
  47. * cm.instrument(new ExprEditor() {
  48. * public void edit(MethodCall m) throws CannotCompileException {
  49. * if (m.getClassName().equals("Point")) {
  50. * System.out.println(m.getMethodName() + " line: "
  51. * + m.getLineNumber());
  52. * }
  53. * });
  54. * </pre>
  55. *
  56. * <p>This code inspects all method calls appearing in the method represented
  57. * by <code>cm</code> and it prints the names and the line numbers of the
  58. * methods declared in class <code>Point</code>. This code does not modify
  59. * the body of the method represented by <code>cm</code>. If the method
  60. * body must be modified, call <code>replace()</code>
  61. * in <code>MethodCall</code>.
  62. *
  63. * @see javassist.CtClass#instrument(ExprEditor)
  64. * @see javassist.CtMethod#instrument(ExprEditor)
  65. * @see javassist.CtConstructor#instrument(ExprEditor)
  66. * @see MethodCall
  67. * @see NewExpr
  68. * @see FieldAccess
  69. *
  70. * @see javassist.CodeConverter
  71. */
  72. public class ExprEditor {
  73. /**
  74. * Default constructor. It does nothing.
  75. */
  76. public ExprEditor() {}
  77. /**
  78. * Undocumented method. Do not use; internal-use only.
  79. */
  80. public boolean doit(CtClass clazz, MethodInfo minfo)
  81. throws CannotCompileException
  82. {
  83. CodeAttribute codeAttr = minfo.getCodeAttribute();
  84. if (codeAttr == null)
  85. return false;
  86. CodeIterator iterator = codeAttr.iterator();
  87. boolean edited = false;
  88. LoopContext context = new LoopContext(codeAttr.getMaxLocals());
  89. while (iterator.hasNext())
  90. if (loopBody(iterator, clazz, minfo, context))
  91. edited = true;
  92. ExceptionTable et = codeAttr.getExceptionTable();
  93. int n = et.size();
  94. for (int i = 0; i < n; ++i) {
  95. Handler h = new Handler(et, i, iterator, clazz, minfo);
  96. edit(h);
  97. if (h.edited()) {
  98. edited = true;
  99. context.updateMax(h.locals(), h.stack());
  100. }
  101. }
  102. // codeAttr might be modified by other partiess
  103. // so I check the current value of max-locals.
  104. if (codeAttr.getMaxLocals() < context.maxLocals)
  105. codeAttr.setMaxLocals(context.maxLocals);
  106. codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack);
  107. try {
  108. if (edited)
  109. minfo.rebuildStackMapIf6(clazz.getClassPool(),
  110. clazz.getClassFile2());
  111. }
  112. catch (BadBytecode b) {
  113. throw new CannotCompileException(b.getMessage(), b);
  114. }
  115. return edited;
  116. }
  117. /**
  118. * Visits each bytecode in the given range.
  119. */
  120. boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context,
  121. CodeIterator iterator, int endPos)
  122. throws CannotCompileException
  123. {
  124. boolean edited = false;
  125. while (iterator.hasNext() && iterator.lookAhead() < endPos) {
  126. int size = iterator.getCodeLength();
  127. if (loopBody(iterator, clazz, minfo, context)) {
  128. edited = true;
  129. int size2 = iterator.getCodeLength();
  130. if (size != size2) // the body was modified.
  131. endPos += size2 - size;
  132. }
  133. }
  134. return edited;
  135. }
  136. final static class NewOp {
  137. NewOp next;
  138. int pos;
  139. String type;
  140. NewOp(NewOp n, int p, String t) {
  141. next = n;
  142. pos = p;
  143. type = t;
  144. }
  145. }
  146. final static class LoopContext {
  147. NewOp newList;
  148. int maxLocals;
  149. int maxStack;
  150. LoopContext(int locals) {
  151. maxLocals = locals;
  152. maxStack = 0;
  153. newList = null;
  154. }
  155. void updateMax(int locals, int stack) {
  156. if (maxLocals < locals)
  157. maxLocals = locals;
  158. if (maxStack < stack)
  159. maxStack = stack;
  160. }
  161. }
  162. final boolean loopBody(CodeIterator iterator, CtClass clazz,
  163. MethodInfo minfo, LoopContext context)
  164. throws CannotCompileException
  165. {
  166. try {
  167. Expr expr = null;
  168. int pos = iterator.next();
  169. int c = iterator.byteAt(pos);
  170. if (c < Opcode.GETSTATIC) // c < 178
  171. /* skip */;
  172. else if (c < Opcode.NEWARRAY) { // c < 188
  173. if (c == Opcode.INVOKESTATIC
  174. || c == Opcode.INVOKEINTERFACE
  175. || c == Opcode.INVOKEVIRTUAL) {
  176. expr = new MethodCall(pos, iterator, clazz, minfo);
  177. edit((MethodCall)expr);
  178. }
  179. else if (c == Opcode.GETFIELD || c == Opcode.GETSTATIC
  180. || c == Opcode.PUTFIELD
  181. || c == Opcode.PUTSTATIC) {
  182. expr = new FieldAccess(pos, iterator, clazz, minfo, c);
  183. edit((FieldAccess)expr);
  184. }
  185. else if (c == Opcode.NEW) {
  186. int index = iterator.u16bitAt(pos + 1);
  187. context.newList = new NewOp(context.newList, pos,
  188. minfo.getConstPool().getClassInfo(index));
  189. }
  190. else if (c == Opcode.INVOKESPECIAL) {
  191. NewOp newList = context.newList;
  192. if (newList != null
  193. && minfo.getConstPool().isConstructor(newList.type,
  194. iterator.u16bitAt(pos + 1)) > 0) {
  195. expr = new NewExpr(pos, iterator, clazz, minfo,
  196. newList.type, newList.pos);
  197. edit((NewExpr)expr);
  198. context.newList = newList.next;
  199. }
  200. else {
  201. MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo);
  202. if (mcall.getMethodName().equals(MethodInfo.nameInit)) {
  203. ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo);
  204. expr = ccall;
  205. edit(ccall);
  206. }
  207. else {
  208. expr = mcall;
  209. edit(mcall);
  210. }
  211. }
  212. }
  213. }
  214. else { // c >= 188
  215. if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY
  216. || c == Opcode.MULTIANEWARRAY) {
  217. expr = new NewArray(pos, iterator, clazz, minfo, c);
  218. edit((NewArray)expr);
  219. }
  220. else if (c == Opcode.INSTANCEOF) {
  221. expr = new Instanceof(pos, iterator, clazz, minfo);
  222. edit((Instanceof)expr);
  223. }
  224. else if (c == Opcode.CHECKCAST) {
  225. expr = new Cast(pos, iterator, clazz, minfo);
  226. edit((Cast)expr);
  227. }
  228. }
  229. if (expr != null && expr.edited()) {
  230. context.updateMax(expr.locals(), expr.stack());
  231. return true;
  232. }
  233. return false;
  234. }
  235. catch (BadBytecode e) {
  236. throw new CannotCompileException(e);
  237. }
  238. }
  239. /**
  240. * Edits a <code>new</code> expression (overridable).
  241. * The default implementation performs nothing.
  242. *
  243. * @param e the <code>new</code> expression creating an object.
  244. */
  245. public void edit(NewExpr e) throws CannotCompileException {}
  246. /**
  247. * Edits an expression for array creation (overridable).
  248. * The default implementation performs nothing.
  249. *
  250. * @param a the <code>new</code> expression for creating an array.
  251. * @throws CannotCompileException
  252. */
  253. public void edit(NewArray a) throws CannotCompileException {}
  254. /**
  255. * Edits a method call (overridable).
  256. *
  257. * The default implementation performs nothing.
  258. */
  259. public void edit(MethodCall m) throws CannotCompileException {}
  260. /**
  261. * Edits a constructor call (overridable).
  262. * The constructor call is either
  263. * <code>super()</code> or <code>this()</code>
  264. * included in a constructor body.
  265. *
  266. * The default implementation performs nothing.
  267. *
  268. * @see #edit(NewExpr)
  269. */
  270. public void edit(ConstructorCall c) throws CannotCompileException {}
  271. /**
  272. * Edits a field-access expression (overridable).
  273. * Field access means both read and write.
  274. * The default implementation performs nothing.
  275. */
  276. public void edit(FieldAccess f) throws CannotCompileException {}
  277. /**
  278. * Edits an instanceof expression (overridable).
  279. * The default implementation performs nothing.
  280. */
  281. public void edit(Instanceof i) throws CannotCompileException {}
  282. /**
  283. * Edits an expression for explicit type casting (overridable).
  284. * The default implementation performs nothing.
  285. */
  286. public void edit(Cast c) throws CannotCompileException {}
  287. /**
  288. * Edits a catch clause (overridable).
  289. * The default implementation performs nothing.
  290. */
  291. public void edit(Handler h) throws CannotCompileException {}
  292. }