зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1441333: Part 3 - Add helper to create a JS error with a saved stack. r=bz
There's no standard way to create a JS error with full stack and location information from a saved stack. Since we need this functionality in order to reject promises with useful Error objects, this patch adds a simple helper to make that possible. MozReview-Commit-ID: FyGuo4UjfsQ --HG-- extra : rebase_source : 65ef11c56f23e04ea5eeb87b972bfc8e4867fdd2
This commit is contained in:
Родитель
597e2b745d
Коммит
69844a6fbb
|
@ -679,5 +679,68 @@ ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal, nsIPrincipal* aPrinc
|
|||
aRetval.set(js::GetFirstSubsumedSavedFrame(cx, principals, frame, kSkipSelfHosted));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
|
||||
JS::Handle<JSObject*> aStack,
|
||||
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv)
|
||||
{
|
||||
if (aStack && !JS::IsSavedFrame(aStack)) {
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
auto cleanup = MakeScopeExit([&]() {
|
||||
aRv.NoteJSContextException(cx);
|
||||
});
|
||||
|
||||
JS::RootedObject retVal(cx);
|
||||
{
|
||||
JS::RootedString fileName(cx, JS_GetEmptyString(cx));
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
JS::RootedObject stack(cx);
|
||||
if (aStack) {
|
||||
stack = UncheckedUnwrap(aStack);
|
||||
ac.emplace(cx, stack);
|
||||
|
||||
if (JS::GetSavedFrameLine(cx, stack, &line) != JS::SavedFrameResult::Ok ||
|
||||
JS::GetSavedFrameColumn(cx, stack, &column) != JS::SavedFrameResult::Ok ||
|
||||
JS::GetSavedFrameSource(cx, stack, &fileName) != JS::SavedFrameResult::Ok) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedString message(cx);
|
||||
{
|
||||
JS::RootedValue msgVal(cx);
|
||||
if (!xpc::NonVoidStringToJsval(cx, aMessage, &msgVal)) {
|
||||
return;
|
||||
}
|
||||
message = msgVal.toString();
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> err(cx);
|
||||
if (!JS::CreateError(cx, JSEXN_ERR, stack,
|
||||
fileName, line, column,
|
||||
nullptr, message, &err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(err.isObject());
|
||||
retVal = &err.toObject();
|
||||
}
|
||||
|
||||
if (aStack && !JS_WrapObject(cx, &retVal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup.release();
|
||||
aRetVal.set(retVal);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -170,6 +170,11 @@ public:
|
|||
static void
|
||||
GetCallerLocation(const GlobalObject& global, nsIPrincipal* principal,
|
||||
JS::MutableHandle<JSObject*> aRetval);
|
||||
|
||||
static void
|
||||
CreateError(const GlobalObject& global, const nsAString& message,
|
||||
JS::Handle<JSObject*> stack,
|
||||
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -301,6 +301,15 @@ partial namespace ChromeUtils {
|
|||
* exists on the call stack, returns null.
|
||||
*/
|
||||
object? getCallerLocation(Principal principal);
|
||||
|
||||
/**
|
||||
* Creates a JS Error object with the given message and stack.
|
||||
*
|
||||
* If a stack object is provided, the error object is created in the global
|
||||
* that it belongs to.
|
||||
*/
|
||||
[Throws]
|
||||
object createError(DOMString message, optional object? stack = null);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,4 +58,27 @@ add_task(async function() {
|
|||
Assert.throws(() => { Cu.reportError("Meh", {}); },
|
||||
err => err.result == Cr.NS_ERROR_INVALID_ARG,
|
||||
"reportError should throw when passed a non-SavedFrame object");
|
||||
|
||||
|
||||
// createError
|
||||
|
||||
Assert.throws(() => { ChromeUtils.createError("Meh", {}); },
|
||||
err => err.result == Cr.NS_ERROR_INVALID_ARG,
|
||||
"createError should throw when passed a non-SavedFrame object");
|
||||
|
||||
let cloned = Cu.cloneInto(frame, sandbox);
|
||||
let error = ChromeUtils.createError("Meh", cloned);
|
||||
|
||||
equal(String(cloned), String(frame),
|
||||
"Cloning a SavedStack preserves its stringification");
|
||||
|
||||
equal(Cu.getGlobalForObject(error), sandbox,
|
||||
"createError creates errors in the global of the SavedFrame");
|
||||
equal(error.stack, String(cloned),
|
||||
"createError creates errors with the correct stack");
|
||||
|
||||
equal(error.message, "Meh", "Error message");
|
||||
equal(error.fileName, "thing.js", "Error filename");
|
||||
equal(error.lineNumber, 5, "Error line");
|
||||
equal(error.columnNumber, 14, "Error column");
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче