return callResult;
}
public Operand buildCase(CaseNode caseNode, IRScope s) {
// get the incoming case value
Operand value = build(caseNode.getCaseNode(), s);
// This is for handling case statements without a value (see example below)
// case
// when true <blah>
// when false <blah>
// end
if (value == null) value = UndefinedValue.UNDEFINED;
Label endLabel = s.getNewLabel();
boolean hasElse = (caseNode.getElseNode() != null);
Label elseLabel = s.getNewLabel();
Variable result = s.getNewTemporaryVariable();
List<Label> labels = new ArrayList<Label>();
Map<Label, Node> bodies = new HashMap<Label, Node>();
// build each "when"
for (Node aCase : caseNode.getCases().childNodes()) {
WhenNode whenNode = (WhenNode)aCase;
Label bodyLabel = s.getNewLabel();
Variable eqqResult = s.getNewTemporaryVariable();
labels.add(bodyLabel);
Operand v1, v2;
if (whenNode.getExpressionNodes() instanceof ListNode) {
// SSS FIXME: Note about refactoring:
// - BEQInstr has a quick implementation when the second operand is a boolean literal
// If it can be fixed to do this even on the first operand, we can switch around
// v1 and v2 in the UndefinedValue scenario and DRY out this code.
// - Even with this asymmetric implementation of BEQInstr, you might be tempted to
// switch around v1 and v2 in the else case. But, that is equivalent to this Ruby code change:
// (v1 == value) instead of (value == v1)
// It seems that they should be identical, but the first one is v1.==(value) and the second one is
// value.==(v1). This is just fine *if* the Ruby programmer has implemented an algebraically
// symmetric "==" method on those objects. If not, then, the results might be unexpected where the
// code (intentionally or otherwise) relies on this asymmetry of "==". While it could be argued
// that this a Ruby code bug, we will just try to preserve the order of the == check as it appears
// in the Ruby code.
if (value == UndefinedValue.UNDEFINED) {
v1 = build(whenNode.getExpressionNodes(), s);
v2 = manager.getTrue();
} else {
v1 = value;
v2 = build(whenNode.getExpressionNodes(), s);
}
} else {
s.addInstr(new EQQInstr(eqqResult, build(whenNode.getExpressionNodes(), s), value));
v1 = eqqResult;
v2 = manager.getTrue();
}
s.addInstr(BEQInstr.create(v1, v2, bodyLabel));
// SSS FIXME: This doesn't preserve original order of when clauses. We could consider
// preserving the order (or maybe not, since we would have to sort the constants first
// in any case) for outputing jump tables in certain situations.
//
// add body to map for emitting later
bodies.put(bodyLabel, whenNode.getBodyNode());
}
// Jump to else in case nothing matches!
s.addInstr(new JumpInstr(elseLabel));
// build "else" if it exists
if (hasElse) {
labels.add(elseLabel);
bodies.put(elseLabel, caseNode.getElseNode());
}
// now emit bodies while preserving when clauses order
for (Label whenLabel: labels) {
s.addInstr(new LabelInstr(whenLabel));
Operand bodyValue = build(bodies.get(whenLabel), s);
// bodyValue can be null if the body ends with a return!
if (bodyValue != null) {
// SSS FIXME: Do local optimization of break results (followed by a copy & jump) to short-circuit the jump right away
// rather than wait to do it during an optimization pass when a dead jump needs to be removed. For this, you have
// to look at what the last generated instruction was.