зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1409852) for build bustage and failing test_js_dev_error_interceptor.js. r=backout on a CLOSED TREE
Backed out changeset 06368bf1a32c (bug 1409852) Backed out changeset 46fce9a2622d (bug 1409852) Backed out changeset 649d7bdf80ad (bug 1409852)
This commit is contained in:
Родитель
41dde8eaf3
Коммит
9743e10556
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/IdleDeadline.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
@ -434,32 +433,5 @@ ChromeUtils::IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
|
|||
aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
/* static */ void
|
||||
ChromeUtils::GetRecentJSDevError(GlobalObject& aGlobal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRetval.setUndefined();
|
||||
auto runtime = CycleCollectedJSRuntime::Get();
|
||||
MOZ_ASSERT(runtime);
|
||||
|
||||
auto cx = aGlobal.Context();
|
||||
if (!runtime->GetRecentDevError(cx, aRetval)) {
|
||||
aRv.NoteJSContextException(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::ClearRecentJSDevError(GlobalObject&)
|
||||
{
|
||||
auto runtime = CycleCollectedJSRuntime::Get();
|
||||
MOZ_ASSERT(runtime);
|
||||
|
||||
runtime->ClearRecentDevError();
|
||||
}
|
||||
#endif // NIGHTLY_BUILD
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -148,12 +148,6 @@ public:
|
|||
IdleRequestCallback& callback,
|
||||
const IdleRequestOptions& options,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void GetRecentJSDevError(GlobalObject& aGlobal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void ClearRecentJSDevError(GlobalObject& aGlobal);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -10992,7 +10992,7 @@ template <prototypes::ID PrototypeID, class NativeType, typename T>
|
|||
static Result<Ok, nsresult>
|
||||
ExtractExceptionValues(JSContext* aCx,
|
||||
JS::HandleObject aObj,
|
||||
nsAString& aSourceSpecOut,
|
||||
nsACString& aSourceSpecOut,
|
||||
uint32_t* aLineOut,
|
||||
uint32_t* aColumnOut,
|
||||
nsString& aMessageOut)
|
||||
|
@ -11000,8 +11000,10 @@ ExtractExceptionValues(JSContext* aCx,
|
|||
RefPtr<T> exn;
|
||||
MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn)));
|
||||
|
||||
exn->GetFilename(aCx, aSourceSpecOut);
|
||||
if (!aSourceSpecOut.IsEmpty()) {
|
||||
nsAutoString filename;
|
||||
exn->GetFilename(aCx, filename);
|
||||
if (!filename.IsEmpty()) {
|
||||
CopyUTF16toUTF8(filename, aSourceSpecOut);
|
||||
*aLineOut = exn->LineNumber(aCx);
|
||||
*aColumnOut = exn->ColumnNumber();
|
||||
}
|
||||
|
@ -11022,19 +11024,6 @@ nsContentUtils::ExtractErrorValues(JSContext* aCx,
|
|||
uint32_t* aLineOut,
|
||||
uint32_t* aColumnOut,
|
||||
nsString& aMessageOut)
|
||||
{
|
||||
nsAutoString sourceSpec;
|
||||
ExtractErrorValues(aCx, aValue, sourceSpec, aLineOut, aColumnOut, aMessageOut);
|
||||
CopyUTF16toUTF8(sourceSpec, aSourceSpecOut);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::ExtractErrorValues(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
nsAString& aSourceSpecOut,
|
||||
uint32_t* aLineOut,
|
||||
uint32_t* aColumnOut,
|
||||
nsString& aMessageOut)
|
||||
{
|
||||
MOZ_ASSERT(aLineOut);
|
||||
MOZ_ASSERT(aColumnOut);
|
||||
|
@ -11056,7 +11045,7 @@ nsContentUtils::ExtractErrorValues(JSContext* aCx,
|
|||
0); // window ID
|
||||
|
||||
if (!report->mFileName.IsEmpty()) {
|
||||
aSourceSpecOut = report->mFileName;
|
||||
CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
|
||||
*aLineOut = report->mLineNumber;
|
||||
*aColumnOut = report->mColumn;
|
||||
}
|
||||
|
|
|
@ -1134,14 +1134,6 @@ public:
|
|||
|
||||
static bool PrefetchPreloadEnabled(nsIDocShell* aDocShell);
|
||||
|
||||
static void
|
||||
ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
nsAString& aSourceSpecOut, uint32_t *aLineOut,
|
||||
uint32_t *aColumnOut, nsString& aMessageOut);
|
||||
|
||||
// Variant on `ExtractErrorValues` with a `nsACString`. This
|
||||
// method is provided for backwards compatibility. Prefer the
|
||||
// faster method above for your code.
|
||||
static void
|
||||
ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
||||
nsACString& aSourceSpecOut, uint32_t *aLineOut,
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
function errors() {
|
||||
return [
|
||||
// The following two errors MUST NOT be captured.
|
||||
new Error("This is an error: " + Math.random()),
|
||||
new RangeError("This is a RangeError: " + Math.random()),
|
||||
"This is a string: " + Math.random(),
|
||||
null,
|
||||
undefined,
|
||||
Math.random(),
|
||||
{},
|
||||
|
||||
// The following errors MUST be captured.
|
||||
new TypeError("This is a TypeError: " + Math.random()),
|
||||
new SyntaxError("This is a SyntaxError: " + Math.random()),
|
||||
new ReferenceError("This is a ReferenceError: " + Math.random())
|
||||
]
|
||||
}
|
||||
|
||||
function isDeveloperError(e) {
|
||||
if (e == null || typeof e != "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return e.constructor == TypeError
|
||||
|| e.constructor == SyntaxError
|
||||
|| e.constructor == ReferenceError;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
ChromeUtils.clearRecentJSDevError();
|
||||
Assert.equal(ChromeUtils.recentJSDevError, undefined);
|
||||
|
||||
for (let exn of errors()) {
|
||||
ChromeUtils.clearRecentJSDevError();
|
||||
try {
|
||||
throw exn;
|
||||
} catch (e) {
|
||||
// Discard error.
|
||||
}
|
||||
do_print(`Threw "${exn}", logged "${ChromeUtils.recentJSDevError}`);
|
||||
if (isDeveloperError(exn)) {
|
||||
Assert.equal(ChromeUtils.recentJSDevError.message, "" + exn);
|
||||
} else {
|
||||
Assert.equal(ChromeUtils.recentJSDevError, undefined);
|
||||
}
|
||||
ChromeUtils.clearRecentJSDevError();
|
||||
Assert.equal(ChromeUtils.recentJSDevError, undefined);
|
||||
}
|
||||
};
|
||||
|
|
@ -54,7 +54,4 @@ head = head_xml.js
|
|||
[test_chromeutils_base64.js]
|
||||
[test_generate_xpath.js]
|
||||
head = head_xml.js
|
||||
[test_js_dev_error_interceptor.js]
|
||||
# This feature is implemented only in NIGHTLY.
|
||||
run-if = nightly_build
|
||||
|
||||
|
|
|
@ -89,40 +89,6 @@ namespace ChromeUtils {
|
|||
ArrayBuffer base64URLDecode(ByteString string,
|
||||
Base64URLDecodeOptions options);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
|
||||
/**
|
||||
* If the chrome code has thrown a JavaScript Dev Error
|
||||
* in the current JSRuntime. the first such error, or `undefined`
|
||||
* otherwise.
|
||||
*
|
||||
* A JavaScript Dev Error is an exception thrown by JavaScript
|
||||
* code that matches both conditions:
|
||||
* - it was thrown by chrome code;
|
||||
* - it is either a `ReferenceError`, a `TypeError` or a `SyntaxError`.
|
||||
*
|
||||
* Such errors are stored regardless of whether they have been
|
||||
* caught.
|
||||
*
|
||||
* This mechanism is designed to help ensure that the code of
|
||||
* Firefox is free from Dev Errors, even if they are accidentally
|
||||
* caught by clients.
|
||||
*
|
||||
* The object returned is not an exception. It has fields:
|
||||
* - DOMString stack
|
||||
* - DOMString filename
|
||||
* - DOMString lineNumber
|
||||
* - DOMString message
|
||||
*/
|
||||
[Throws]
|
||||
readonly attribute any recentJSDevError;
|
||||
|
||||
/**
|
||||
* Reset `recentJSDevError` to `undefined` for the current JSRuntime.
|
||||
*/
|
||||
void clearRecentJSDevError();
|
||||
#endif // NIGHTLY_BUILD
|
||||
|
||||
/**
|
||||
* IF YOU ADD NEW METHODS HERE, MAKE SURE THEY ARE THREAD-SAFE.
|
||||
*/
|
||||
|
|
|
@ -363,7 +363,6 @@ GENERATED_WEBIDL_FILES = [
|
|||
]
|
||||
|
||||
PREPROCESSED_WEBIDL_FILES = [
|
||||
'ChromeUtils.webidl',
|
||||
'Navigator.webidl',
|
||||
'Node.webidl',
|
||||
'Window.webidl',
|
||||
|
@ -426,6 +425,7 @@ WEBIDL_FILES = [
|
|||
'CheckerboardReportService.webidl',
|
||||
'ChildNode.webidl',
|
||||
'ChromeNodeList.webidl',
|
||||
'ChromeUtils.webidl',
|
||||
'Client.webidl',
|
||||
'Clients.webidl',
|
||||
'ClipboardEvent.webidl',
|
||||
|
|
|
@ -137,13 +137,6 @@ if CONFIG['ENABLE_STREAMS']:
|
|||
'testReadableStream.cpp',
|
||||
]
|
||||
|
||||
|
||||
if CONFIG['NIGHTLY_BUILD']:
|
||||
# The Error interceptor only exists on Nightly.
|
||||
UNIFIED_SOURCES += [
|
||||
'testErrorInterceptor.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['JS_BUILD_BINAST'] and CONFIG['JS_STANDALONE']:
|
||||
# Standalone builds leave the source directory untouched,
|
||||
# which lets us run tests with the data files intact.
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
#include "jsapi.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
// Tests for JS_GetErrorInterceptorCallback and JS_SetErrorInterceptorCallback.
|
||||
|
||||
|
||||
namespace {
|
||||
const double EXN_VALUE = 3.14;
|
||||
|
||||
static JS::PersistentRootedString gLatestMessage;
|
||||
|
||||
// An interceptor that stores the error in `gLatestMessage`.
|
||||
struct SimpleInterceptor: JSErrorInterceptor {
|
||||
virtual void interceptError(JSContext* cx, const JS::Value& val) override {
|
||||
js::StringBuffer buffer(cx);
|
||||
if (!ValueToStringBuffer(cx, val, buffer))
|
||||
MOZ_CRASH("Could not convert to string buffer");
|
||||
gLatestMessage = buffer.finishString();
|
||||
if (!gLatestMessage)
|
||||
MOZ_CRASH("Could not convert to string");
|
||||
}
|
||||
};
|
||||
|
||||
bool equalStrings(JSContext* cx, JSString* a, JSString* b) {
|
||||
int32_t result = 0;
|
||||
if (!JS_CompareStrings(cx, a, b, &result))
|
||||
MOZ_CRASH("Could not compare strings");
|
||||
return result == 0;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_TEST(testErrorInterceptor)
|
||||
{
|
||||
// Run the following snippets.
|
||||
const char* SAMPLES[] = {
|
||||
"throw new Error('I am an Error')\0",
|
||||
"throw new TypeError('I am a TypeError')\0",
|
||||
"throw new ReferenceError('I am a ReferenceError')\0",
|
||||
"throw new SyntaxError('I am a SyntaxError')\0",
|
||||
"throw 5\0",
|
||||
"undefined[0]\0",
|
||||
"foo[0]\0",
|
||||
"b[\0",
|
||||
};
|
||||
// With the simpleInterceptor, we should end up with the following error:
|
||||
const char* TO_STRING[] = {
|
||||
"Error: I am an Error\0",
|
||||
"TypeError: I am a TypeError\0",
|
||||
"ReferenceError: I am a ReferenceError\0",
|
||||
"SyntaxError: I am a SyntaxError\0",
|
||||
"5\0",
|
||||
"TypeError: undefined has no properties\0",
|
||||
"ReferenceError: foo is not defined\0",
|
||||
"SyntaxError: expected expression, got end of script\0",
|
||||
};
|
||||
MOZ_ASSERT(mozilla::ArrayLength(SAMPLES) == mozilla::ArrayLength(TO_STRING));
|
||||
|
||||
|
||||
// Save original callback.
|
||||
JSErrorInterceptor* original = JS_GetErrorInterceptorCallback(cx->runtime());
|
||||
gLatestMessage.init(cx);
|
||||
|
||||
// Test without callback.
|
||||
JS_SetErrorInterceptorCallback(cx->runtime(), nullptr);
|
||||
CHECK(gLatestMessage == nullptr);
|
||||
|
||||
for (auto sample: SAMPLES) {
|
||||
if (execDontReport(sample, __FILE__, __LINE__))
|
||||
MOZ_CRASH("This sample should have failed");
|
||||
CHECK(JS_IsExceptionPending(cx));
|
||||
CHECK(gLatestMessage == nullptr);
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
// Test with callback.
|
||||
SimpleInterceptor simpleInterceptor;
|
||||
JS_SetErrorInterceptorCallback(cx->runtime(), &simpleInterceptor);
|
||||
|
||||
// Test that we return the right callback.
|
||||
CHECK_EQUAL(JS_GetErrorInterceptorCallback(cx->runtime()), &simpleInterceptor);
|
||||
|
||||
// This shouldn't cause any error.
|
||||
EXEC("function bar() {}");
|
||||
CHECK(gLatestMessage == nullptr);
|
||||
|
||||
// Test error throwing with a callback that succeeds.
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(SAMPLES); ++i) {
|
||||
// This should cause the appropriate error.
|
||||
if (execDontReport(SAMPLES[i], __FILE__, __LINE__))
|
||||
MOZ_CRASH("This sample should have failed");
|
||||
CHECK(JS_IsExceptionPending(cx));
|
||||
|
||||
// Check result of callback.
|
||||
CHECK(gLatestMessage != nullptr);
|
||||
CHECK(js::StringEqualsAscii(&gLatestMessage->asLinear(), TO_STRING[i]));
|
||||
|
||||
// Check the final error.
|
||||
JS::RootedValue exn(cx);
|
||||
CHECK(JS_GetPendingException(cx, &exn));
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
js::StringBuffer buffer(cx);
|
||||
CHECK(ValueToStringBuffer(cx, exn, buffer));
|
||||
CHECK(equalStrings(cx, buffer.finishString(), gLatestMessage));
|
||||
|
||||
// Cleanup.
|
||||
gLatestMessage = nullptr;
|
||||
}
|
||||
|
||||
// Test again without callback.
|
||||
JS_SetErrorInterceptorCallback(cx->runtime(), nullptr);
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(SAMPLES); ++i) {
|
||||
if (execDontReport(SAMPLES[i], __FILE__, __LINE__))
|
||||
MOZ_CRASH("This sample should have failed");
|
||||
CHECK(JS_IsExceptionPending(cx));
|
||||
|
||||
// Check that the callback wasn't called.
|
||||
CHECK(gLatestMessage == nullptr);
|
||||
|
||||
// Check the final error.
|
||||
JS::RootedValue exn(cx);
|
||||
CHECK(JS_GetPendingException(cx, &exn));
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
js::StringBuffer buffer(cx);
|
||||
CHECK(ValueToStringBuffer(cx, exn, buffer));
|
||||
CHECK(js::StringEqualsAscii(buffer.finishString(), TO_STRING[i]));
|
||||
|
||||
// Cleanup.
|
||||
gLatestMessage = nullptr;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
JS_SetErrorInterceptorCallback(cx->runtime(), original);
|
||||
gLatestMessage = nullptr;
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testErrorInterceptor)
|
|
@ -653,40 +653,6 @@ JS_SetCompartmentNameCallback(JSContext* cx, JSCompartmentNameCallback callback)
|
|||
cx->runtime()->compartmentNameCallback = callback;
|
||||
}
|
||||
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetErrorInterceptorCallback(JSRuntime* rt, JSErrorInterceptor* callback)
|
||||
{
|
||||
rt->errorInterception.interceptor = callback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSErrorInterceptor*)
|
||||
JS_GetErrorInterceptorCallback(JSRuntime* rt)
|
||||
{
|
||||
return rt->errorInterception.interceptor;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(Maybe<JSExnType>)
|
||||
JS_GetErrorType(const JS::Value& val)
|
||||
{
|
||||
// All errors are objects.
|
||||
if (!val.isObject())
|
||||
return mozilla::Nothing();
|
||||
|
||||
const JSObject& obj = val.toObject();
|
||||
|
||||
// All errors are `ErrorObject`.
|
||||
if (!obj.is<js::ErrorObject>()) {
|
||||
// Not one of the primitive errors.
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
const js::ErrorObject& err = obj.as<js::ErrorObject>();
|
||||
return mozilla::Some(err.type());
|
||||
}
|
||||
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetWrapObjectCallbacks(JSContext* cx, const JSWrapObjectCallbacks* callbacks)
|
||||
{
|
||||
|
|
|
@ -680,18 +680,6 @@ typedef void
|
|||
using JSExternalStringSizeofCallback =
|
||||
size_t (*)(JSString* str, mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
/**
|
||||
* Callback used to intercept JavaScript errors.
|
||||
*/
|
||||
struct JSErrorInterceptor {
|
||||
/**
|
||||
* This method is called whenever an error has been raised from JS code.
|
||||
*
|
||||
* This method MUST be infallible.
|
||||
*/
|
||||
virtual void interceptError(JSContext* cx, const JS::Value& error) = 0;
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
static MOZ_ALWAYS_INLINE JS::Value
|
||||
|
@ -1339,33 +1327,6 @@ JS_SetWrapObjectCallbacks(JSContext* cx, const JSWrapObjectCallbacks* callbacks)
|
|||
extern JS_PUBLIC_API(void)
|
||||
JS_SetExternalStringSizeofCallback(JSContext* cx, JSExternalStringSizeofCallback callback);
|
||||
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
|
||||
// Set a callback that will be called whenever an error
|
||||
// is thrown in this runtime. This is designed as a mechanism
|
||||
// for logging errors. Note that the VM makes no attempt to sanitize
|
||||
// the contents of the error (so it may contain private data)
|
||||
// or to sort out among errors (so it may not be the error you
|
||||
// are interested in or for the component in which you are
|
||||
// interested).
|
||||
//
|
||||
// If the callback sets a new error, this new error
|
||||
// will replace the original error.
|
||||
//
|
||||
// May be `nullptr`.
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetErrorInterceptorCallback(JSRuntime*, JSErrorInterceptor* callback);
|
||||
|
||||
extern JS_PUBLIC_API(JSErrorInterceptor*)
|
||||
JS_GetErrorInterceptorCallback(JSRuntime*);
|
||||
|
||||
// Examine a value to determine if it is one of the built-in Error types.
|
||||
// If so, return the error type.
|
||||
extern JS_PUBLIC_API(mozilla::Maybe<JSExnType>)
|
||||
JS_GetErrorType(const JS::Value& val);
|
||||
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_SetCompartmentPrivate(JSCompartment* compartment, void* data);
|
||||
|
||||
|
|
|
@ -434,31 +434,6 @@ JSContext::minorGC(JS::gcreason::Reason reason)
|
|||
inline void
|
||||
JSContext::setPendingException(const js::Value& v)
|
||||
{
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
do {
|
||||
// Do not intercept exceptions if we are already
|
||||
// in the exception interceptor. That would lead
|
||||
// to infinite recursion.
|
||||
if (this->runtime()->errorInterception.isExecuting)
|
||||
break;
|
||||
|
||||
// Check whether we have an interceptor at all.
|
||||
if (!this->runtime()->errorInterception.interceptor)
|
||||
break;
|
||||
|
||||
// Make sure that we do not call the interceptor from within
|
||||
// the interceptor.
|
||||
this->runtime()->errorInterception.isExecuting = true;
|
||||
|
||||
// The interceptor must be infallible.
|
||||
const mozilla::DebugOnly<bool> wasExceptionPending = this->isExceptionPending();
|
||||
this->runtime()->errorInterception.interceptor->interceptError(this, v);
|
||||
MOZ_ASSERT(wasExceptionPending == this->isExceptionPending());
|
||||
|
||||
this->runtime()->errorInterception.isExecuting = false;
|
||||
} while (false);
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
// overRecursed_ is set after the fact by ReportOverRecursed.
|
||||
this->overRecursed_ = false;
|
||||
this->throwing = true;
|
||||
|
|
|
@ -1081,30 +1081,6 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
void* wasmUnwindPC() const {
|
||||
return wasmUnwindPC_;
|
||||
}
|
||||
|
||||
public:
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
// Support for informing the embedding of any error thrown.
|
||||
// This mechanism is designed to let the embedding
|
||||
// log/report/fail in case certain errors are thrown
|
||||
// (e.g. SyntaxError, ReferenceError or TypeError
|
||||
// in critical code).
|
||||
struct ErrorInterceptionSupport {
|
||||
ErrorInterceptionSupport()
|
||||
: isExecuting(false)
|
||||
, interceptor(nullptr)
|
||||
{ }
|
||||
|
||||
// true if the error interceptor is currently executing,
|
||||
// false otherwise. Used to avoid infinite loops.
|
||||
bool isExecuting;
|
||||
|
||||
// if non-null, any call to `setPendingException`
|
||||
// in this runtime will trigger the call to `interceptor`
|
||||
JSErrorInterceptor* interceptor;
|
||||
};
|
||||
ErrorInterceptionSupport errorInterception;
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
};
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -97,11 +97,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// For performance reasons, we make the JS Dev Error Interceptor a Nightly-only feature.
|
||||
#define MOZ_JS_DEV_ERROR_INTERCEPTOR = 1
|
||||
#endif // NIGHTLY_BUILD
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -561,18 +556,11 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSContext* aCx)
|
|||
js::SetScriptEnvironmentPreparer(aCx, &mEnvironmentPreparer);
|
||||
|
||||
JS::dbg::SetDebuggerMallocSizeOf(aCx, moz_malloc_size_of);
|
||||
|
||||
#ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
JS_SetErrorInterceptorCallback(mJSRuntime, &mErrorInterceptor);
|
||||
#endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::Shutdown(JSContext* cx)
|
||||
{
|
||||
#ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
mErrorInterceptor.Shutdown(mJSRuntime);
|
||||
#endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
JS_RemoveExtraGCRootsTracer(cx, TraceBlackJS, this);
|
||||
JS_RemoveExtraGCRootsTracer(cx, TraceGrayJS, this);
|
||||
#ifdef DEBUG
|
||||
|
@ -582,9 +570,6 @@ CycleCollectedJSRuntime::Shutdown(JSContext* cx)
|
|||
|
||||
CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
|
||||
{
|
||||
#ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
mErrorInterceptor.Shutdown(mJSRuntime);
|
||||
#endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
MOZ_COUNT_DTOR(CycleCollectedJSRuntime);
|
||||
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
|
||||
MOZ_ASSERT(mShutdownCalled);
|
||||
|
@ -1572,113 +1557,3 @@ CycleCollectedJSRuntime::Get()
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
|
||||
namespace js {
|
||||
extern void DumpValue(const JS::Value& val);
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::ErrorInterceptor::Shutdown(JSRuntime* rt)
|
||||
{
|
||||
JS_SetErrorInterceptorCallback(rt, nullptr);
|
||||
mThrownError.reset();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
CycleCollectedJSRuntime::ErrorInterceptor::interceptError(JSContext* cx, const JS::Value& exn)
|
||||
{
|
||||
if (mThrownError) {
|
||||
// We already have an error, we don't need anything more.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nsContentUtils::ThreadsafeIsSystemCaller(cx)) {
|
||||
// We are only interested in chrome code.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto type = JS_GetErrorType(exn);
|
||||
if (!type) {
|
||||
// This is not one of the primitive error types.
|
||||
return;
|
||||
}
|
||||
|
||||
switch (*type) {
|
||||
case JSExnType::JSEXN_REFERENCEERR:
|
||||
case JSExnType::JSEXN_SYNTAXERR:
|
||||
case JSExnType::JSEXN_TYPEERR:
|
||||
break;
|
||||
default:
|
||||
// Not one of the errors we are interested in.
|
||||
return;
|
||||
}
|
||||
|
||||
// Now copy the details of the exception locally.
|
||||
// While copying the details of an exception could be expensive, in most runs,
|
||||
// this will be done at most once during the execution of the process, so the
|
||||
// total cost should be reasonable.
|
||||
JS::RootedValue value(cx, exn);
|
||||
|
||||
ErrorDetails details;
|
||||
details.mType = *type;
|
||||
// If `exn` isn't an exception object, `ExtractErrorValues` could end up calling
|
||||
// `toString()`, which could in turn end up throwing an error. While this should
|
||||
// work, we want to avoid that complex use case.
|
||||
// Fortunately, we have already checked above that `exn` is an exception object,
|
||||
// so nothing such should happen.
|
||||
nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage);
|
||||
|
||||
nsAutoCString stack;
|
||||
JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false);
|
||||
stack.Append(buf.get());
|
||||
CopyUTF8toUTF16(buf.get(), details.mStack);
|
||||
|
||||
mThrownError.emplace(Move(details));
|
||||
}
|
||||
|
||||
void
|
||||
CycleCollectedJSRuntime::ClearRecentDevError()
|
||||
{
|
||||
mErrorInterceptor.mThrownError.reset();
|
||||
}
|
||||
|
||||
bool
|
||||
CycleCollectedJSRuntime::GetRecentDevError(JSContext*cx, JS::MutableHandle<JS::Value> error)
|
||||
{
|
||||
if (!mErrorInterceptor.mThrownError) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a copy of the exception.
|
||||
JS::RootedObject obj(cx, JS_NewPlainObject(cx));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue message(cx);
|
||||
JS::RootedValue filename(cx);
|
||||
JS::RootedValue stack(cx);
|
||||
if (!ToJSValue(cx, mErrorInterceptor.mThrownError->mMessage, &message) ||
|
||||
!ToJSValue(cx, mErrorInterceptor.mThrownError->mFilename, &filename) ||
|
||||
!ToJSValue(cx, mErrorInterceptor.mThrownError->mStack, &stack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the object.
|
||||
const auto FLAGS = JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
||||
if (!JS_DefineProperty(cx, obj, "message", message, FLAGS) ||
|
||||
!JS_DefineProperty(cx, obj, "fileName", filename, FLAGS) ||
|
||||
!JS_DefineProperty(cx, obj, "lineNumber", mErrorInterceptor.mThrownError->mLine, FLAGS) ||
|
||||
!JS_DefineProperty(cx, obj, "stack", stack, FLAGS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass the result.
|
||||
error.setObject(*obj);
|
||||
return true;
|
||||
}
|
||||
#endif // MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
|
||||
#undef MOZ_JS_DEV_ERROR_INTERCEPTOR
|
||||
|
|
|
@ -328,11 +328,6 @@ public:
|
|||
void AddContext(CycleCollectedJSContext* aContext);
|
||||
void RemoveContext(CycleCollectedJSContext* aContext);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
bool GetRecentDevError(JSContext* aContext, JS::MutableHandle<JS::Value> aError);
|
||||
void ClearRecentDevError();
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
private:
|
||||
LinkedList<CycleCollectedJSContext> mContexts;
|
||||
|
||||
|
@ -377,37 +372,6 @@ private:
|
|||
#ifdef DEBUG
|
||||
bool mShutdownCalled;
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Implementation of the error interceptor.
|
||||
// Built on nightly only to avoid any possible performance impact on release
|
||||
|
||||
struct ErrorInterceptor final : public JSErrorInterceptor {
|
||||
virtual void interceptError(JSContext* cx, const JS::Value& val) override;
|
||||
void Shutdown(JSRuntime* rt);
|
||||
|
||||
// Copy of the details of the exception.
|
||||
// We store this rather than the exception itself to avoid dealing with complicated
|
||||
// garbage-collection scenarios, e.g. a JSContext being killed while we still hold
|
||||
// onto an exception thrown from it.
|
||||
struct ErrorDetails {
|
||||
nsString mFilename;
|
||||
nsString mMessage;
|
||||
nsString mStack;
|
||||
JSExnType mType;
|
||||
uint32_t mLine;
|
||||
uint32_t mColumn;
|
||||
};
|
||||
|
||||
// If we have encountered at least one developer error,
|
||||
// the first error we have encountered. Otherwise, or
|
||||
// if we have reset since the latest error, `None`.
|
||||
Maybe<ErrorDetails> mThrownError;
|
||||
};
|
||||
ErrorInterceptor mErrorInterceptor;
|
||||
|
||||
#endif // defined(NIGHTLY_BUILD)
|
||||
|
||||
};
|
||||
|
||||
void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
|
||||
|
|
Загрузка…
Ссылка в новой задаче