return buildRescueInternal(node, s, null);
}
private Operand buildRescueInternal(RescueNode rescueNode, IRScope s, EnsureBlockInfo ensure) {
// Labels marking start, else, end of the begin-rescue(-ensure)-end block
Label rBeginLabel = ensure == null ? s.getNewLabel() : ensure.regionStart;
Label rEndLabel = ensure == null ? s.getNewLabel() : ensure.end;
Label rescueLabel = s.getNewLabel(); // Label marking start of the first rescue code.
if (ensure == null) s.addInstr(new LabelInstr(rBeginLabel));
// Placeholder rescue instruction that tells rest of the compiler passes the boundaries of the rescue block.
s.addInstr(new ExceptionRegionStartMarkerInstr(rBeginLabel, rEndLabel, ensure == null ? null : ensure.dummyRescueBlockLabel, rescueLabel));
// Save $! in a temp var so it can be restored when the exception gets handled.
// SSS FIXME: Dont yet understand why an exception needs to be saved/restored.
Variable savedGlobalException = s.getNewTemporaryVariable();
s.addInstr(new GetGlobalVariableInstr(savedGlobalException, "$!"));
if (ensure != null) ensure.savedGlobalException = savedGlobalException;
// Body
Operand tmp = manager.getNil(); // default return value if for some strange reason, we neither have the body node or the else node!
Variable rv = s.getNewTemporaryVariable();
if (rescueNode.getBodyNode() != null) tmp = build(rescueNode.getBodyNode(), s);
// Push rescue block *after* body has been built.
// If not, this messes up generation of retry in these scenarios like this:
//
// begin -- 1
// ...
// rescue
// begin -- 2
// ...
// retry
// rescue
// ...
// end
// end
//
// The retry should jump to 1, not 2.
// If we push the rescue block before building the body, we will jump to 2.
_rescueBlockStack.push(new RescueBlockInfo(rescueNode, rBeginLabel, savedGlobalException, getCurrentLoop()));
// Since rescued regions are well nested within Ruby, this bare marker is sufficient to
// let us discover the edge of the region during linear traversal of instructions during cfg construction.
ExceptionRegionEndMarkerInstr rbEndInstr = new ExceptionRegionEndMarkerInstr();
s.addInstr(rbEndInstr);
// Else part of the body -- we simply fall through from the main body if there were no exceptions
Label elseLabel = rescueNode.getElseNode() == null ? null : s.getNewLabel();
if (elseLabel != null) {
s.addInstr(new LabelInstr(elseLabel));
tmp = build(rescueNode.getElseNode(), s);
}