mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1626100 - Introduce an ExceptionStack class and StealPendingExceptionStack. r=sfink,mccr8
See also the bug for a more detailed description. Differential Revision: https://phabricator.services.mozilla.com/D69636 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
882077ccb1
commit
fae4303276
@ -8,6 +8,7 @@
|
||||
|
||||
#include "AudioParamMap.h"
|
||||
#include "js/Array.h" // JS::{Get,Set}ArrayLength, JS::NewArrayLength
|
||||
#include "js/Exception.h"
|
||||
#include "mozilla/dom/AudioWorkletNodeBinding.h"
|
||||
#include "mozilla/dom/AudioParamMapBinding.h"
|
||||
#include "mozilla/dom/RootedDictionary.h"
|
||||
@ -197,26 +198,24 @@ void WorkletNodeEngine::SendProcessorError(AudioNodeTrack* aTrack,
|
||||
ReleaseJSResources();
|
||||
// The processor errored out while getting a context, try to tell the node
|
||||
// anyways.
|
||||
if (!aCx) {
|
||||
if (!aCx || !JS_IsExceptionPending(aCx)) {
|
||||
ProcessorErrorDetails details;
|
||||
details.mMessage.Assign(u"Unknown processor error");
|
||||
SendErrorToMainThread(aTrack, details);
|
||||
return;
|
||||
}
|
||||
|
||||
js::ErrorReport jsReport(aCx);
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
JS::Rooted<JSObject*> exnStack(aCx);
|
||||
if (JS_GetPendingException(aCx, &exn)) {
|
||||
exnStack.set(JS::GetPendingExceptionStack(aCx));
|
||||
JS_ClearPendingException(aCx);
|
||||
if (!jsReport.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
|
||||
JS::ExceptionStack exnStack(aCx);
|
||||
if (JS::StealPendingExceptionStack(aCx, &exnStack)) {
|
||||
js::ErrorReport jsReport(aCx);
|
||||
if (!jsReport.init(aCx, exnStack, js::ErrorReport::WithSideEffects)) {
|
||||
ProcessorErrorDetails details;
|
||||
details.mMessage.Assign(u"Unknown processor error");
|
||||
SendErrorToMainThread(aTrack, details);
|
||||
// Set the exception and stack back to have it in the console with a stack
|
||||
// trace.
|
||||
JS::SetPendingExceptionAndStack(aCx, exn, exnStack);
|
||||
JS::SetPendingExceptionAndStack(aCx, exnStack.exception(),
|
||||
exnStack.stack());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -235,7 +234,8 @@ void WorkletNodeEngine::SendProcessorError(AudioNodeTrack* aTrack,
|
||||
|
||||
// Set the exception and stack back to have it in the console with a stack
|
||||
// trace.
|
||||
JS::SetPendingExceptionAndStack(aCx, exn, exnStack);
|
||||
JS::SetPendingExceptionAndStack(aCx, exnStack.exception(),
|
||||
exnStack.stack());
|
||||
} else {
|
||||
NS_WARNING("No exception, but processor errored out?");
|
||||
}
|
||||
|
@ -556,8 +556,13 @@ bool AutoJSAPI::StealExceptionAndStack(JS::MutableHandle<JS::Value> aVal,
|
||||
if (!PeekException(aVal)) {
|
||||
return false;
|
||||
}
|
||||
aStack.set(JS::GetPendingExceptionStack(cx()));
|
||||
JS_ClearPendingException(cx());
|
||||
|
||||
JS::ExceptionStack exnStack(cx());
|
||||
if (!JS::StealPendingExceptionStack(cx(), &exnStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aStack.set(exnStack.stack());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/ContextOptions.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/LocaleSensitive.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
#include "js/SourceText.h"
|
||||
@ -4404,27 +4405,30 @@ void WorkerPrivate::ReportError(JSContext* aCx,
|
||||
data->mErrorHandlerRecursionCount == 1,
|
||||
"Bad recursion logic!");
|
||||
|
||||
JS::Rooted<JS::Value> exn(aCx);
|
||||
if (!JS_GetPendingException(aCx, &exn)) {
|
||||
// Probably shouldn't actually happen? But let's go ahead and just use null
|
||||
// for lack of anything better.
|
||||
exn.setNull();
|
||||
}
|
||||
JS::RootedObject exnStack(aCx, JS::GetPendingExceptionStack(aCx));
|
||||
JS_ClearPendingException(aCx);
|
||||
|
||||
UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>();
|
||||
if (aReport) {
|
||||
report->AssignErrorReport(aReport);
|
||||
}
|
||||
|
||||
JS::RootedObject stack(aCx), stackGlobal(aCx);
|
||||
xpc::FindExceptionStackForConsoleReport(nullptr, exn, exnStack, &stack,
|
||||
&stackGlobal);
|
||||
JS::ExceptionStack exnStack(aCx);
|
||||
if (JS_IsExceptionPending(aCx)) {
|
||||
if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack) {
|
||||
JSAutoRealm ar(aCx, stackGlobal);
|
||||
report->SerializeWorkerStack(aCx, this, stack);
|
||||
JS::RootedObject stack(aCx), stackGlobal(aCx);
|
||||
xpc::FindExceptionStackForConsoleReport(
|
||||
nullptr, exnStack.exception(), exnStack.stack(), &stack, &stackGlobal);
|
||||
|
||||
if (stack) {
|
||||
JSAutoRealm ar(aCx, stackGlobal);
|
||||
report->SerializeWorkerStack(aCx, this, stack);
|
||||
}
|
||||
} else {
|
||||
// ReportError is also used for reporting warnings,
|
||||
// so there won't be a pending exception.
|
||||
MOZ_ASSERT(aReport->isWarning());
|
||||
}
|
||||
|
||||
if (report->mMessage.IsEmpty() && aToStringResult) {
|
||||
@ -4453,7 +4457,7 @@ void WorkerPrivate::ReportError(JSContext* aCx,
|
||||
JS::CurrentGlobalOrNull(aCx);
|
||||
|
||||
WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr,
|
||||
std::move(report), 0, exn);
|
||||
std::move(report), 0, exnStack.exception());
|
||||
|
||||
data->mErrorHandlerRecursionCount--;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/ThreadEventQueue.h"
|
||||
#include "js/Exception.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -186,16 +187,17 @@ void WorkletJSContext::ReportError(JSErrorReport* aReport,
|
||||
RefPtr<AsyncErrorReporter> reporter = new AsyncErrorReporter(xpcReport);
|
||||
|
||||
JSContext* cx = Context();
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (JS_GetPendingException(cx, &exn)) {
|
||||
JS::Rooted<JSObject*> exnStack(cx, JS::GetPendingExceptionStack(cx));
|
||||
JS_ClearPendingException(cx);
|
||||
JS::Rooted<JSObject*> stack(cx);
|
||||
JS::Rooted<JSObject*> stackGlobal(cx);
|
||||
xpc::FindExceptionStackForConsoleReport(nullptr, exn, exnStack, &stack,
|
||||
&stackGlobal);
|
||||
if (stack) {
|
||||
reporter->SerializeStack(cx, stack);
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
JS::ExceptionStack exnStack(cx);
|
||||
if (JS::StealPendingExceptionStack(cx, &exnStack)) {
|
||||
JS::Rooted<JSObject*> stack(cx);
|
||||
JS::Rooted<JSObject*> stackGlobal(cx);
|
||||
xpc::FindExceptionStackForConsoleReport(nullptr, exnStack.exception(),
|
||||
exnStack.stack(), &stack,
|
||||
&stackGlobal);
|
||||
if (stack) {
|
||||
reporter->SerializeStack(cx, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
64
js/public/Exception.h
Normal file
64
js/public/Exception.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef js_Exception_h
|
||||
#define js_Exception_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include "jspubtd.h"
|
||||
#include "js/RootingAPI.h" // JS::{Handle,Rooted}
|
||||
#include "js/Value.h" // JS::Value, JS::Handle<JS::Value>
|
||||
|
||||
namespace JS {
|
||||
|
||||
// This class encapsulates a (pending) exception and the corresponding optional
|
||||
// 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).
|
||||
class MOZ_STACK_CLASS ExceptionStack {
|
||||
Rooted<Value> exception_;
|
||||
Rooted<JSObject*> stack_;
|
||||
|
||||
friend JS_PUBLIC_API bool GetPendingExceptionStack(
|
||||
JSContext* cx, JS::ExceptionStack* exceptionStack);
|
||||
|
||||
void init(HandleValue exception, HandleObject stack) {
|
||||
exception_ = exception;
|
||||
stack_ = stack;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ExceptionStack(JSContext* cx) : exception_(cx), stack_(cx) {}
|
||||
|
||||
ExceptionStack(JSContext* cx, HandleValue exception, HandleObject stack)
|
||||
: exception_(cx, exception), stack_(cx, stack) {}
|
||||
|
||||
HandleValue exception() const { return exception_; }
|
||||
|
||||
// |stack| can be null.
|
||||
HandleObject stack() const { return stack_; }
|
||||
};
|
||||
|
||||
// Get the current pending exception value and stack.
|
||||
// This function asserts that there is a pending exception.
|
||||
// If this function returns false, then retrieving the current pending exception
|
||||
// failed and might have been overwritten by a new exception.
|
||||
extern JS_PUBLIC_API bool GetPendingExceptionStack(
|
||||
JSContext* cx, JS::ExceptionStack* exceptionStack);
|
||||
|
||||
// Similar to GetPendingExceptionStack, but also clears the current
|
||||
// pending exception.
|
||||
extern JS_PUBLIC_API bool StealPendingExceptionStack(
|
||||
JSContext* cx, JS::ExceptionStack* exceptionStack);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif // js_Exception_h
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "builtin/TestingFunctions.h"
|
||||
#include "js/CompilationAndEvaluation.h" // JS::Evaluate
|
||||
#include "js/Exception.h"
|
||||
#include "js/SavedFrameAPI.h"
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "jsapi-tests/tests.h"
|
||||
@ -297,7 +298,7 @@ BEGIN_TEST(testSavedStacks_selfHostedFrames) {
|
||||
}
|
||||
END_TEST(testSavedStacks_selfHostedFrames)
|
||||
|
||||
BEGIN_TEST(test_JS_GetPendingExceptionStack) {
|
||||
BEGIN_TEST(test_GetPendingExceptionStack) {
|
||||
CHECK(js::DefineTestingFunctions(cx, global, false, false));
|
||||
|
||||
JSPrincipals* principals = cx->realm()->principals();
|
||||
@ -327,14 +328,15 @@ BEGIN_TEST(test_JS_GetPendingExceptionStack) {
|
||||
CHECK(JS_IsExceptionPending(cx));
|
||||
CHECK(val.isUndefined());
|
||||
|
||||
JS::RootedObject stack(cx, JS::GetPendingExceptionStack(cx));
|
||||
CHECK(stack);
|
||||
CHECK(stack->is<js::SavedFrame>());
|
||||
JS::Rooted<js::SavedFrame*> savedFrameStack(cx, &stack->as<js::SavedFrame>());
|
||||
JS::ExceptionStack exnStack(cx);
|
||||
CHECK(JS::GetPendingExceptionStack(cx, &exnStack));
|
||||
CHECK(exnStack.stack());
|
||||
CHECK(exnStack.stack()->is<js::SavedFrame>());
|
||||
JS::Rooted<js::SavedFrame*> savedFrameStack(
|
||||
cx, &exnStack.stack()->as<js::SavedFrame>());
|
||||
|
||||
JS_GetPendingException(cx, &val);
|
||||
CHECK(val.isInt32());
|
||||
CHECK(val.toInt32() == 5);
|
||||
CHECK(exnStack.exception().isInt32());
|
||||
CHECK(exnStack.exception().toInt32() == 5);
|
||||
|
||||
struct {
|
||||
uint32_t line;
|
||||
@ -392,4 +394,4 @@ BEGIN_TEST(test_JS_GetPendingExceptionStack) {
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(test_JS_GetPendingExceptionStack)
|
||||
END_TEST(test_GetPendingExceptionStack)
|
||||
|
@ -4956,12 +4956,6 @@ JS_PUBLIC_API void JS::SetPendingExceptionAndStack(JSContext* cx,
|
||||
cx->setPendingException(value, nstack);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::GetPendingExceptionStack(JSContext* cx) {
|
||||
AssertHeapIsIdle();
|
||||
CHECK_THREAD(cx);
|
||||
return cx->getPendingExceptionStack();
|
||||
}
|
||||
|
||||
JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext* cx)
|
||||
: context(cx),
|
||||
wasPropagatingForcedReturn(cx->propagatingForcedReturn_),
|
||||
|
@ -2651,18 +2651,6 @@ class JS_PUBLIC_API AutoSaveExceptionState {
|
||||
// must be a SavedFrame.
|
||||
JS_PUBLIC_API void SetPendingExceptionAndStack(JSContext* cx, HandleValue value,
|
||||
HandleObject stack);
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/Class.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Exception.h" // JS::ExceptionStack
|
||||
#include "js/SavedFrameAPI.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "js/Value.h"
|
||||
@ -467,6 +468,11 @@ ErrorReport::ErrorReport(JSContext* cx) : reportp(nullptr), exnObject(cx) {}
|
||||
|
||||
ErrorReport::~ErrorReport() = default;
|
||||
|
||||
bool ErrorReport::init(JSContext* cx, const JS::ExceptionStack& exnStack,
|
||||
SniffingBehavior sniffingBehavior) {
|
||||
return init(cx, exnStack.exception(), sniffingBehavior, exnStack.stack());
|
||||
}
|
||||
|
||||
bool ErrorReport::init(JSContext* cx, HandleValue exn,
|
||||
SniffingBehavior sniffingBehavior,
|
||||
HandleObject fallbackStack) {
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/Class.h"
|
||||
#include "js/ErrorReport.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
@ -44,6 +45,8 @@ struct JSJitInfo;
|
||||
namespace JS {
|
||||
template <class T>
|
||||
class Heap;
|
||||
|
||||
class ExceptionStack;
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
@ -1334,6 +1337,9 @@ struct MOZ_STACK_CLASS JS_FRIEND_API ErrorReport {
|
||||
SniffingBehavior sniffingBehavior,
|
||||
JS::HandleObject fallbackStack = nullptr);
|
||||
|
||||
bool init(JSContext* cx, const JS::ExceptionStack& exnStack,
|
||||
SniffingBehavior sniffingBehavior);
|
||||
|
||||
JSErrorReport* report() { return reportp; }
|
||||
|
||||
const JS::ConstUTF8CharsZ toStringResult() { return toStringResult_; }
|
||||
|
@ -141,6 +141,7 @@ EXPORTS.js += [
|
||||
'../public/Debug.h',
|
||||
'../public/Equality.h',
|
||||
'../public/ErrorReport.h',
|
||||
'../public/Exception.h',
|
||||
'../public/ForOfIterator.h',
|
||||
'../public/GCAnnotations.h',
|
||||
'../public/GCAPI.h',
|
||||
@ -306,6 +307,7 @@ UNIFIED_SOURCES += [
|
||||
'vm/EqualityOperations.cpp',
|
||||
'vm/ErrorObject.cpp',
|
||||
'vm/ErrorReporting.cpp',
|
||||
'vm/Exception.cpp',
|
||||
'vm/ForOfIterator.cpp',
|
||||
'vm/FrameIter.cpp',
|
||||
'vm/GeckoProfiler.cpp',
|
||||
|
@ -98,6 +98,7 @@
|
||||
#include "js/ContextOptions.h" // JS::ContextOptions{,Ref}
|
||||
#include "js/Debug.h"
|
||||
#include "js/Equality.h" // JS::SameValue
|
||||
#include "js/Exception.h" // JS::StealPendingExceptionStack
|
||||
#include "js/experimental/SourceHook.h" // js::{Set,Forget,}SourceHook
|
||||
#include "js/GCVector.h"
|
||||
#include "js/Initialization.h"
|
||||
@ -9579,15 +9580,12 @@ js::shell::AutoReportException::~AutoReportException() {
|
||||
}
|
||||
|
||||
// Get exception object and stack before printing and clearing exception.
|
||||
RootedValue exn(cx);
|
||||
(void)JS_GetPendingException(cx, &exn);
|
||||
RootedObject stack(cx, GetPendingExceptionStack(cx));
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
JS::ExceptionStack exnStack(cx);
|
||||
JS::StealPendingExceptionStack(cx, &exnStack);
|
||||
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
js::ErrorReport report(cx);
|
||||
if (!report.init(cx, exn, js::ErrorReport::WithSideEffects, stack)) {
|
||||
if (!report.init(cx, exnStack, js::ErrorReport::WithSideEffects)) {
|
||||
fprintf(stderr, "out of memory initializing ErrorReport\n");
|
||||
fflush(stderr);
|
||||
JS_ClearPendingException(cx);
|
||||
@ -9600,7 +9598,7 @@ js::shell::AutoReportException::~AutoReportException() {
|
||||
PrintError(cx, fp, report.toStringResult(), report.report(), reportWarnings);
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
if (!PrintStackTrace(cx, stack)) {
|
||||
if (!PrintStackTrace(cx, exnStack.stack())) {
|
||||
fputs("(Unable to print stack trace)\n", fp);
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "js/CompilationAndEvaluation.h" // JS::Evaluate
|
||||
#include "js/CompileOptions.h" // JS::CompileOptions
|
||||
#include "js/Exception.h" // JS::StealPendingExceptionStack
|
||||
#include "js/RootingAPI.h" // JS::Rooted
|
||||
#include "js/SourceText.h" // JS::Source{Ownership,Text}
|
||||
#include "js/Value.h" // JS::Value
|
||||
@ -34,20 +35,17 @@ static std::string gFuzzModuleName;
|
||||
|
||||
static void CrashOnPendingException() {
|
||||
if (JS_IsExceptionPending(gCx)) {
|
||||
JS::Rooted<JS::Value> exn(gCx);
|
||||
(void)JS_GetPendingException(gCx, &exn);
|
||||
JS::Rooted<JSObject*> stack(gCx, JS::GetPendingExceptionStack(gCx));
|
||||
|
||||
JS_ClearPendingException(gCx);
|
||||
JS::ExceptionStack exnStack(gCx);
|
||||
(void)JS::StealPendingExceptionStack(gCx, &exnStack);
|
||||
|
||||
js::ErrorReport report(gCx);
|
||||
if (!report.init(gCx, exn, js::ErrorReport::WithSideEffects)) {
|
||||
if (!report.init(gCx, exnStack, js::ErrorReport::WithSideEffects)) {
|
||||
fprintf(stderr, "out of memory initializing ErrorReport\n");
|
||||
fflush(stderr);
|
||||
} else {
|
||||
js::PrintError(gCx, stderr, report.toStringResult(), report.report(),
|
||||
js::shell::reportWarnings);
|
||||
if (!js::shell::PrintStackTrace(gCx, stack)) {
|
||||
if (!js::shell::PrintStackTrace(gCx, exnStack.stack())) {
|
||||
fputs("(Unable to print stack trace)\n", stderr);
|
||||
}
|
||||
}
|
||||
|
41
js/src/vm/Exception.cpp
Normal file
41
js/src/vm/Exception.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "js/Exception.h"
|
||||
|
||||
#include "jsapi.h" // AssertHeapIsIdle
|
||||
#include "vm/JSContext.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
bool JS::StealPendingExceptionStack(JSContext* cx,
|
||||
JS::ExceptionStack* exceptionStack) {
|
||||
if (!GetPendingExceptionStack(cx, exceptionStack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// "Steal" exception by clearing it.
|
||||
cx->clearPendingException();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JS::GetPendingExceptionStack(JSContext* cx,
|
||||
JS::ExceptionStack* exceptionStack) {
|
||||
AssertHeapIsIdle();
|
||||
CHECK_THREAD(cx);
|
||||
|
||||
MOZ_ASSERT(exceptionStack);
|
||||
MOZ_ASSERT(cx->isExceptionPending());
|
||||
|
||||
RootedValue exception(cx);
|
||||
if (!cx->getPendingException(&exception)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject stack(cx, cx->getPendingExceptionStack());
|
||||
exceptionStack->init(exception, stack);
|
||||
return true;
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
#include "WrapperFactory.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/Proxy.h"
|
||||
#include "js/Wrapper.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
@ -277,34 +278,35 @@ static void MaybeSanitizeException(JSContext* cx,
|
||||
// don't end up unnecessarily wrapping exceptions.
|
||||
{ // Scope for JSAutoRealm
|
||||
JSAutoRealm ar(cx, unwrappedFun);
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
// If JS_GetPendingException returns false, this was an uncatchable
|
||||
|
||||
JS::ExceptionStack exnStack(cx);
|
||||
// If JS::GetPendingExceptionStack returns false, this was an uncatchable
|
||||
// exception, or we somehow failed to wrap the exception into our
|
||||
// compartment. In either case, treating this as uncatchable exception,
|
||||
// by returning without setting any exception on the JSContext,
|
||||
// seems fine.
|
||||
if (!JS_GetPendingException(cx, &exn)) {
|
||||
if (!JS::GetPendingExceptionStack(cx, &exnStack)) {
|
||||
JS_ClearPendingException(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let through non-objects as-is, because some APIs rely on
|
||||
// that and accidental exceptions are never non-objects.
|
||||
if (!exn.isObject() ||
|
||||
if (!exnStack.exception().isObject() ||
|
||||
callerPrincipal->Subsumes(nsContentUtils::ObjectPrincipal(
|
||||
js::UncheckedUnwrap(&exn.toObject())))) {
|
||||
js::UncheckedUnwrap(&exnStack.exception().toObject())))) {
|
||||
// Just leave exn as-is.
|
||||
return;
|
||||
}
|
||||
|
||||
// Whoever we are throwing the exception to should not have access to
|
||||
// the exception. Sanitize it. First report the existing exception.
|
||||
JS::Rooted<JSObject*> exnStack(cx, JS::GetPendingExceptionStack(cx));
|
||||
// the exception. Sanitize it. First clear the existing exception.
|
||||
JS_ClearPendingException(cx);
|
||||
{ // Scope for AutoJSAPI
|
||||
AutoJSAPI jsapi;
|
||||
if (jsapi.Init(unwrappedFun)) {
|
||||
JS::SetPendingExceptionAndStack(cx, exn, exnStack);
|
||||
JS::SetPendingExceptionAndStack(cx, exnStack.exception(),
|
||||
exnStack.stack());
|
||||
}
|
||||
// If Init() fails, we can't report the exception, but oh, well.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user