Skip to content

Commit

Permalink
Re-push the frame, class, file, and line from the thread block.
Browse files Browse the repository at this point in the history
When a non-local flow event bubbles out of a thread, it lazily
triggers a LocalJumpError to be created, along which path a hook
event for "raise" fires. Unfortunately this tries to access the
current frame and stack trace information, which are already
popped off the stack by the thread's proc.

This change re-pushes that frame and stack trace info around the
creation of the exception, so that the hook has appropriate info
available.

Fixes #3781
headius committed Mar 15, 2017
1 parent 838703a commit 9dcc41c
Showing 1 changed file with 16 additions and 4 deletions.
20 changes: 16 additions & 4 deletions core/src/main/java/org/jruby/internal/runtime/RubyRunnable.java
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
import org.jruby.exceptions.MainExitException;
import org.jruby.exceptions.ThreadKill;
import org.jruby.runtime.Block;
import org.jruby.runtime.Frame;
import org.jruby.runtime.RubyEvent;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@@ -93,15 +94,26 @@ public void run() {
// uber-ThreadKill catcher, since it should always just mean "be dead"
try {
// Call the thread's code
RubyModule frameClass = proc.getBlock().getFrame().getKlazz();
Block threadBlock = proc.getBlock();
RubyModule frameClass = threadBlock.getFrame().getKlazz();
String file = threadBlock.getBinding().getFile();
int line = threadBlock.getBinding().getLine();
try {
if (runtime.hasEventHooks() && runtime.is2_0()) context.trace(RubyEvent.THREAD_BEGIN, null, frameClass);
if (runtime.hasEventHooks() && runtime.is2_0()) context.trace(RubyEvent.THREAD_BEGIN, null, frameClass, file, line);
IRubyObject result = proc.call(context, arguments);
if (runtime.hasEventHooks() && runtime.is2_0()) context.trace(RubyEvent.THREAD_END, null, frameClass);
if (runtime.hasEventHooks() && runtime.is2_0()) context.trace(RubyEvent.THREAD_END, null, frameClass, file, line);
rubyThread.cleanTerminate(result);
} catch (JumpException.ReturnJump rj) {
if (runtime.is1_9()) {
rubyThread.exceptionRaised(rj.buildException(runtime));
// re-push the class, file, and line so the exception has appropriate data (jruby/jruby#3781)
Frame oldFrame = context.preYieldNoScope(threadBlock.getBinding(), frameClass);
ThreadContext.pushBacktrace(context, "(thread " + javaThread.getName(), file, line);
try {
rubyThread.exceptionRaised(rj.buildException(runtime));
} finally {
ThreadContext.popBacktrace(context);
context.postYieldNoScope(oldFrame);
}
} else {
rubyThread.exceptionRaised(runtime.newThreadError("return can't jump across threads"));
}

0 comments on commit 9dcc41c

Please sign in to comment.