001: // Copyright (c) 1998, 2004 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: Label cases_label;
024: Type[] typeState;
025:
026: public int getMaxValue() {
027: return maxValue;
028: }
029:
030: public SwitchState(CodeAttr code) {
031: switch_label = new Label(code);
032: cases_label = new Label(code);
033:
034: code.popType(); // pop switch value
035:
036: // Save stack types (except top int) into typeState
037: typeState = code.saveStackTypeState(false);
038:
039: code.fixupChain(cases_label, switch_label);
040:
041: numCases = 0;
042: }
043:
044: /** Emit a new case, for the given value, whose label is here. */
045: public boolean addCase(int value, CodeAttr code) {
046: Label label = new Label(code);
047: boolean ok = addCase(value, label, code);
048: label.define(code);
049: code.restoreStackTypeState(typeState);
050: return ok;
051: }
052:
053: public void addDefault(CodeAttr code) {
054: Label label = new Label(code);
055: label.define(code);
056: addDefault(label, code);
057: }
058:
059: public void addDefault(Label label, CodeAttr code) {
060: defaultLabel = label;
061: code.restoreStackTypeState(typeState);
062: }
063:
064: /** Add a new case.
065: * @param value the case value to match against at run-time
066: * @param label the location to go to if the value matches
067: * @param code the CodeAttr of the Method we are generating code for
068: * @return true on success; false if value duplicates an existing value
069: */
070: public boolean addCase(int value, Label label, CodeAttr code) {
071: if (values == null) {
072: values = new int[10];
073: labels = new Label[10];
074: numCases = 1;
075: minValue = maxValue = value;
076: values[0] = value;
077: labels[0] = label;
078: return true;
079: }
080: int[] old_values = values;
081: Label[] old_labels = labels;
082: if (numCases >= values.length) {
083: values = new int[2 * numCases];
084: labels = new Label[2 * numCases];
085: }
086: int copyBefore;
087: if (value < minValue) {
088: copyBefore = 0;
089: minValue = value;
090: } else if (value > maxValue) {
091: copyBefore = numCases;
092: maxValue = value;
093: } else {
094: // Binary search.
095: int low = 0;
096: int hi = numCases - 1;
097: copyBefore = 0;
098: while (low <= hi) {
099: copyBefore = (low + hi) >> 1;
100: if (old_values[copyBefore] >= value)
101: hi = copyBefore - 1;
102: else
103: low = ++copyBefore;
104: }
105:
106: if (value == old_values[copyBefore])
107: return false;
108: }
109: int copyAfter = numCases - copyBefore;
110: System.arraycopy(old_values, copyBefore, values,
111: copyBefore + 1, copyAfter);
112: System.arraycopy(old_values, 0, values, 0, copyBefore);
113: values[copyBefore] = value;
114: System.arraycopy(old_labels, copyBefore, labels,
115: copyBefore + 1, copyAfter);
116: System.arraycopy(old_labels, 0, labels, 0, copyBefore);
117: labels[copyBefore] = label;
118: numCases++;
119: return true;
120: }
121:
122: /** Handle the end of the switch statement.
123: * Assume the case value is on the stack; go to the matching case label. */
124: public void finish(CodeAttr code) {
125: if (defaultLabel == null) {
126: defaultLabel = new Label(code);
127: defaultLabel.define(code);
128: ClassType ex = ClassType.make("java.lang.RuntimeException");
129: code.emitNew(ex);
130: code.emitDup(ex);
131: code.emitPushString("bad case value!");
132: Type[] args = { Type.string_type };
133: Method con = ex.addMethod("<init>", Access.PUBLIC, args,
134: Type.void_type);
135: code.emitInvokeSpecial(con);
136: code.emitThrow();
137: }
138: Label after_label = new Label(code);
139: code.fixupChain(switch_label, after_label);
140: if (numCases <= 1) {
141: code.pushType(Type.int_type);
142: if (numCases == 1) {
143: code.emitPushInt(minValue);
144: code.emitGotoIfEq(labels[0]);
145: } else {
146: code.emitPop(1);
147: }
148: code.emitGoto(defaultLabel);
149: } else if (2 * numCases >= maxValue - minValue) {
150: code.reserve(13 + 4 * (maxValue - minValue + 1));
151: code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
152: code.put1(170); // tableswitch
153: code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
154: code.PC += 4;
155: code.put4(minValue);
156: code.put4(maxValue);
157: int index = 0;
158: for (int i = minValue; i <= maxValue; i++) {
159: Label lab = values[index] == i ? labels[index++]
160: : defaultLabel;
161: code.fixupAdd(CodeAttr.FIXUP_CASE, lab);
162: code.PC += 4;
163: }
164: } else {
165: code.reserve(9 + 8 * numCases);
166: code.fixupAdd(CodeAttr.FIXUP_SWITCH, null);
167: code.put1(171); // lookupswitch
168: code.fixupAdd(CodeAttr.FIXUP_CASE, defaultLabel);
169: code.PC += 4;
170: code.put4(numCases);
171: for (int index = 0; index < numCases; index++) {
172: code.put4(values[index]);
173: code.fixupAdd(CodeAttr.FIXUP_CASE, labels[index]);
174: code.PC += 4;
175: }
176: }
177: code.fixupChain(after_label, cases_label);
178: }
179: }
|