Issue #2089 - Use JS engine stack if necessary when reporting errors

Based-on: m-c 996060
This commit is contained in:
Martok 2023-01-21 02:13:01 +01:00 • committed by roytam1
commit 6b50dd5d06
21 changed files with 209 additions and 69 deletions

View file

@ -224,16 +224,23 @@ ProcessNameForCollectorLog()
namespace xpc {
// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
// Exceptions.
// This handles JS Exceptions (via ExceptionStackOrNull), DOM and XPC
// Exceptions, and arbitrary values that were associated with a stack by the
// JS engine when they were thrown, as specified by exceptionStack.
//
// Note that the returned object is _not_ wrapped into the compartment of
// exceptionValue.
JSObject*
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
JS::HandleValue exceptionValue)
JS::HandleValue exceptionValue,
JS::HandleObject exceptionStack)
{
if (!exceptionValue.isObject()) {
// Use the stack provided by the JS engine, if available. This will not be
// a wrapper.
if (exceptionStack) {
return exceptionStack;
}
return nullptr;
}
@ -257,6 +264,10 @@ FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
// Not a DOM Exception, try XPC Exception.
UNWRAP_OBJECT(Exception, exceptionObject, exception);
if (!exception) {
// As above, use the stack provided by the JS engine, if available.
if (exceptionStack) {
return exceptionStack;
}
return nullptr;
}
}
@ -421,10 +432,12 @@ public:
ScriptErrorEvent(nsPIDOMWindowInner* aWindow,
JS::RootingContext* aRootingCx,
xpc::ErrorReport* aReport,
JS::Handle<JS::Value> aError)
JS::Handle<JS::Value> aError,
JS::Handle<JSObject*> aErrorStack)
: mWindow(aWindow)
, mReport(aReport)
, mError(aRootingCx, aError)
, mErrorStack(aRootingCx, aErrorStack)
{}
NS_IMETHOD Run() override
@ -471,7 +484,7 @@ public:
if (status != nsEventStatus_eConsumeNoDefault) {
JS::Rooted<JSObject*> stack(rootingCx,
xpc::FindExceptionStackForConsoleReport(win, mError));
xpc::FindExceptionStackForConsoleReport(win, mError, mErrorStack));
mReport->LogToConsoleWithStack(stack);
}
@ -481,7 +494,8 @@ public:
private:
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<xpc::ErrorReport> mReport;
JS::PersistentRootedValue mError;
JS::PersistentRootedValue mError;
JS::PersistentRootedObject mErrorStack;
static bool sHandlingScriptError;
};
@ -494,9 +508,10 @@ namespace xpc {
void
DispatchScriptErrorEvent(nsPIDOMWindowInner *win, JS::RootingContext* rootingCx,
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception)
xpc::ErrorReport *xpcReport, JS::Handle<JS::Value> exception,
JS::Handle<JSObject*> exceptionStack)
{
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception));
nsContentUtils::AddScriptRunner(new ScriptErrorEvent(win, rootingCx, xpcReport, exception, exceptionStack));
}
} /* namespace xpc */

View file

@ -577,8 +577,9 @@ AutoJSAPI::ReportException()
}
JSAutoCompartment ac(cx(), errorGlobal);
JS::Rooted<JS::Value> exn(cx());
JS::Rooted<JSObject*> exnStack(cx());
js::ErrorReport jsReport(cx());
if (StealException(&exn) &&
if (StealExceptionAndStack(&exn, &exnStack) &&
jsReport.init(cx(), exn, js::ErrorReport::WithSideEffects)) {
if (mIsMainThread) {
RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
@ -595,10 +596,10 @@ AutoJSAPI::ReportException()
inner ? inner->WindowID() : 0);
if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
JS::RootingContext* rcx = JS::RootingContext::get(cx());
DispatchScriptErrorEvent(inner, rcx, xpcReport, exn);
DispatchScriptErrorEvent(inner, rcx, xpcReport, exn, exnStack);
} else {
JS::Rooted<JSObject*> stack(cx(),
xpc::FindExceptionStackForConsoleReport(inner, exn));
xpc::FindExceptionStackForConsoleReport(inner, exn, exnStack));
xpcReport->LogToConsoleWithStack(stack);
}
} else {
@ -638,9 +639,16 @@ AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
bool
AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
{
JS::Rooted<JSObject*> stack(cx());
return StealExceptionAndStack(aVal, &stack);
}
bool AutoJSAPI::StealExceptionAndStack(JS::MutableHandle<JS::Value> aVal,
JS::MutableHandle<JSObject*> aStack) {
if (!PeekException(aVal)) {
return false;
}
aStack.set(JS::GetPendingExceptionStack(cx()));
JS_ClearPendingException(cx());
return true;
}

View file

@ -274,6 +274,12 @@ public:
// into the current compartment.
MOZ_MUST_USE bool StealException(JS::MutableHandle<JS::Value> aVal);
// As for StealException(), but put the saved frames for any stack trace
// associated with the point the exception was thrown into aStack.
// aVal will be in the current compartment, but aStack might not be.
MOZ_MUST_USE bool StealExceptionAndStack(JS::MutableHandle<JS::Value> aVal,
JS::MutableHandle<JSObject*> aStack);
// Peek the current exception from the JS engine, without stealing it.
// Callers must ensure that HasException() is true, and that cx() is in a
// non-null compartment.

View file

@ -941,7 +941,7 @@ HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr, bool* mus
return jit::DebugEpilogue(cx, frame, pc, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
return false;
default:

View file

@ -6219,12 +6219,16 @@ JS_GetPendingException(JSContext* cx, MutableHandleValue vp)
}
JS_PUBLIC_API(void)
JS_SetPendingException(JSContext* cx, HandleValue value)
JS_SetPendingException(JSContext* cx, HandleValue value, JS::ExceptionStackBehavior behavior)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
releaseAssertSameCompartment(cx, value);
cx->setPendingException(value);
if (behavior == JS::ExceptionStackBehavior::Capture) {
cx->setPendingExceptionAndCaptureStack(value);
} else {
cx->setPendingException(value, nullptr);
}
}
JS_PUBLIC_API(void)
@ -6234,12 +6238,20 @@ JS_ClearPendingException(JSContext* cx)
cx->clearPendingException();
}
JS_PUBLIC_API(JSObject*)
JS::GetPendingExceptionStack(JSContext* cx)
{
AssertHeapIsIdle(cx);
return cx->getPendingExceptionStack();
}
JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx)
: context(cx),
wasPropagatingForcedReturn(cx->propagatingForcedReturn_),
wasOverRecursed(cx->overRecursed_),
wasThrowing(cx->throwing),
exceptionValue(cx)
exceptionValue(cx),
exceptionStack(cx)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
@ -6249,10 +6261,21 @@ JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx)
cx->overRecursed_ = false;
if (wasThrowing) {
exceptionValue = cx->unwrappedException_;
exceptionStack = cx->unwrappedExceptionStack_;
cx->clearPendingException();
}
}
void
JS::AutoSaveExceptionState::drop()
{
wasPropagatingForcedReturn = false;
wasOverRecursed = false;
wasThrowing = false;
exceptionValue.setUndefined();
exceptionStack = nullptr;
}
void
JS::AutoSaveExceptionState::restore()
{
@ -6260,6 +6283,9 @@ JS::AutoSaveExceptionState::restore()
context->overRecursed_ = wasOverRecursed;
context->throwing = wasThrowing;
context->unwrappedException_ = exceptionValue;
if (exceptionStack) {
context->unwrappedExceptionStack_ = &exceptionStack->as<SavedFrame>();
}
drop();
}
@ -6272,6 +6298,9 @@ JS::AutoSaveExceptionState::~AutoSaveExceptionState()
context->overRecursed_ = wasOverRecursed;
context->throwing = true;
context->unwrappedException_ = exceptionValue;
if (exceptionStack) {
context->unwrappedExceptionStack_ = &exceptionStack->as<SavedFrame>();
}
}
}
}

View file

@ -5762,8 +5762,22 @@ JS_IsExceptionPending(JSContext* cx);
extern JS_PUBLIC_API(bool)
JS_GetPendingException(JSContext* cx, JS::MutableHandleValue vp);
namespace JS {
enum class ExceptionStackBehavior: bool {
// Do not capture any stack.
DoNotCapture,
// Capture the current JS stack when setting the exception. It may be
// retrieved by JS::GetPendingExceptionStack.
Capture
};
} // namespace JS
extern JS_PUBLIC_API(void)
JS_SetPendingException(JSContext* cx, JS::HandleValue v);
JS_SetPendingException(JSContext* cx, JS::HandleValue v,
JS::ExceptionStackBehavior behavior = JS::ExceptionStackBehavior::Capture);
extern JS_PUBLIC_API(void)
JS_ClearPendingException(JSContext* cx);
@ -5790,6 +5804,7 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
bool wasOverRecursed;
bool wasThrowing;
RootedValue exceptionValue;
RootedObject exceptionStack;
public:
/*
@ -5808,12 +5823,7 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
* Discard any stored exception state.
* If this is called, the destructor is a no-op.
*/
void drop() {
wasPropagatingForcedReturn = false;
wasOverRecursed = false;
wasThrowing = false;
exceptionValue.setUndefined();
}
void drop();
/*
* Replace cx's exception state with the stored exception state. Then
@ -5823,6 +5833,18 @@ class JS_PUBLIC_API(AutoSaveExceptionState)
void restore();
};
/**
* Get the SavedFrame stack object captured when the pending exception was set
* on the JSContext. This fuzzily correlates with a `throw` statement in JS,
* although arbitrary JSAPI consumers or VM code may also set pending exceptions
* via `JS_SetPendingException`.
*
* This is not the same stack as `e.stack` when `e` is an `Error` object. (That
* would be JS::ExceptionStackOrNull).
*/
MOZ_MUST_USE JS_PUBLIC_API(JSObject*)
GetPendingExceptionStack(JSContext* cx);
} /* namespace JS */
/* Deprecated API. Use AutoSaveExceptionState instead. */

View file

@ -241,7 +241,8 @@ js::ReportOutOfMemory(ExclusiveContext* cxArg)
if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
oomCallback(cx, cx->runtime()->oomCallbackData);
cx->setPendingException(StringValue(cx->names().outOfMemory));
RootedValue oomMessage(cx, StringValue(cx->names().outOfMemory));
cx->setPendingException(oomMessage, nullptr);
}
void
@ -1013,6 +1014,7 @@ JSContext::JSContext(JSRuntime* parentRuntime)
JSRuntime(parentRuntime),
throwing(false),
unwrappedException_(this),
unwrappedExceptionStack_(this),
overRecursed_(false),
propagatingForcedReturn_(false),
liveVolatileJitFrameIterators_(nullptr),
@ -1038,6 +1040,24 @@ JSContext::~JSContext()
MOZ_ASSERT(!resolvingList);
}
void
JSContext::setPendingExceptionAndCaptureStack(HandleValue value)
{
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
RootedObject stack(this);
if (!CaptureCurrentStack(this, &stack, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)))) {
clearPendingException();
}
RootedSavedFrame nstack(this);
if (stack) {
nstack = &stack->as<SavedFrame>();
}
setPendingException(value, nstack);
}
bool
JSContext::getPendingException(MutableHandleValue rval)
{
@ -1045,16 +1065,23 @@ JSContext::getPendingException(MutableHandleValue rval)
rval.set(unwrappedException_);
if (IsAtomsCompartment(compartment()))
return true;
RootedSavedFrame stack(this, unwrappedExceptionStack_);
bool wasOverRecursed = overRecursed_;
clearPendingException();
if (!compartment()->wrap(this, rval))
return false;
assertSameCompartment(this, rval);
setPendingException(rval);
setPendingException(rval, stack);
overRecursed_ = wasOverRecursed;
return true;
}
SavedFrame*
JSContext::getPendingExceptionStack()
{
return unwrappedExceptionStack_;
}
bool
JSContext::isThrowingOutOfMemory()
{

View file

@ -369,6 +369,7 @@ struct JSContext : public js::ExclusiveContext,
/* Exception state -- the exception member is a GC root by definition. */
bool throwing; /* is there a pending exception? */
JS::PersistentRooted<JS::Value> unwrappedException_; /* most-recently-thrown exception */
JS::PersistentRooted<js::SavedFrame*> unwrappedExceptionStack_; /* stack when the exception was thrown */
// True if the exception currently being thrown is by result of
// ReportOverRecursed. See Debugger::slowPathOnExceptionUnwind.
@ -495,17 +496,21 @@ struct JSContext : public js::ExclusiveContext,
MOZ_MUST_USE
bool getPendingException(JS::MutableHandleValue rval);
js::SavedFrame* getPendingExceptionStack();
bool isThrowingOutOfMemory();
bool isThrowingDebuggeeWouldRun();
bool isClosingGenerator();
void setPendingException(const js::Value& v);
void setPendingException(JS::HandleValue v, js::HandleSavedFrame stack);
void setPendingExceptionAndCaptureStack(JS::HandleValue v);
void clearPendingException() {
throwing = false;
overRecursed_ = false;
unwrappedException_.setUndefined();
unwrappedExceptionStack_ = nullptr;
}
bool isThrowingOverRecursed() const { return throwing && overRecursed_; }

View file

@ -368,12 +368,13 @@ ExclusiveContext::typeLifoAlloc()
} /* namespace js */
inline void
JSContext::setPendingException(const js::Value& v)
JSContext::setPendingException(JS::HandleValue v, js::HandleSavedFrame stack)
{
// overRecursed_ is set after the fact by ReportOverRecursed.
this->overRecursed_ = false;
this->throwing = true;
this->unwrappedException_ = v;
this->unwrappedExceptionStack_ = stack;
// We don't use assertSameCompartment here to allow
// js::SetPendingExceptionCrossContext to work.
MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());

View file

@ -361,13 +361,13 @@ struct SuppressErrorsGuard
}
};
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
static bool
CaptureStack(JSContext* cx, MutableHandleObject stack)
{
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
return CaptureCurrentStack(cx, stack,
JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)));
}
@ -699,7 +699,12 @@ js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
return;
// Throw it.
cx->setPendingException(ObjectValue(*errObject));
RootedValue errValue(cx, ObjectValue(*errObject));
RootedSavedFrame nstack(cx);
if (stack) {
nstack = &stack->as<SavedFrame>();
}
cx->setPendingException(errValue, nstack);
// Flag the error report passed in to indicate an exception was raised.
reportp->flags |= JSREPORT_EXCEPTION;

View file

@ -971,8 +971,10 @@ js::ThrowStopIteration(JSContext* cx)
// StopIteration isn't a constructor, but it's stored in GlobalObject
// as one, out of laziness. Hence the GetBuiltinConstructor call here.
RootedObject ctor(cx);
if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
cx->setPendingException(ObjectValue(*ctor));
if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor)) {
RootedValue ctorval(cx, ObjectValue(*ctor));
cx->setPendingExceptionAndCaptureStack(ctorval);
}
return false;
}
@ -1261,12 +1263,13 @@ js::UnwindIteratorForException(JSContext* cx, HandleObject obj)
{
RootedValue v(cx);
bool getOk = cx->getPendingException(&v);
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
cx->clearPendingException();
if (!CloseIterator(cx, obj))
return false;
if (!getOk)
return false;
cx->setPendingException(v);
cx->setPendingException(v, stack);
return true;
}

View file

@ -408,12 +408,15 @@ ErrorCopier::~ErrorCopier()
{
RootedValue exc(cx);
if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
cx->clearPendingException();
ac.reset();
Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
JSObject* copyobj = CopyErrorObject(cx, errObj);
if (copyobj)
cx->setPendingException(ObjectValue(*copyobj));
if (copyobj) {
RootedValue rootedCopy(cx, ObjectValue(*copyobj));
cx->setPendingException(rootedCopy, stack);
}
}
}
}

View file

@ -848,7 +848,7 @@ Debugger::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
case JSTRAP_ERROR:
@ -960,7 +960,7 @@ Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode
return true;
case JSTRAP_THROW:
cx->setPendingException(value);
cx->setPendingExceptionAndCaptureStack(value);
return false;
case JSTRAP_ERROR:
@ -993,7 +993,7 @@ Debugger::slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
default:
@ -1028,7 +1028,7 @@ Debugger::slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
break;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
break;
case JSTRAP_ERROR:
@ -1321,7 +1321,7 @@ public:
bool operator()(JSContext* cx) override
{
cx->setPendingException(exn_);
cx->setPendingExceptionAndCaptureStack(exn_);
return false;
}
@ -1753,6 +1753,7 @@ Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
MOZ_ASSERT(hook->isCallable());
RootedValue exc(cx);
RootedSavedFrame stack(cx, cx->getPendingExceptionStack());
if (!cx->getPendingException(&exc))
return JSTRAP_ERROR;
cx->clearPendingException();
@ -1772,7 +1773,7 @@ Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
JSTrapStatus st = processHandlerResult(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
if (st == JSTRAP_CONTINUE)
cx->setPendingException(exc);
cx->setPendingException(exc, stack);
return st;
}
@ -2005,13 +2006,7 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
* onStep handlers mess with that (other than by returning a resumption
* value).
*/
RootedValue exception(cx, UndefinedValue());
bool exceptionPending = cx->isExceptionPending();
if (exceptionPending) {
if (!cx->getPendingException(&exception))
return JSTRAP_ERROR;
cx->clearPendingException();
}
JS::AutoSaveExceptionState savedExc(cx);
/*
* Build list of Debugger.Frame instances referring to this frame with
@ -2070,13 +2065,13 @@ Debugger::onSingleStep(JSContext* cx, MutableHandleValue vp)
bool ok = js::Call(cx, fval, frame, &rval);
JSTrapStatus st = dbg->processHandlerResult(ac, ok, rval, iter.abstractFramePtr(),
iter.pc(), vp);
if (st != JSTRAP_CONTINUE)
if (st != JSTRAP_CONTINUE) {
savedExc.drop();
return st;
}
}
vp.setUndefined();
if (exceptionPending)
cx->setPendingException(exception);
return JSTRAP_CONTINUE;
}

View file

@ -158,9 +158,12 @@ ForOfIterator::closeThrow()
MOZ_ASSERT(iterator);
RootedValue completionException(cx_);
RootedSavedFrame completionExceptionStack(cx_);
if (cx_->isExceptionPending()) {
if (!GetAndClearException(cx_, &completionException))
if (!GetAndClearExceptionAndStack(cx_, &completionException, &completionExceptionStack)) {
completionException.setUndefined();
completionExceptionStack = nullptr;
}
}
// Steps 1-2 (implicit)
@ -172,7 +175,7 @@ ForOfIterator::closeThrow()
// Step 4.
if (returnVal.isUndefined()) {
cx_->setPendingException(completionException);
cx_->setPendingException(completionException, completionExceptionStack);
return;
}
@ -195,7 +198,7 @@ ForOfIterator::closeThrow()
}
// Step 6.
cx_->setPendingException(completionException);
cx_->setPendingException(completionException, completionExceptionStack);
// Steps 7-9 (skipped).
return;

View file

@ -131,7 +131,7 @@ js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<Generato
HandleValue arg, uint32_t resumeKind)
{
if (resumeKind == GeneratorObject::THROW) {
cx->setPendingException(arg);
cx->setPendingExceptionAndCaptureStack(arg);
genObj->setRunning();
} else {
MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
@ -143,7 +143,8 @@ js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<Generato
MOZ_ASSERT(arg.isUndefined());
}
cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
cx->setPendingException(closing, nullptr);
genObj->setClosing();
}
return false;

View file

@ -1883,7 +1883,7 @@ CASE(EnableInterruptsPseudoOpcode)
goto error;
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
goto error;
default:;
}
@ -1905,7 +1905,7 @@ CASE(EnableInterruptsPseudoOpcode)
goto error;
goto successful_return_continuation;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
goto error;
default:
break;
@ -3825,7 +3825,8 @@ CASE(JSOP_RETSUB)
* be necessary, but it seems clearer. And it points out a FIXME:
* 350509, due to Igor Bukanov.
*/
cx->setPendingException(rval);
ReservedRooted<Value> v(&rootValue0, rval);
cx->setPendingExceptionAndCaptureStack(v);
goto error;
}
MOZ_ASSERT(rval.isInt32());
@ -4331,7 +4332,7 @@ bool
js::Throw(JSContext* cx, HandleValue v)
{
MOZ_ASSERT(!cx->isExceptionPending());
cx->setPendingException(v);
cx->setPendingExceptionAndCaptureStack(v);
return false;
}
@ -4342,7 +4343,7 @@ js::ThrowingOperation(JSContext* cx, HandleValue v)
// execution instead of calling the (JIT) exception handler.
MOZ_ASSERT(!cx->isExceptionPending());
cx->setPendingException(v);
cx->setPendingExceptionAndCaptureStack(v);
return true;
}
@ -4548,16 +4549,25 @@ js::ThrowMsgOperation(JSContext* cx, const unsigned errorNum)
}
bool
js::GetAndClearException(JSContext* cx, MutableHandleValue res)
js::GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res,
MutableHandleSavedFrame stack)
{
if (!cx->getPendingException(res))
return false;
stack.set(cx->getPendingExceptionStack());
cx->clearPendingException();
// Allow interrupting deeply nested exception handling.
return CheckForInterrupt(cx);
}
bool
js::GetAndClearException(JSContext* cx, MutableHandleValue res)
{
RootedSavedFrame stack(cx);
return GetAndClearExceptionAndStack(cx, res, &stack);
}
template <bool strict>
bool
js::DeletePropertyJit(JSContext* cx, HandleValue v, HandlePropertyName name, bool* bp)

View file

@ -489,6 +489,9 @@ ThrowMsgOperation(JSContext* cx, const unsigned errorNum);
bool
GetAndClearException(JSContext* cx, MutableHandleValue res);
bool
GetAndClearExceptionAndStack(JSContext* cx, MutableHandleValue res, MutableHandleSavedFrame stack);
bool
DeleteNameOperation(JSContext* cx, HandlePropertyName name, HandleObject scopeObj,
MutableHandleValue res);

View file

@ -548,7 +548,7 @@ InvokeInterruptCallback(JSContext* cx)
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
cx->setPendingExceptionAndCaptureStack(rval);
return false;
default:;
}

View file

@ -1729,7 +1729,8 @@ RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
return false;
RootedValue rejectionValue(cx);
if (!GetAndClearException(cx, &rejectionValue))
RootedSavedFrame stack(cx);
if (!GetAndClearExceptionAndStack(cx, &rejectionValue, &stack))
return false;
return PromiseObject::reject(cx, promise, rejectionValue);

View file

@ -2310,7 +2310,7 @@ nsXPCComponents_Utils::ReportError(HandleValue error, JSContext* cx)
if (errorObj) {
JS::RootedObject stackVal(cx,
FindExceptionStackForConsoleReport(win, error));
FindExceptionStackForConsoleReport(win, error, nullptr));
if (stackVal) {
scripterr = new nsScriptErrorWithStack(stackVal);
}

View file

@ -590,11 +590,13 @@ class ErrorReport : public ErrorBase {
void
DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx,
xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception);
xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception,
JS::Handle<JSObject*> exceptionStack);
// Get a stack of the sort that can be passed to
// xpc::ErrorReport::LogToConsoleWithStack from the given exception value. Can
// return null if the exception value doesn't have an associated stack. The
// be nullptr if the exception value doesn't have an associated stack, and if
// there is no stack supplied by the JS engine in exceptionStack. The
// returned stack, if any, may also not be in the same compartment as
// exceptionValue.
//
@ -605,7 +607,8 @@ DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx,
// the stack in the console message keeping the window alive.
JSObject*
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
JS::HandleValue exceptionValue);
JS::HandleValue exceptionValue,
JS::HandleObject exceptionStack);
// Return a name for the compartment.
// This function makes reasonable efforts to make this name both mostly human-readable