зеркало из https://github.com/mozilla/pjs.git
Bug 676937 - Make entering a compartment and pushing a dummy frame an atomic stack operation (r=mrbkap)
This commit is contained in:
Родитель
d618965c5c
Коммит
6ac38cb8e2
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче