List<CompilerCallback> bodies,
CompilerCallback fallback) {
Map<CompilerCallback, Label> bodyLabels = new HashMap<CompilerCallback, Label>();
Label defaultCase = new Label();
Label slowPath = new Label();
CompilerCallback getCaseValue = null;
final int tmp = getVariableCompiler().grabTempLocal();
if (inputValue != null) {
// we have an input case, prepare branching logic
inputValue.call(this);
getVariableCompiler().setTempLocal(tmp);
getCaseValue = new CompilerCallback() {
public void call(BodyCompiler context) {
getVariableCompiler().getTempLocal(tmp);
}
};
if (switchCases != null) {
// we have optimized switch cases, build a lookupswitch
SortedMap<Integer, Label> optimizedLabels = new TreeMap<Integer, Label>();
for (Map.Entry<CompilerCallback, int[]> entry : switchCases.entrySet()) {
Label lbl = new Label();
bodyLabels.put(entry.getKey(), lbl);
for (int i : entry.getValue()) {
optimizedLabels.put(i, lbl);
}
}
int[] caseValues = new int[optimizedLabels.size()];
Label[] caseLabels = new Label[optimizedLabels.size()];
Set<Map.Entry<Integer, Label>> entrySet = optimizedLabels.entrySet();
Iterator<Map.Entry<Integer, Label>> iterator = entrySet.iterator();
for (int i = 0; i < entrySet.size(); i++) {
Map.Entry<Integer, Label> entry = iterator.next();
caseValues[i] = entry.getKey();
caseLabels[i] = entry.getValue();
}
// checkcast the value; if match, fast path; otherwise proceed to slow logic
getCaseValue.call(this);
method.instance_of(p(fastSwitchType.getAssociatedClass()));
method.ifeq(slowPath);
switch (fastSwitchType) {
case FIXNUM:
getCaseValue.call(this);
method.checkcast(p(RubyFixnum.class));
method.invokevirtual(p(RubyFixnum.class), "getLongValue", sig(long.class));
method.l2i();
break;
case SINGLE_CHAR_STRING:
getCaseValue.call(this);
invokeUtilityMethod("isFastSwitchableSingleCharString", sig(boolean.class, IRubyObject.class));
method.ifeq(slowPath);
getCaseValue.call(this);
invokeUtilityMethod("getFastSwitchSingleCharString", sig(int.class, IRubyObject.class));
break;
case STRING:
getCaseValue.call(this);
invokeUtilityMethod("isFastSwitchableString", sig(boolean.class, IRubyObject.class));
method.ifeq(slowPath);
getCaseValue.call(this);
invokeUtilityMethod("getFastSwitchString", sig(int.class, IRubyObject.class));
break;
case SINGLE_CHAR_SYMBOL:
getCaseValue.call(this);
invokeUtilityMethod("isFastSwitchableSingleCharSymbol", sig(boolean.class, IRubyObject.class));
method.ifeq(slowPath);
getCaseValue.call(this);
invokeUtilityMethod("getFastSwitchSingleCharSymbol", sig(int.class, IRubyObject.class));
break;
case SYMBOL:
getCaseValue.call(this);
invokeUtilityMethod("isFastSwitchableSymbol", sig(boolean.class, IRubyObject.class));
method.ifeq(slowPath);
getCaseValue.call(this);
invokeUtilityMethod("getFastSwitchSymbol", sig(int.class, IRubyObject.class));
break;
}
method.lookupswitch(defaultCase, caseValues, caseLabels);
}
}
Label done = new Label();
// expression-based tests + bodies
Label currentLabel = slowPath;
for (int i = 0; i < conditionals.size(); i++) {
ArgumentsCallback conditional = conditionals.get(i);
CompilerCallback body = bodies.get(i);
method.label(currentLabel);
getInvocationCompiler().invokeEqq(conditional, getCaseValue);
if (i + 1 < conditionals.size()) {
// normal case, create a new label
currentLabel = new Label();
} else {
// last conditional case, use defaultCase
currentLabel = defaultCase;
}
method.ifeq(currentLabel);
Label bodyLabel = bodyLabels.get(body);
if (bodyLabel != null) method.label(bodyLabel);
body.call(this);
method.go_to(done);
}
// "else" body