final RescueNode rescueNode = (RescueNode) node;
boolean noEnsure = _ensureBlockStack.empty();
EnsureBlockInfo ebi = noEnsure ? null : _ensureBlockStack.peek();
// Labels marking start, else, end of the begin-rescue(-ensure)-end block
Label rBeginLabel = availableBeginLabel != null ? availableBeginLabel : m.getNewLabel();
Label rEndLabel = noEnsure ? m.getNewLabel() : ebi.end;
Label elseLabel = rescueNode.getElseNode() == null ? null : m.getNewLabel();
// Only generate the label instruction if we weren't passed in a label
// Optimization to eliminate extra labels in begin-rescue-ensure-end code
if (availableBeginLabel == null)
m.addInstr(new LABEL_Instr(rBeginLabel));
// Placeholder rescue instruction that tells rest of the compiler passes the boundaries of the rescue block.
List<Label> rescueBlockLabels = new ArrayList<Label>();
ExceptionRegionStartMarkerInstr rbStartInstr = new ExceptionRegionStartMarkerInstr(rBeginLabel, rEndLabel, rescueBlockLabels);
m.addInstr(rbStartInstr);
// Body
Operand tmp = Nil.NIL; // default return value if for some strange reason, we neither have the body node or the else node!
Variable rv = m.getNewTemporaryVariable();
if (rescueNode.getBodyNode() != null)
tmp = build(rescueNode.getBodyNode(), m);
// Else part of the body -- we simply fall through from the main body if there were no exceptions
if (elseLabel != null) {
m.addInstr(new LABEL_Instr(elseLabel));
tmp = build(rescueNode.getElseNode(), m);
}
if (tmp != U_NIL) {
m.addInstr(new CopyInstr(rv, tmp));
// No explicit return from the protected body
// - If we dont have any ensure blocks, simply jump to the end of the rescue block
// - If we do, get the innermost ensure block, set up the return address to the end of the ensure block, and go execute the ensure code.
if (noEnsure) {
m.addInstr(new JumpInstr(rEndLabel));
}
else {
// NOTE: rEndLabel is identical to ebi.end, but less confusing to use rEndLabel since that makes more semantic sense
m.addInstr(new SET_RETADDR_Instr(ebi.returnAddr, rEndLabel));
m.addInstr(new JumpInstr(ebi.start));
}
}
else {
// If the body had an explicit return, the return instruction IR build takes care of setting
// up execution of all necessary ensure blocks. So, nothing to do here!
//
// Additionally, the value in 'rv' will never be used, so need to set it to any specific value.
// So, we can leave it undefined. If on the other hand, there was an exception in that block,
// 'rv' will get set in the rescue handler -- see the 'rv' being passed into
// buildRescueBodyInternal below. So, in either case, we are good!
}
// 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();
m.addInstr(rbEndInstr);
// Build the actual rescue block(s)
Label rbLabel = m.getNewLabel(); // Label marking start of the first rescue code.
rescueBlockLabels.add(rbLabel);
m.addInstr(new LABEL_Instr(rbLabel));
buildRescueBodyInternal(m, rescueNode.getRescueNode(), rv, rEndLabel, rescueBlockLabels);
// End label -- only if there is no ensure block! With an ensure block, you end at ensureEndLabel.