123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- // Copyright (c) 1998, 2004, 2008 Per M.A. Bothner.
- // This is free software; for terms and warranty disclaimer see ./COPYING.
- package gnu.bytecode;
- /** Maintains the state for generating a switch statement or expression.
- *
- * <h3>Simple example</h3>
- * <p>To translate:
- * <blockquote><pre>
- * switch (exps) {
- * case 1: exp1; break;
- * case 2: exp2; break;
- * default: expd;
- * } </pre></blockquote>
- * you can do:
- * <blockquote><pre>
- * compile[exps]
- * SwitchState sw = code.startSwitch();
- * sw.addCase(1, code);
- * compile[exp1];
- * sw.exitSwitch(code);
- * sw.addCase(2, code);
- * compile[exp2];
- * sw.exitSwitch(code);
- * sw.addDefault(code);
- * compile[expd];
- * sw.finish(code);
- * </pre></blockquote>
- */
- public class SwitchState {
- /** The smallest case value, so far. */
- int minValue;
- /** The largest case value, so far. */
- int maxValue;
- /** The number of cases (not including the default case). */
- int numCases;
- /** The case values, in numerical order (in values[0..numCases-1]). */
- int[] values;
- /** The case locations, in the same order as values. */
- Label[] labels;
- /** The location to jump to if none of the cases match. */
- Label defaultLabel;
- /** Location of the actual switch instruction. */
- Label switch_label;
- /** Start of the "cases".
- * This is used to store the type-state for each case. */
- Label cases_label;
- /** Code following the switch. */
- Label after_label;
- TryState outerTry;
- public int getMaxValue() { return maxValue; }
- public int getNumCases() { return numCases; }
- public SwitchState(CodeAttr code) {
- switch_label = new Label(code);
- cases_label = new Label(code);
- after_label = new Label(code);
- defaultLabel = new Label(code);
- outerTry = code.try_stack;
- numCases = 0;
- }
- /** Needs to be called after the switch value has been pushed.
- * I.e. in execution order, just before the actual switch instruction.
- * Called implicitly by {@link CodeAttr#startSwitch}.
- */
- public void switchValuePushed(CodeAttr code) {
- Type top = code.popType(); // pop switch value
- cases_label.setTypes(code);
- code.pushType(top);
- switch_label.setTypes(code);
- code.fixupAdd(CodeAttr.FIXUP_MOVE, -1, switch_label);
- code.setUnreachable();
- cases_label.define(code);
- }
- /** Emit a new case, for the given value, whose label is here. */
- /** Add a new case.
- * @param value the case value to match against at run-time
- * @param code the CodeAttr of the Method we are generating code for
- * @return true on success; false if value duplicates an existing value
- */
- public boolean addCase(int value, CodeAttr code){
- Label label = new Label(code);
- label.setTypes(cases_label);
- label.define(code);
- return insertCase(value, label, code);
- }
- /** Optimization of {@code addCase(value, code); emitGoto(label)}. */
- public boolean addCaseGoto(int value, CodeAttr code, Label label) {
- boolean ok = insertCase(value, label, code);
- label.setTypes(cases_label);
- code.setUnreachable();
- return ok;
- }
- public void addDefault(CodeAttr code) {
- if (defaultLabel.defined()) throw new Error();
- if (outerTry != code.try_stack)
- defaultLabel.setTypes(code);
- defaultLabel.setTypes(cases_label);
- defaultLabel.define(code);
- }
- /** Internal routine to add a new case.
- * @param value the case value to match against at run-time
- * @param label the location to go to if the value matches
- * @param code the CodeAttr of the Method we are generating code for
- * @return true on success; false if value duplicates an existing value
- */
- public boolean insertCase(int value, Label label, CodeAttr code) {
- if (values == null) {
- values = new int[10];
- labels = new Label[10];
- numCases = 1;
- minValue = maxValue = value;
- values[0] = value;
- labels[0] = label;
- return true;
- }
- int[] old_values = values;
- Label[] old_labels = labels;
- int copyBefore;
- if (value < minValue) {
- copyBefore = 0;
- minValue = value;
- } else if (value > maxValue) {
- copyBefore = numCases;
- maxValue = value;
- } else {
- // Binary search.
- int low = 0;
- int hi = numCases - 1;
- copyBefore = 0;
- while (low <= hi) {
- copyBefore = (low + hi) >>> 1;
- if (old_values[copyBefore] >= value)
- hi = copyBefore - 1;
- else
- low = ++ copyBefore;
- }
- if (value == old_values[copyBefore])
- return false;
- }
- if (numCases >= values.length) {
- values = new int[2 * numCases];
- labels = new Label[2 * numCases];
- }
- int copyAfter = numCases - copyBefore;
- System.arraycopy(old_values, copyBefore, values, copyBefore+1, copyAfter);
- System.arraycopy(old_values, 0, values, 0, copyBefore);
- values[copyBefore] = value;
- System.arraycopy(old_labels, copyBefore, labels, copyBefore+1, copyAfter);
- System.arraycopy(old_labels, 0, labels, 0, copyBefore);
- labels[copyBefore] = label;
- numCases++;
- return true;
- }
- /** Break/exit from this switch.
- * Doesn't allow exiting through a try - if you need that,
- * use an {@link ExitableBlock}.
- */
- public void exitSwitch(CodeAttr code) {
- if (code.reachableHere()) {
- if (outerTry != code.try_stack)
- throw new Error("exitSwitch cannot exit through a try");
- code.emitGoto(after_label);
- }
- }
-
- /** Handle the end of the switch statement.
- * Assume the case value is on the stack; go to the matching case label. */
- public void finish (CodeAttr code) {
- if (code.reachableHere())
- exitSwitch(code);
- if (!defaultLabel.defined()) {
- defaultLabel.define(code);
- ClassType ex = ClassType.make("java.lang.RuntimeException");
- code.emitNew(ex);
- code.emitDup(ex);
- code.emitPushString("bad case value!");
- Type[] args = { Type.string_type };
- Method con = ex.addMethod("<init>", Access.PUBLIC,
- args, Type.voidType);
- code.emitInvokeSpecial(con);
- code.emitThrow();
- }
- code.fixupAdd(CodeAttr.FIXUP_MOVE, -1, after_label);
- switch_label.define(code);
- if (numCases <= 1) {
- if (numCases == 1) {
- if (minValue == 0)
- code.emitIfIntEqZero();
- else {
- code.emitPushInt(minValue);
- code.emitIfEq();
- }
- code.emitGoto(labels[0]);
- code.emitElse();
- code.emitGoto(defaultLabel);
- code.emitFi();
- } else {
- code.emitPop(1);
- code.emitGoto(defaultLabel);
- }
- } else {
- long rangeDim = (long)maxValue - (long)minValue;
- if (2 * numCases >= rangeDim) {
- int size = (int) (13 + 4 * (rangeDim + 1));
- code.reserve(size);
- code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
- code.put1(170); // tableswitch
- code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
- code.PC += 4;
- code.put4(minValue);
- code.put4(maxValue);
- int index = 0;
- // convoluted code in case maxValue==Integer.MAX_VALUE
- for (int i = minValue; ; i++) {
- Label lab = values[index] == i ? labels[index++] : defaultLabel;
- code.fixupAdd(CodeAttr.FIXUP_CASE, lab);
- code.PC += 4;
- if (i == maxValue) break;
- }
- } else {
- code.reserve(9 + 8 * numCases);
- code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
- code.put1(171); // lookupswitch
- code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
- code.PC += 4; // make room for defaultLabel
- code.put4(numCases);
- for (int index = 0; index < numCases; index++) {
- code.put4(values[index]);
- code.fixupAdd(CodeAttr.FIXUP_CASE, labels[index]);
- code.PC += 4;
- }
- }
- }
- code.fixupAdd(CodeAttr.FIXUP_MOVE, cases_label);
- code.setUnreachable();
- if (after_label.isUsed())
- after_label.define(code);
- else
- after_label.defineRaw(code);
- }
- }
|