-
-
Save headius/affb0bbae13bdd2ddd85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/core/src/main/java/org/jruby/ir/IRBuilder.java b/core/src/main/java/org/jruby/ir/IRBuilder.java | |
index 4f2ec6a..2f37326 100644 | |
--- a/core/src/main/java/org/jruby/ir/IRBuilder.java | |
+++ b/core/src/main/java/org/jruby/ir/IRBuilder.java | |
@@ -2996,50 +2996,70 @@ public class IRBuilder { | |
addInstr(new ThreadPollInstr(true)); | |
addInstr(new JumpInstr(((IRClosure) scope).startLabel)); | |
} else { | |
addInstr(new ThrowExceptionInstr(IRException.REDO_LocalJumpError)); | |
} | |
} | |
return manager.getNil(); | |
} | |
public Operand buildRegexp(RegexpNode reNode) { | |
// SSS FIXME: Rather than throw syntax error at runtime, we should detect | |
// regexp syntax errors at build time and add an exception-throwing instruction instead | |
return copyAndReturnValue(new Regexp(reNode.getValue(), reNode.getOptions())); | |
} | |
public Operand buildRescue(RescueNode node) { | |
return buildRescueInternal(node, null); | |
} | |
private Operand buildRescueInternal(RescueNode rescueNode, EnsureBlockInfo ensure) { | |
- // Labels marking start, else, end of the begin-rescue(-ensure)-end block | |
- Label rBeginLabel = ensure == null ? getNewLabel() : ensure.regionStart; | |
- Label rEndLabel = ensure == null ? getNewLabel() : ensure.end; | |
- Label rescueLabel = getNewLabel(); // Label marking start of the first rescue code. | |
+ // Wrap the entire begin+rescue+ensure+else with $!-resetting "finally" logic | |
// Save $! in a temp var so it can be restored when the exception gets handled. | |
Variable savedGlobalException = createTemporaryVariable(); | |
addInstr(new GetGlobalVariableInstr(savedGlobalException, "$!")); | |
if (ensure != null) ensure.savedGlobalException = savedGlobalException; | |
+ // prepare $!-clearing ensure block | |
+ EnsureBlockInfo ebi = new EnsureBlockInfo(scope, | |
+ null, | |
+ getCurrentLoop(), | |
+ activeRescuers.peek()); | |
+ { | |
+ | |
+ ebi.addInstr(new PutGlobalVarInstr("$!", savedGlobalException)); | |
+ | |
+ // ------------ Build the protected region ------------ | |
+ activeEnsureBlockStack.push(ebi); | |
+ | |
+ // Start of protected region | |
+ addInstr(new LabelInstr(ebi.regionStart)); | |
+ addInstr(new ExceptionRegionStartMarkerInstr(ebi.dummyRescueBlockLabel)); | |
+ activeRescuers.push(ebi.dummyRescueBlockLabel); | |
+ } | |
+ | |
+ // Labels marking start, else, end of the begin-rescue(-ensure)-end block | |
+ Label rBeginLabel = ensure == null ? getNewLabel() : ensure.regionStart; | |
+ Label rEndLabel = ensure == null ? getNewLabel() : ensure.end; | |
+ Label rescueLabel = getNewLabel(); // Label marking start of the first rescue code. | |
+ | |
addInstr(new LabelInstr(rBeginLabel)); | |
// Placeholder rescue instruction that tells rest of the compiler passes the boundaries of the rescue block. | |
addInstr(new ExceptionRegionStartMarkerInstr(rescueLabel)); | |
activeRescuers.push(rescueLabel); | |
// 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 = createTemporaryVariable(); | |
if (rescueNode.getBodyNode() != null) tmp = build(rescueNode.getBodyNode()); | |
// 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 | |
@@ -3080,41 +3100,72 @@ public class IRBuilder { | |
// 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! | |
//} | |
// Start of rescue logic | |
addInstr(new LabelInstr(rescueLabel)); | |
// Save off exception & exception comparison type | |
Variable exc = addResultInstr(new ReceiveRubyExceptionInstr(createTemporaryVariable())); | |
// Build the actual rescue block(s) | |
buildRescueBodyInternal(rescueNode.getRescueNode(), rv, exc, rEndLabel); | |
// End label -- only if there is no ensure block! With an ensure block, you end at ensureEndLabel. | |
if (ensure == null) addInstr(new LabelInstr(rEndLabel)); | |
- activeRescueBlockStack.pop(); | |
+ // close out the $!-clearing region | |
+ { | |
+ // End of protected region | |
+ addInstr(new ExceptionRegionEndMarkerInstr()); | |
+ activeRescuers.pop(); | |
+ | |
+ // Non-exceptional reset of $! | |
+ addInstr(new PutGlobalVarInstr("$!", savedGlobalException)); | |
+ addInstr(new JumpInstr(ebi.end)); | |
+ | |
+ // Pop the current ensure block info node | |
+ activeEnsureBlockStack.pop(); | |
+ | |
+ // ------------ Emit the ensure body alongwith dummy rescue block ------------ | |
+ // Now build the dummy rescue block that | |
+ // catches all exceptions thrown by the body | |
+ Variable exc2 = createTemporaryVariable(); | |
+ addInstr(new LabelInstr(ebi.dummyRescueBlockLabel)); | |
+ addInstr(new ReceiveJRubyExceptionInstr(exc2)); | |
+ | |
+ // Now emit the ensure body's stashed instructions | |
+ ebi.emitBody(this); | |
+ | |
+ // Return (rethrow exception/end) | |
+ // rethrows the caught exception from the dummy ensure block | |
+ addInstr(new ThrowExceptionInstr(exc2)); | |
+ | |
+ // End label for the exception region | |
+ addInstr(new LabelInstr(ebi.end)); | |
+ | |
+ activeRescueBlockStack.pop(); | |
+ } | |
return rv; | |
} | |
private void outputExceptionCheck(Operand excType, Operand excObj, Label caughtLabel) { | |
Variable eqqResult = addResultInstr(new RescueEQQInstr(createTemporaryVariable(), excType, excObj)); | |
addInstr(BEQInstr.create(eqqResult, manager.getTrue(), caughtLabel)); | |
} | |
private void buildRescueBodyInternal(RescueBodyNode rescueBodyNode, Variable rv, Variable exc, Label endLabel) { | |
final Node exceptionList = rescueBodyNode.getExceptionNodes(); | |
// Compare and branch as necessary! | |
Label uncaughtLabel = getNewLabel(); | |
Label caughtLabel = getNewLabel(); | |
if (exceptionList != null) { | |
if (exceptionList instanceof ListNode) { | |
List<Operand> excTypes = new ArrayList<>(); | |
for (Node excType : exceptionList.childNodes()) { | |
excTypes.add(build(excType)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment