diff --git a/js/src/jit-test/tests/debug/cross-context-3.js b/js/src/jit-test/tests/debug/cross-context-3.js new file mode 100644 index 000000000000..1c799d27cab3 --- /dev/null +++ b/js/src/jit-test/tests/debug/cross-context-3.js @@ -0,0 +1,14 @@ +var g = newGlobal('new-compartment'); +g.eval('function f() { debugger; evaluate("debugger;", {newContext: true}); }'); + +var dbg = new Debugger(g); +var hits = 0; +dbg.onDebuggerStatement = function (frame1) { + dbg.onDebuggerStatement = function (frame2) { + assertEq(frame1.eval("throw 'ponies'").throw, 'ponies'); + hits++; + }; +}; + +g.f(); +assertEq(hits, 1); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 3b13506800b8..07f4df2b7842 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -1098,6 +1098,27 @@ CrashIfInvalidSlot(StackFrame *fp, Value *vp) } } +/* + * Given that the iterator's current value of fp_ and calls_ (initialized on + * construction or after operator++ popped the previous scripted/native call), + * "settle" the iterator on a new StackIter::State value. The goal is to + * present the client a simple linear sequence of native/scripted calls while + * covering up unpleasant stack implementation details: + * - The frame change can be "saved" and "restored" (see JS_SaveFrameChain). + * This artificially cuts the call chain and the StackIter client may want + * to continue through this cut to the previous frame by passing + * GO_THROUGH_SAVED. + * - fp->prev can be in a different contiguous segment from fp. In this case, + * the current values of sp/pc after calling popFrame/popCall are incorrect + * and should be recovered from fp->prev's segment. + * - there is no explicit relationship to determine whether fp_ or calls_ is + * the innermost invocation so implicit memory ordering is used since both + * push values on the stack. + * - calls to natives directly from JS do not push a record and thus the + * native call must be recovered by sniffing the stack. + * - a native call's 'callee' argument is clobbered on return while the + * CallArgsList element is still visible. + */ void StackIter::settleOnNewState() { @@ -1119,7 +1140,8 @@ StackIter::settleOnNewState() bool containsFrame = seg_->contains(fp_); bool containsCall = seg_->contains(calls_); while (!containsFrame && !containsCall) { - seg_ = seg_->prevInContext(); + /* Eval-in-frame can cross contexts, so use prevInMemory. */ + seg_ = seg_->prevInMemory(); containsFrame = seg_->contains(fp_); containsCall = seg_->contains(calls_); @@ -1134,8 +1156,10 @@ StackIter::settleOnNewState() *this = tmp; return; } + /* There is no eval-in-frame equivalent for native calls. */ JS_ASSERT_IF(containsCall, &seg_->calls() == calls_); + settleOnNewSegment(); } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 3333eb767ffb..9fc956d14730 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -140,11 +140,15 @@ namespace detail { * * An additional feature (perhaps not for much longer: bug 650361) is that * multiple independent "contexts" can interleave (LIFO) on a single contiguous - * stack. "Independent" here means that neither context sees the other's - * frames. Concretely, an embedding may enter the JS engine on cx1 and then, - * from a native called by the JS engine, reenter the VM on cx2. Changing from - * cx1 to cx2 causes a new segment to be started for cx2's stack on top of - * cx1's current segment. These two segments are linked from the perspective of + * stack. "Independent" here means that each context has its own callstack. + * Note, though, that eval-in-frame allows one context's callstack to join + * another context's callstack. Thus, in general, the structure of calls in a + * StackSpace is a forest. + * + * More concretely, an embedding may enter the JS engine on cx1 and then, from + * a native called by the JS engine, reenter the VM on cx2. Changing from cx1 + * to cx2 causes a new segment to be started for cx2's stack on top of cx1's + * current segment. These two segments are linked from the perspective of * StackSpace, since they are adjacent on the thread's stack, but not from the * perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory * and prevInContext. Each independent stack is encapsulated and managed by @@ -1773,10 +1777,10 @@ class GeneratorFrameGuard : public FrameGuard /*****************************************************************************/ /* - * Iterate through the callstack of the given context. Each element of said - * callstack can either be the execution of a script (scripted function call, - * global code, eval code, debugger code) or the invocation of a (C++) native. - * Example usage: + * Iterate through the callstack (following fp->prev) of the given context. + * Each element of said callstack can either be the execution of a script + * (scripted function call, global code, eval code, debugger code) or the + * invocation of a (C++) native. Example usage: * * for (Stackiter i(cx); !i.done(); ++i) { * if (i.isScript()) {