001: // Copyright (c) 1998 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.bytecode;
005:
006: /** Maintains the state for generating a switch statement. */
007:
008: public class SwitchState {
009: /** The smallest case value, so far. */
010: int minValue;
011: /** The largest case value, so far. */
012: int maxValue;
013: /** The number of cases (not including the default case). */
014: int numCases;
015: /** The case values, in numerical order (in values[0..numCases-1]). */
016: int[] values;
017: /** The case locations, in the same order as values. */
018: Label[] labels;
019: /** The location to jump to if none of the cases match. */
020: Label defaultLabel;
021: /* Location of the actual switch instruction. */
022: Label switch_label;
023: Type[] typeState;
024:
025: public int getMaxValue() {
026: return maxValue;
027: }
028:
029: public SwitchState(CodeAttr code) {
030: switch_label = new Label(code);
031:
032: code.popType(); // pop switch value
033:
034: // Save stack types (except top int) into typeState
035: typeState = code.saveStackTypeState(false);
036:
037: code.emitGoto(switch_label);
038:
039: numCases = 0;
040: }
041:
042: /** Emit a new case, for the given value, whose label is here. */
043: public boolean addCase(int value, CodeAttr code) {
044: Label label = new Label(code);
045: boolean ok = addCase(value, label, code);
046: label.define(code);
047: code.restoreStackTypeState(typeState);
048: return ok;
049: }
050:
051: public void addDefault(CodeAttr code) {
052: Label label = new Label(code);
053: label.define(code);
054: addDefault(label, code);
055: }
056:
057: public void addDefault(Label label, CodeAttr code) {
058: defaultLabel = label;
059: code.restoreStackTypeState(typeState);
060: }
061:
062: /** Add a new case.
063: * @param value the case value to match against at run-time
064: * @param label the location to go to if the value matches
065: * @param code the CodeAttr of the Method we are generating code for
066: * @return true on success; false if value duplicates an existing value
067: */
068: public boolean addCase(int value, Label label, CodeAttr code) {
069: if (values == null) {
070: values = new int[10];
071: labels = new Label[10];
072: numCases = 1;
073: minValue = maxValue = value;
074: values[0] = value;
075: labels[0] = label;
076: return true;
077: }
078: int[] old_values = values;
079: Label[] old_labels = labels;
080: if (numCases >= values.length) {
081: values = new int[2 * numCases];
082: labels = new Label[2 * numCases];
083: }
084: int copyBefore;
085: if (value < minValue) {
086: copyBefore = 0;
087: minValue = value;
088: } else if (value > maxValue) {
089: copyBefore = numCases;
090: maxValue = value;
091: } else {
092: // Binary search.
093: int low = 0;
094: int hi = numCases - 1;
095: copyBefore = 0;
096: while (low <= hi) {
097: copyBefore = (low + hi) >> 1;
098: if (old_values[copyBefore] >= value)
099: hi = copyBefore - 1;
100: else
101: low = ++copyBefore;
102: }
103:
104: if (value == old_values[copyBefore])
105: return false;
106: }
107: int copyAfter = numCases - copyBefore;
108: System.arraycopy(old_values, copyBefore, values,
109: copyBefore + 1, copyAfter);
110: System.arraycopy(old_values, 0, values, 0, copyBefore);
111: values[copyBefore] = value;
112: System.arraycopy(old_labels, copyBefore, labels,
113: copyBefore + 1, copyAfter);
114: System.arraycopy(old_labels, 0, labels, 0, copyBefore);
115: labels[copyBefore] = label;
116: numCases++;
117: return true;
118: }
119:
120: /** Handle the end of the switch statement.
121: * Assume the case value is on the stack; go to the matching case label. */
122: public void finish(CodeAttr code) {
123: if (defaultLabel == null) {
124: defaultLabel = new Label(code);
125: defaultLabel.define(code);
126: ClassType ex = ClassType.make("java.lang.RuntimeException");
127: code.emitNew(ex);
128: code.emitDup(ex);
129: code.emitPushString("bad case value!");
130: Type[] args = { Type.string_type };
131: Method con = ex.addMethod("<init>", Access.PUBLIC, args,
132: Type.void_type);
133: code.emitInvokeSpecial(con);
134: code.emitThrow();
135: }
136: switch_label.define(code);
137: if (numCases <= 1) {
138: code.pushType(Type.int_type);
139: if (numCases == 1) {
140: code.emitPushInt(minValue);
141: code.emitGotoIfEq(labels[0]);
142: } else {
143: code.emitPop(1);
144: }
145: code.emitGoto(defaultLabel);
146: return;
147: }
148: int start = code.PC;
149: int pad = (3 - start) & 3;
150: if (2 * numCases >= maxValue - minValue) {
151: code.reserve(13 + pad + 4 * (maxValue - minValue + 1));
152: code.put1(170); // tableswitch
153: while (--pad >= 0)
154: code.put1(0);
155: defaultLabel.emit_wide(code, start);
156: code.put4(minValue);
157: code.put4(maxValue);
158: int index = 0;
159: for (int i = minValue; i <= maxValue; i++) {
160: Label lab = values[index] == i ? labels[index++]
161: : defaultLabel;
162: lab.emit_wide(code, start);
163: }
164: } else {
165: code.reserve(9 + pad + 8 * numCases);
166: code.put1(171); // lookupswitch
167: while (--pad >= 0)
168: code.put1(0);
169: defaultLabel.emit_wide(code, start);
170: code.put4(numCases);
171: for (int index = 0; index < numCases; index++) {
172: code.put4(values[index]);
173: labels[index].emit_wide(code, start);
174: }
175: }
176: }
177: }
|