// start of protected region
s.addInstr(new LabelInstr(rBeginLabel));
s.addInstr(new ExceptionRegionStartMarkerInstr(rBeginLabel, rEndLabel, ebi.dummyRescueBlockLabel, ebi.dummyRescueBlockLabel));
// Generate IR for code being protected
Operand rv;
if (bodyNode instanceof RescueNode) {
// The rescue code will ensure that the region is ended
rv = buildRescueInternal((RescueNode) bodyNode, s, ebi);
} else {
rv = build(bodyNode, s);
// Jump to start of ensure block -- dont bother if we had a return in the protected body
if (rv != U_NIL) s.addInstr(new SetReturnAddressInstr(ebi.returnAddr, rEndLabel));
}
// end of protected region
s.addInstr(new ExceptionRegionEndMarkerInstr());
// Pop the current ensure block info node *BEFORE* generating the ensure code for this block itself!
_ensureBlockStack.pop();
// Run the ensure block now
s.addInstr(new JumpInstr(ebi.start));
// Now build the dummy rescue block that:
// * catches all exceptions thrown by the body
// * jumps to the ensure block code
// * returns back (via set_retaddr instr)
Label rethrowExcLabel = s.getNewLabel();
Variable exc = s.getNewTemporaryVariable();
s.addInstr(new LabelInstr(ebi.dummyRescueBlockLabel));
s.addInstr(new ReceiveExceptionInstr(exc, false)); // Dont check type since we are simply throwing it back
s.addInstr(new SetReturnAddressInstr(ebi.returnAddr, rethrowExcLabel));
// Generate the ensure block now
s.addInstr(new LabelInstr(ebi.start));
// Two cases:
// 1. Ensure block has no explicit return => the result of the entire ensure expression is the result of the protected body.
// 2. Ensure block has an explicit return => the result of the protected body is ignored.
Operand ensureRetVal = (ensureNode.getEnsureNode() == null) ? manager.getNil() : build(ensureNode.getEnsureNode(), s);
// U_NIL => there was a return from within the ensure block!
if (ensureRetVal == U_NIL) rv = U_NIL;
// Return (rethrow exception/end)
s.addInstr(new JumpIndirectInstr(ebi.returnAddr));