diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 120943e4d18..6a8fcc30f12 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -395,11 +395,12 @@ ForceFrame::enter() LeaveTrace(context); JS_ASSERT(context->compartment == target->compartment()); + JSCompartment *destination = context->compartment; JSObject *scopeChain = target->getGlobal(); JS_ASSERT(scopeChain->isNative()); - return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame); + return context->stack.pushDummyFrame(context, *scopeChain, frame); } AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target) @@ -428,21 +429,8 @@ AutoCompartment::enter() JS_ASSERT(scopeChain->isNative()); frame.construct(); - - /* - * Set the compartment eagerly so that pushDummyFrame associates the - * resource allocation request with 'destination' instead of 'origin'. - * (This is important when content has overflowed the stack and chrome - * is preparing to run JS to throw up a slow script dialog.) However, - * if an exception is thrown, we need it to be in origin's compartment - * so be careful to only report after restoring. - */ - context->compartment = destination; - if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) { - context->compartment = origin; - js_ReportOverRecursed(context); + if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref())) return false; - } if (context->isExceptionPending()) context->wrapPendingException(); diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 5f70a55f3cb..da5925d84ef 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -388,7 +388,8 @@ StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx) STATIC_POSTCONDITION(!return || ubound(from) >= nvals) JS_ALWAYS_INLINE bool -StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const +StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, + JSCompartment *dest) const { assertInvariants(); JS_ASSERT(from >= firstUnused()); @@ -396,10 +397,16 @@ StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptr JS_ASSERT(from <= commitEnd_); #endif if (JS_UNLIKELY(conservativeEnd_ - from < nvals)) - return ensureSpaceSlow(cx, report, from, nvals); + return ensureSpaceSlow(cx, report, from, nvals, dest); return true; } +bool +StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const +{ + return ensureSpace(cx, report, from, nvals, cx->compartment); +} + inline Value * StackSpace::getStackLimit(JSContext *cx, MaybeReportError report) { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 41ccdf8d573..cab01bae913 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -414,13 +414,13 @@ StackSpace::mark(JSTracer *trc) } JS_FRIEND_API(bool) -StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals) const +StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, + JSCompartment *dest) const { assertInvariants(); - bool trusted = !cx->compartment || - cx->compartment->principals == cx->runtime->trustedPrincipals(); + JS_ASSERT_IF(dest, cx); + bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals(); Value *end = trusted ? trustedEnd_ : defaultEnd_; /* @@ -548,17 +548,17 @@ ContextStack::containsSlow(const StackFrame *target) const */ Value * ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, - MaybeExtend extend, bool *pushedSeg) + MaybeExtend extend, bool *pushedSeg, JSCompartment *dest) { Value *firstUnused = space().firstUnused(); if (onTop() && extend) { - if (!space().ensureSpace(cx, report, firstUnused, nvars)) + if (!space().ensureSpace(cx, report, firstUnused, nvars, dest)) return NULL; return firstUnused; } - if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars)) + if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest)) return NULL; FrameRegs *regs; @@ -577,6 +577,13 @@ ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, return seg_->slotsBegin(); } +Value * +ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, + MaybeExtend extend, bool *pushedSeg) +{ + return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment); +} + void ContextStack::popSegment() { @@ -696,11 +703,12 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi } bool -ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain, - DummyFrameGuard *dfg) +ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg) { + JSCompartment *dest = scopeChain.compartment(); + uintN nvars = VALUES_PER_STACK_FRAME; - Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_); + Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest); if (!firstUnused) return NULL; @@ -708,6 +716,7 @@ ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &s fp->initDummyFrame(cx, scopeChain); dfg->regs_.initDummyFrame(*fp); + cx->compartment = dest; dfg->prevRegs_ = seg_->pushRegs(dfg->regs_); JS_ASSERT(space().firstUnused() == dfg->regs_.sp); dfg->setPushed(*this); @@ -790,24 +799,11 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg) bool ContextStack::saveFrameChain() { - /* - * The StackSpace uses the context's current compartment to determine - * whether to allow access to the privileged end-of-stack buffer. - * However, we always want saveFrameChain to have access to this privileged - * buffer since it gets used to prepare calling trusted JS. To force this, - * we clear the current compartment (which is interpreted by ensureSpace as - * 'trusted') and either restore it on OOM or let resetCompartment() - * clobber it. - */ - JSCompartment *original = cx_->compartment; - cx_->compartment = NULL; + JSCompartment *dest = NULL; bool pushedSeg; - if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) { - cx_->compartment = original; - js_ReportOverRecursed(cx_); + if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest)) return false; - } JS_ASSERT(pushedSeg); JS_ASSERT(!hasfp()); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index e761cd3f5f9..bc10ce5bdf8 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -44,6 +44,7 @@ #include "jsfun.h" struct JSContext; +struct JSCompartment; namespace js { @@ -1329,10 +1330,15 @@ class StackSpace friend class ContextStack; friend class StackFrame; + inline bool ensureSpace(JSContext *cx, MaybeReportError report, + Value *from, ptrdiff_t nvals, + JSCompartment *dest) const; inline bool ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const; JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals) const; + Value *from, ptrdiff_t nvals, + JSCompartment *dest) const; + StackSegment &findContainingSegment(const StackFrame *target) const; public: @@ -1420,6 +1426,9 @@ class ContextStack /* Implementation details of push* public interface. */ StackSegment *pushSegment(JSContext *cx); enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false }; + Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, + MaybeExtend extend, bool *pushedSeg, + JSCompartment *dest); Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, MaybeExtend extend, bool *pushedSeg); @@ -1502,9 +1511,16 @@ class ContextStack */ bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg); - /* Pushes a "dummy" frame; should be removed one day. */ - bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain, - DummyFrameGuard *dfg); + /* + * When changing the compartment of a cx, it is necessary to immediately + * change the scope chain to a global in the right compartment since any + * amount of general VM code can run before the first scripted frame is + * pushed (if at all). This is currently and hackily accomplished by + * pushing a "dummy frame" with the correct scope chain. On success, this + * function will change the compartment to 'scopeChain.compartment()' and + * push a dummy frame for 'scopeChain'. On failure, nothing is changed. + */ + bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg); /* * An "inline frame" may only be pushed from within the top, active