SwitchState.java 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) 1998, 2004, 2008 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.bytecode;
  4. /** Maintains the state for generating a switch statement or expression.
  5. *
  6. * <h3>Simple example</h3>
  7. * <p>To translate:
  8. * <blockquote><pre>
  9. * switch (exps) {
  10. * case 1: exp1; break;
  11. * case 2: exp2; break;
  12. * default: expd;
  13. * } </pre></blockquote>
  14. * you can do:
  15. * <blockquote><pre>
  16. * compile[exps]
  17. * SwitchState sw = code.startSwitch();
  18. * sw.addCase(1, code);
  19. * compile[exp1];
  20. * sw.exitSwitch(code);
  21. * sw.addCase(2, code);
  22. * compile[exp2];
  23. * sw.exitSwitch(code);
  24. * sw.addDefault(code);
  25. * compile[expd];
  26. * sw.finish(code);
  27. * </pre></blockquote>
  28. */
  29. public class SwitchState {
  30. /** The smallest case value, so far. */
  31. int minValue;
  32. /** The largest case value, so far. */
  33. int maxValue;
  34. /** The number of cases (not including the default case). */
  35. int numCases;
  36. /** The case values, in numerical order (in values[0..numCases-1]). */
  37. int[] values;
  38. /** The case locations, in the same order as values. */
  39. Label[] labels;
  40. /** The location to jump to if none of the cases match. */
  41. Label defaultLabel;
  42. /** Location of the actual switch instruction. */
  43. Label switch_label;
  44. /** Start of the "cases".
  45. * This is used to store the type-state for each case. */
  46. Label cases_label;
  47. /** Code following the switch. */
  48. Label after_label;
  49. TryState outerTry;
  50. public int getMaxValue() { return maxValue; }
  51. public int getNumCases() { return numCases; }
  52. public SwitchState(CodeAttr code) {
  53. switch_label = new Label(code);
  54. cases_label = new Label(code);
  55. after_label = new Label(code);
  56. defaultLabel = new Label(code);
  57. outerTry = code.try_stack;
  58. numCases = 0;
  59. }
  60. /** Needs to be called after the switch value has been pushed.
  61. * I.e. in execution order, just before the actual switch instruction.
  62. * Called implicitly by {@link CodeAttr#startSwitch}.
  63. */
  64. public void switchValuePushed(CodeAttr code) {
  65. Type top = code.popType(); // pop switch value
  66. cases_label.setTypes(code);
  67. code.pushType(top);
  68. switch_label.setTypes(code);
  69. code.fixupAdd(CodeAttr.FIXUP_MOVE, -1, switch_label);
  70. code.setUnreachable();
  71. cases_label.define(code);
  72. }
  73. /** Emit a new case, for the given value, whose label is here. */
  74. /** Add a new case.
  75. * @param value the case value to match against at run-time
  76. * @param code the CodeAttr of the Method we are generating code for
  77. * @return true on success; false if value duplicates an existing value
  78. */
  79. public boolean addCase(int value, CodeAttr code){
  80. Label label = new Label(code);
  81. label.setTypes(cases_label);
  82. label.define(code);
  83. return insertCase(value, label, code);
  84. }
  85. /** Optimization of {@code addCase(value, code); emitGoto(label)}. */
  86. public boolean addCaseGoto(int value, CodeAttr code, Label label) {
  87. boolean ok = insertCase(value, label, code);
  88. label.setTypes(cases_label);
  89. code.setUnreachable();
  90. return ok;
  91. }
  92. public void addDefault(CodeAttr code) {
  93. if (defaultLabel.defined()) throw new Error();
  94. if (outerTry != code.try_stack)
  95. defaultLabel.setTypes(code);
  96. defaultLabel.setTypes(cases_label);
  97. defaultLabel.define(code);
  98. }
  99. /** Internal routine to add a new case.
  100. * @param value the case value to match against at run-time
  101. * @param label the location to go to if the value matches
  102. * @param code the CodeAttr of the Method we are generating code for
  103. * @return true on success; false if value duplicates an existing value
  104. */
  105. public boolean insertCase(int value, Label label, CodeAttr code) {
  106. if (values == null) {
  107. values = new int[10];
  108. labels = new Label[10];
  109. numCases = 1;
  110. minValue = maxValue = value;
  111. values[0] = value;
  112. labels[0] = label;
  113. return true;
  114. }
  115. int[] old_values = values;
  116. Label[] old_labels = labels;
  117. int copyBefore;
  118. if (value < minValue) {
  119. copyBefore = 0;
  120. minValue = value;
  121. } else if (value > maxValue) {
  122. copyBefore = numCases;
  123. maxValue = value;
  124. } else {
  125. // Binary search.
  126. int low = 0;
  127. int hi = numCases - 1;
  128. copyBefore = 0;
  129. while (low <= hi) {
  130. copyBefore = (low + hi) >>> 1;
  131. if (old_values[copyBefore] >= value)
  132. hi = copyBefore - 1;
  133. else
  134. low = ++ copyBefore;
  135. }
  136. if (value == old_values[copyBefore])
  137. return false;
  138. }
  139. if (numCases >= values.length) {
  140. values = new int[2 * numCases];
  141. labels = new Label[2 * numCases];
  142. }
  143. int copyAfter = numCases - copyBefore;
  144. System.arraycopy(old_values, copyBefore, values, copyBefore+1, copyAfter);
  145. System.arraycopy(old_values, 0, values, 0, copyBefore);
  146. values[copyBefore] = value;
  147. System.arraycopy(old_labels, copyBefore, labels, copyBefore+1, copyAfter);
  148. System.arraycopy(old_labels, 0, labels, 0, copyBefore);
  149. labels[copyBefore] = label;
  150. numCases++;
  151. return true;
  152. }
  153. /** Break/exit from this switch.
  154. * Doesn't allow exiting through a try - if you need that,
  155. * use an {@link ExitableBlock}.
  156. */
  157. public void exitSwitch(CodeAttr code) {
  158. if (code.reachableHere()) {
  159. if (outerTry != code.try_stack)
  160. throw new Error("exitSwitch cannot exit through a try");
  161. code.emitGoto(after_label);
  162. }
  163. }
  164. /** Handle the end of the switch statement.
  165. * Assume the case value is on the stack; go to the matching case label. */
  166. public void finish (CodeAttr code) {
  167. if (code.reachableHere())
  168. exitSwitch(code);
  169. if (!defaultLabel.defined()) {
  170. defaultLabel.define(code);
  171. ClassType ex = ClassType.make("java.lang.RuntimeException");
  172. code.emitNew(ex);
  173. code.emitDup(ex);
  174. code.emitPushString("bad case value!");
  175. Type[] args = { Type.string_type };
  176. Method con = ex.addMethod("<init>", Access.PUBLIC,
  177. args, Type.voidType);
  178. code.emitInvokeSpecial(con);
  179. code.emitThrow();
  180. }
  181. code.fixupAdd(CodeAttr.FIXUP_MOVE, -1, after_label);
  182. switch_label.define(code);
  183. if (numCases <= 1) {
  184. if (numCases == 1) {
  185. if (minValue == 0)
  186. code.emitIfIntEqZero();
  187. else {
  188. code.emitPushInt(minValue);
  189. code.emitIfEq();
  190. }
  191. code.emitGoto(labels[0]);
  192. code.emitElse();
  193. code.emitGoto(defaultLabel);
  194. code.emitFi();
  195. } else {
  196. code.emitPop(1);
  197. code.emitGoto(defaultLabel);
  198. }
  199. } else {
  200. long rangeDim = (long)maxValue - (long)minValue;
  201. if (2 * numCases >= rangeDim) {
  202. int size = (int) (13 + 4 * (rangeDim + 1));
  203. code.reserve(size);
  204. code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
  205. code.put1(170); // tableswitch
  206. code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
  207. code.PC += 4;
  208. code.put4(minValue);
  209. code.put4(maxValue);
  210. int index = 0;
  211. // convoluted code in case maxValue==Integer.MAX_VALUE
  212. for (int i = minValue; ; i++) {
  213. Label lab = values[index] == i ? labels[index++] : defaultLabel;
  214. code.fixupAdd(CodeAttr.FIXUP_CASE, lab);
  215. code.PC += 4;
  216. if (i == maxValue) break;
  217. }
  218. } else {
  219. code.reserve(9 + 8 * numCases);
  220. code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
  221. code.put1(171); // lookupswitch
  222. code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
  223. code.PC += 4; // make room for defaultLabel
  224. code.put4(numCases);
  225. for (int index = 0; index < numCases; index++) {
  226. code.put4(values[index]);
  227. code.fixupAdd(CodeAttr.FIXUP_CASE, labels[index]);
  228. code.PC += 4;
  229. }
  230. }
  231. }
  232. code.fixupAdd(CodeAttr.FIXUP_MOVE, cases_label);
  233. code.setUnreachable();
  234. if (after_label.isUsed())
  235. after_label.define(code);
  236. else
  237. after_label.defineRaw(code);
  238. }
  239. }