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

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