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 8.4KB

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