Bug 951282 - Wrap the pending exception lazily; r=luke

--HG--
extra : rebase_source : e2bf58c6c5ba5ba857a97bd18f5543f32da6f508
This commit is contained in:
Terrence Cole 2013-12-17 13:01:05 -08:00
parent 3e8465db86
commit 17b810fe52
11 changed files with 79 additions and 71 deletions

View File

@ -540,7 +540,9 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
rfe->kind = ResumeFromException::RESUME_FINALLY;
jsbytecode *finallyPC = script->main() + tn->start + tn->length;
rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC);
rfe->exception = cx->getPendingException();
// Drop the exception instead of leaking cross compartment data.
if (!cx->getPendingException(MutableHandleValue::fromMarkedLocation(&rfe->exception)))
rfe->exception = UndefinedValue();
cx->clearPendingException();
return;
}

View File

@ -5865,9 +5865,7 @@ JS_GetPendingException(JSContext *cx, MutableHandleValue vp)
CHECK_REQUEST(cx);
if (!cx->isExceptionPending())
return false;
vp.set(cx->getPendingException());
assertSameCompartment(cx, vp);
return true;
return cx->getPendingException(vp);
}
JS_PUBLIC_API(void)

View File

@ -1073,7 +1073,7 @@ ThreadSafeContext::asForkJoinSlice()
JSContext::JSContext(JSRuntime *rt)
: ExclusiveContext(rt, &rt->mainThread, Context_JS),
throwing(false),
exception(UndefinedValue()),
unwrappedException_(UndefinedValue()),
options_(),
reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
resolvingList(nullptr),
@ -1109,21 +1109,21 @@ JSContext::~JSContext()
JS_ASSERT(!resolvingList);
}
/*
* Since this function is only called in the context of a pending exception,
* the caller must subsequently take an error path. If wrapping fails, it will
* set a new (uncatchable) exception to be used in place of the original.
*/
void
JSContext::wrapPendingException()
bool
JSContext::getPendingException(MutableHandleValue rval)
{
RootedValue value(this, getPendingException());
JS_ASSERT(throwing);
rval.set(unwrappedException_);
if (IsAtomsCompartment(compartment()))
return true;
clearPendingException();
if (!IsAtomsCompartment(compartment()) && compartment()->wrap(this, &value))
setPendingException(value);
if (!compartment()->wrap(this, rval))
return false;
assertSameCompartment(this, rval);
setPendingException(rval);
return true;
}
void
JSContext::enterGenerator(JSGenerator *gen)
{
@ -1171,9 +1171,6 @@ JSContext::restoreFrameChain()
if (Activation *act = mainThread().activation())
act->restoreFrameChain();
if (isExceptionPending())
wrapPendingException();
}
bool
@ -1287,7 +1284,7 @@ JSContext::mark(JSTracer *trc)
if (defaultCompartmentObject_)
MarkObjectRoot(trc, &defaultCompartmentObject_, "default compartment object");
if (isExceptionPending())
MarkValueRoot(trc, &exception, "exception");
MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
TraceCycleDetectionSet(trc, cycleDetectorSet);

View File

@ -426,7 +426,7 @@ struct JSContext : public js::ExclusiveContext,
private:
/* Exception state -- the exception member is a GC root by definition. */
bool throwing; /* is there a pending exception? */
js::Value exception; /* most-recently-thrown exception */
js::Value unwrappedException_; /* most-recently-thrown exception */
/* Per-context options. */
JS::ContextOptions options_;
@ -468,9 +468,6 @@ struct JSContext : public js::ExclusiveContext,
return defaultCompartmentObject_;
}
/* Wrap cx->exception for the current compartment. */
void wrapPendingException();
/* State for object and array toSource conversion. */
js::ObjectSet cycleDetectorSet;
@ -583,16 +580,13 @@ struct JSContext : public js::ExclusiveContext,
return throwing;
}
js::Value getPendingException() {
JS_ASSERT(throwing);
return exception;
}
bool getPendingException(JS::MutableHandleValue rval);
void setPendingException(js::Value v);
void clearPendingException() {
throwing = false;
exception.setUndefined();
unwrappedException_.setUndefined();
}
#ifdef DEBUG

View File

@ -358,10 +358,11 @@ ExclusiveContext::typeLifoAlloc()
} /* namespace js */
inline void
JSContext::setPendingException(js::Value v) {
JSContext::setPendingException(js::Value v)
{
JS_ASSERT(!IsPoisonedValue(v));
this->throwing = true;
this->exception = v;
this->unwrappedException_ = v;
js::assertSameCompartment(this, v);
}
@ -388,11 +389,6 @@ js::ExclusiveContext::enterCompartment(JSCompartment *c)
enterCompartmentDepth_++;
c->enter();
setCompartment(c);
if (JSContext *cx = maybeJSContext()) {
if (cx->throwing)
cx->wrapPendingException();
}
}
inline void
@ -406,11 +402,6 @@ js::ExclusiveContext::leaveCompartment(JSCompartment *oldCompartment)
JSCompartment *startingCompartment = compartment_;
setCompartment(oldCompartment);
startingCompartment->leave();
if (JSContext *cx = maybeJSContext()) {
if (cx->throwing && oldCompartment)
cx->wrapPendingException();
}
}
inline void

View File

@ -730,7 +730,10 @@ js_ReportUncaughtException(JSContext *cx)
if (!cx->isExceptionPending())
return true;
RootedValue exn(cx, cx->getPendingException());
RootedValue exn(cx);
if (!cx->getPendingException(&exn))
return false;
AutoValueVector roots(cx);
roots.resize(6);

View File

@ -978,7 +978,8 @@ js::CloseIterator(JSContext *cx, HandleObject obj)
bool
js::UnwindIteratorForException(JSContext *cx, HandleObject obj)
{
RootedValue v(cx, cx->getPendingException());
RootedValue v(cx);
cx->getPendingException(&v);
cx->clearPendingException();
if (!CloseIterator(cx, obj))
return false;
@ -1195,7 +1196,12 @@ js_IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
return false;
if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
/* Check for StopIteration. */
if (!cx->isExceptionPending() || !JS_IsStopIteration(cx->getPendingException()))
if (!cx->isExceptionPending())
return false;
RootedValue exception(cx);
if (!cx->getPendingException(&exception))
return false;
if (!JS_IsStopIteration(exception))
return false;
cx->clearPendingException();

View File

@ -149,8 +149,8 @@ ErrorCopier::~ErrorCopier()
{
JSContext *cx = ac.ref().context()->asJSContext();
if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) {
RootedValue exc(cx, cx->getPendingException());
if (exc.isObject() && exc.toObject().is<ErrorObject>()) {
RootedValue exc(cx);
if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
cx->clearPendingException();
ac.destroy();
Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());

View File

@ -783,11 +783,13 @@ Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment> &ac,
JSContext *cx = ac.ref().context()->asJSContext();
if (cx->isExceptionPending()) {
if (callHook && uncaughtExceptionHook) {
Value fval = ObjectValue(*uncaughtExceptionHook);
Value exc = cx->getPendingException();
RootedValue rv(cx);
RootedValue exc(cx);
if (!cx->getPendingException(&exc))
return JSTRAP_ERROR;
cx->clearPendingException();
if (Invoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
RootedValue rv(cx);
if (Invoke(cx, ObjectValue(*object), fval, 1, exc.address(), &rv))
return vp ? parseResumptionValue(ac, true, rv, *vp, false) : JSTRAP_CONTINUE;
}
@ -823,7 +825,8 @@ Debugger::resultToCompletion(JSContext *cx, bool ok, const Value &rv,
value.set(rv);
} else if (cx->isExceptionPending()) {
*status = JSTRAP_THROW;
value.set(cx->getPendingException());
if (!cx->getPendingException(value))
*status = JSTRAP_ERROR;
cx->clearPendingException();
} else {
*status = JSTRAP_ERROR;
@ -984,7 +987,9 @@ Debugger::fireExceptionUnwind(JSContext *cx, MutableHandleValue vp)
JS_ASSERT(hook);
JS_ASSERT(hook->isCallable());
RootedValue exc(cx, cx->getPendingException());
RootedValue exc(cx);
if (!cx->getPendingException(&exc))
return JSTRAP_ERROR;
cx->clearPendingException();
Maybe<AutoCompartment> ac;
@ -1231,7 +1236,8 @@ Debugger::onSingleStep(JSContext *cx, MutableHandleValue vp)
RootedValue exception(cx, UndefinedValue());
bool exceptionPending = cx->isExceptionPending();
if (exceptionPending) {
exception = cx->getPendingException();
if (!cx->getPendingException(&exception))
return JSTRAP_ERROR;
cx->clearPendingException();
}

View File

@ -993,6 +993,7 @@ HandleError(JSContext *cx, FrameRegs &regs)
}
}
RootedValue exception(cx);
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
JSTryNote *tn = *tni;
@ -1009,14 +1010,10 @@ HandleError(JSContext *cx, FrameRegs &regs)
switch (tn->kind) {
case JSTRY_CATCH:
/* Catch cannot intercept the closing of a generator. */
if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
if (!cx->getPendingException(&exception))
return ErrorReturnContinuation;
if (exception.isMagic(JS_GENERATOR_CLOSING))
break;
/*
* Don't clear exceptions to save cx->exception from GC
* until it is pushed to the stack via [exception] in the
* catch block.
*/
return CatchContinuation;
case JSTRY_FINALLY:
@ -1042,11 +1039,16 @@ HandleError(JSContext *cx, FrameRegs &regs)
* Propagate the exception or error to the caller unless the exception
* is an asynchronous return from a generator.
*/
if (JS_UNLIKELY(cx->isExceptionPending() &&
cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) {
cx->clearPendingException();
ok = true;
regs.fp()->clearReturnValue();
if (cx->isExceptionPending()) {
RootedValue exception(cx);
if (!cx->getPendingException(&exception))
return ErrorReturnContinuation;
if (exception.isMagic(JS_GENERATOR_CLOSING)) {
cx->clearPendingException();
ok = true;
regs.fp()->clearReturnValue();
}
}
} else {
UnwindForUncatchableException(cx, regs);
@ -3433,8 +3435,13 @@ DEFAULT()
* Push (true, exception) pair for finally to indicate that [retsub]
* should rethrow the exception.
*/
RootedValue &exception = rootValue0;
if (!cx->getPendingException(&exception)) {
interpReturnOK = false;
goto return_continuation;
}
PUSH_BOOLEAN(true);
PUSH_COPY(cx->getPendingException());
PUSH_COPY(exception);
cx->clearPendingException();
ADVANCE_AND_DISPATCH(0);
}
@ -3683,9 +3690,9 @@ js::GetAndClearException(JSContext *cx, MutableHandleValue res)
if (cx->runtime()->interrupt && !js_HandleExecutionInterrupt(cx))
return false;
res.set(cx->getPendingException());
bool status = cx->getPendingException(res);
cx->clearPendingException();
return true;
return status;
}
template <bool strict>

View File

@ -632,8 +632,10 @@ GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc
bool wasThrowing = cx->isExceptionPending();
RootedValue lastException(cx, UndefinedValue());
if (wasThrowing)
lastException = cx->getPendingException();
if (wasThrowing) {
if (!cx->getPendingException(&lastException))
return false;
}
cx->clearPendingException();
Rooted<jsid> id(cx, shape->propid());
@ -644,7 +646,9 @@ GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc
pd->value = JSVAL_VOID;
} else {
pd->flags = JSPD_EXCEPTION;
pd->value = cx->getPendingException();
if (!cx->getPendingException(&value))
return false;
pd->value = value;
}
} else {
pd->flags = 0;