Skip to content

Instantly share code, notes, and snippets.

@headius
Created May 8, 2015 14:17
Show Gist options
  • Save headius/affb0bbae13bdd2ddd85 to your computer and use it in GitHub Desktop.
Save headius/affb0bbae13bdd2ddd85 to your computer and use it in GitHub Desktop.
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