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.

CodeAnalyzer.java 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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.bytecode;
  17. /**
  18. * Utility for computing <code>max_stack</code>.
  19. */
  20. class CodeAnalyzer implements Opcode {
  21. private ConstPool constPool;
  22. private CodeAttribute codeAttr;
  23. public CodeAnalyzer(CodeAttribute ca) {
  24. codeAttr = ca;
  25. constPool = ca.getConstPool();
  26. }
  27. public int computeMaxStack()
  28. throws BadBytecode
  29. {
  30. /* d = stack[i]
  31. * d == 0: not visited
  32. * d > 0: the depth is d - 1 after executing the bytecode at i.
  33. * d < 0: not visited. the initial depth (before execution) is 1 - d.
  34. */
  35. CodeIterator ci = codeAttr.iterator();
  36. int length = ci.getCodeLength();
  37. int[] stack = new int[length];
  38. constPool = codeAttr.getConstPool();
  39. initStack(stack, codeAttr);
  40. boolean repeat;
  41. do {
  42. repeat = false;
  43. for (int i = 0; i < length; ++i)
  44. if (stack[i] < 0) {
  45. repeat = true;
  46. visitBytecode(ci, stack, i);
  47. }
  48. } while (repeat);
  49. int maxStack = 1;
  50. for (int i = 0; i < length; ++i)
  51. if (stack[i] > maxStack)
  52. maxStack = stack[i];
  53. return maxStack - 1; // the base is 1.
  54. }
  55. private void initStack(int[] stack, CodeAttribute ca) {
  56. stack[0] = -1;
  57. ExceptionTable et = ca.getExceptionTable();
  58. if (et != null) {
  59. int size = et.size();
  60. for (int i = 0; i < size; ++i)
  61. stack[et.handlerPc(i)] = -2; // an exception is on stack
  62. }
  63. }
  64. private void visitBytecode(CodeIterator ci, int[] stack, int index)
  65. throws BadBytecode
  66. {
  67. int codeLength = stack.length;
  68. ci.move(index);
  69. int stackDepth = -stack[index];
  70. int[] jsrDepth = new int[1];
  71. jsrDepth[0] = -1;
  72. while (ci.hasNext()) {
  73. index = ci.next();
  74. stack[index] = stackDepth;
  75. int op = ci.byteAt(index);
  76. stackDepth = visitInst(op, ci, index, stackDepth);
  77. if (stackDepth < 1)
  78. throw new BadBytecode("stack underflow at " + index);
  79. if (processBranch(op, ci, index, codeLength, stack, stackDepth, jsrDepth))
  80. break;
  81. if (isEnd(op)) // return, ireturn, athrow, ...
  82. break;
  83. if (op == JSR || op == JSR_W)
  84. --stackDepth;
  85. }
  86. }
  87. private boolean processBranch(int opcode, CodeIterator ci, int index,
  88. int codeLength, int[] stack, int stackDepth, int[] jsrDepth)
  89. throws BadBytecode
  90. {
  91. if ((IFEQ <= opcode && opcode <= IF_ACMPNE)
  92. || opcode == IFNULL || opcode == IFNONNULL) {
  93. int target = index + ci.s16bitAt(index + 1);
  94. checkTarget(index, target, codeLength, stack, stackDepth);
  95. }
  96. else {
  97. int target, index2;
  98. switch (opcode) {
  99. case GOTO :
  100. target = index + ci.s16bitAt(index + 1);
  101. checkTarget(index, target, codeLength, stack, stackDepth);
  102. return true;
  103. case GOTO_W :
  104. target = index + ci.s32bitAt(index + 1);
  105. checkTarget(index, target, codeLength, stack, stackDepth);
  106. return true;
  107. case JSR :
  108. case JSR_W :
  109. if (opcode == JSR)
  110. target = index + ci.s16bitAt(index + 1);
  111. else
  112. target = index + ci.s32bitAt(index + 1);
  113. checkTarget(index, target, codeLength, stack, stackDepth);
  114. /*
  115. * It is unknown which RET comes back to this JSR.
  116. * So we assume that if the stack depth at one JSR instruction
  117. * is N, then it is also N at other JSRs and N - 1 at all RET
  118. * instructions. Note that STACK_GROW[JSR] is 1 since it pushes
  119. * a return address on the operand stack.
  120. */
  121. if (jsrDepth[0] < 0) {
  122. jsrDepth[0] = stackDepth;
  123. return false;
  124. }
  125. else if (stackDepth == jsrDepth[0])
  126. return false;
  127. else
  128. throw new BadBytecode(
  129. "sorry, cannot compute this data flow due to JSR: "
  130. + stackDepth + "," + jsrDepth[0]);
  131. case RET :
  132. if (jsrDepth[0] < 0) {
  133. jsrDepth[0] = stackDepth + 1;
  134. return false;
  135. }
  136. else if (stackDepth + 1 == jsrDepth[0])
  137. return true;
  138. else
  139. throw new BadBytecode(
  140. "sorry, cannot compute this data flow due to RET: "
  141. + stackDepth + "," + jsrDepth[0]);
  142. case LOOKUPSWITCH :
  143. case TABLESWITCH :
  144. index2 = (index & ~3) + 4;
  145. target = index + ci.s32bitAt(index2);
  146. checkTarget(index, target, codeLength, stack, stackDepth);
  147. if (opcode == LOOKUPSWITCH) {
  148. int npairs = ci.s32bitAt(index2 + 4);
  149. index2 += 12;
  150. for (int i = 0; i < npairs; ++i) {
  151. target = index + ci.s32bitAt(index2);
  152. checkTarget(index, target, codeLength,
  153. stack, stackDepth);
  154. index2 += 8;
  155. }
  156. }
  157. else {
  158. int low = ci.s32bitAt(index2 + 4);
  159. int high = ci.s32bitAt(index2 + 8);
  160. int n = high - low + 1;
  161. index2 += 12;
  162. for (int i = 0; i < n; ++i) {
  163. target = index + ci.s32bitAt(index2);
  164. checkTarget(index, target, codeLength,
  165. stack, stackDepth);
  166. index2 += 4;
  167. }
  168. }
  169. return true; // always branch.
  170. }
  171. }
  172. return false; // may not branch.
  173. }
  174. private void checkTarget(int opIndex, int target, int codeLength,
  175. int[] stack, int stackDepth)
  176. throws BadBytecode
  177. {
  178. if (target < 0 || codeLength <= target)
  179. throw new BadBytecode("bad branch offset at " + opIndex);
  180. int d = stack[target];
  181. if (d == 0)
  182. stack[target] = -stackDepth;
  183. else if (d != stackDepth && d != -stackDepth)
  184. throw new BadBytecode("verification error (" + stackDepth +
  185. "," + d + ") at " + opIndex);
  186. }
  187. private static boolean isEnd(int opcode) {
  188. return (IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW;
  189. }
  190. /**
  191. * Visits an instruction.
  192. */
  193. private int visitInst(int op, CodeIterator ci, int index, int stack)
  194. throws BadBytecode
  195. {
  196. String desc;
  197. switch (op) {
  198. case GETFIELD :
  199. stack += getFieldSize(ci, index) - 1;
  200. break;
  201. case PUTFIELD :
  202. stack -= getFieldSize(ci, index) + 1;
  203. break;
  204. case GETSTATIC :
  205. stack += getFieldSize(ci, index);
  206. break;
  207. case PUTSTATIC :
  208. stack -= getFieldSize(ci, index);
  209. break;
  210. case INVOKEVIRTUAL :
  211. case INVOKESPECIAL :
  212. desc = constPool.getMethodrefType(ci.u16bitAt(index + 1));
  213. stack += Descriptor.dataSize(desc) - 1;
  214. break;
  215. case INVOKESTATIC :
  216. desc = constPool.getMethodrefType(ci.u16bitAt(index + 1));
  217. stack += Descriptor.dataSize(desc);
  218. break;
  219. case INVOKEINTERFACE :
  220. desc = constPool.getInterfaceMethodrefType(
  221. ci.u16bitAt(index + 1));
  222. stack += Descriptor.dataSize(desc) - 1;
  223. break;
  224. case ATHROW :
  225. stack = 1; // the stack becomes empty (1 means no values).
  226. break;
  227. case MULTIANEWARRAY :
  228. stack += 1 - ci.byteAt(index + 3);
  229. break;
  230. case WIDE :
  231. op = ci.byteAt(index + 1);
  232. // don't break here.
  233. default :
  234. stack += STACK_GROW[op];
  235. }
  236. return stack;
  237. }
  238. private int getFieldSize(CodeIterator ci, int index) {
  239. String desc = constPool.getFieldrefType(ci.u16bitAt(index + 1));
  240. return Descriptor.dataSize(desc);
  241. }
  242. }