Bug 758617 - Fix StackIter::settleOnNewState to handle cross-context eval-in-frame (r=jorendorff)

This commit is contained in:
Luke Wagner 2012-05-29 14:46:42 -07:00
Родитель 30f69421cd
Коммит a28268350f
3 изменённых файлов: 52 добавлений и 10 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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 void
StackIter::settleOnNewState() StackIter::settleOnNewState()
{ {
@ -1119,7 +1140,8 @@ StackIter::settleOnNewState()
bool containsFrame = seg_->contains(fp_); bool containsFrame = seg_->contains(fp_);
bool containsCall = seg_->contains(calls_); bool containsCall = seg_->contains(calls_);
while (!containsFrame && !containsCall) { while (!containsFrame && !containsCall) {
seg_ = seg_->prevInContext(); /* Eval-in-frame can cross contexts, so use prevInMemory. */
seg_ = seg_->prevInMemory();
containsFrame = seg_->contains(fp_); containsFrame = seg_->contains(fp_);
containsCall = seg_->contains(calls_); containsCall = seg_->contains(calls_);
@ -1134,8 +1156,10 @@ StackIter::settleOnNewState()
*this = tmp; *this = tmp;
return; return;
} }
/* There is no eval-in-frame equivalent for native calls. */ /* There is no eval-in-frame equivalent for native calls. */
JS_ASSERT_IF(containsCall, &seg_->calls() == calls_); JS_ASSERT_IF(containsCall, &seg_->calls() == calls_);
settleOnNewSegment(); settleOnNewSegment();
} }

Просмотреть файл

@ -140,11 +140,15 @@ namespace detail {
* *
* An additional feature (perhaps not for much longer: bug 650361) is that * An additional feature (perhaps not for much longer: bug 650361) is that
* multiple independent "contexts" can interleave (LIFO) on a single contiguous * multiple independent "contexts" can interleave (LIFO) on a single contiguous
* stack. "Independent" here means that neither context sees the other's * stack. "Independent" here means that each context has its own callstack.
* frames. Concretely, an embedding may enter the JS engine on cx1 and then, * Note, though, that eval-in-frame allows one context's callstack to join
* from a native called by the JS engine, reenter the VM on cx2. Changing from * another context's callstack. Thus, in general, the structure of calls in a
* cx1 to cx2 causes a new segment to be started for cx2's stack on top of * StackSpace is a forest.
* cx1's current segment. These two segments are linked from the perspective of *
* 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 * 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 * perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
* and prevInContext. Each independent stack is encapsulated and managed by * 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 * Iterate through the callstack (following fp->prev) of the given context.
* callstack can either be the execution of a script (scripted function call, * Each element of said callstack can either be the execution of a script
* global code, eval code, debugger code) or the invocation of a (C++) native. * (scripted function call, global code, eval code, debugger code) or the
* Example usage: * invocation of a (C++) native. Example usage:
* *
* for (Stackiter i(cx); !i.done(); ++i) { * for (Stackiter i(cx); !i.done(); ++i) {
* if (i.isScript()) { * if (i.isScript()) {