/* -*- 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/. */
size_t ExtraMallocSize(JSErrorReport* report) { if (report->linebuf()) { /* * Count with null terminator and alignment. * See CopyExtraData for the details about alignment.
*/ return (report->linebufLength() + 1) * sizeof(char16_t) + 1;
}
bool CopyExtraData(JSContext* cx, uint8_t** cursor, JSErrorReport* copy,
JSErrorReport* report) { if (report->linebuf()) { /* * Make sure cursor is properly aligned for char16_t for platforms * which need it and it's at the end of the buffer on exit.
*/
size_t alignment_backlog = 0; if (size_t(*cursor) % 2) {
(*cursor)++;
} else {
alignment_backlog = 1;
}
template <typename T> static UniquePtr<T> CopyErrorHelper(JSContext* cx, T* report) { /* * We use a single malloc block to make a deep copy of JSErrorReport or * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with * the following layout: * JSErrorReport or JSErrorNotes::Note * char array with characters for message_ * char array with characters for filename * char16_t array with characters for linebuf (only for JSErrorReport) * Such layout together with the properties enforced by the following * asserts does not need any extra alignment padding.
*/
static_assert(sizeof(T) % sizeof(constchar*) == 0);
static_assert(sizeof(constchar*) % sizeof(char16_t) == 0);
/* * The mallocSize can not overflow since it represents the sum of the * sizes of already allocated objects.
*/
size_t mallocSize = sizeof(T) + messageSize + filenameSize + ExtraMallocSize(report);
uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize); if (!cursor) { return nullptr;
}
JSErrorReport* js::ErrorFromException(JSContext* cx, HandleObject objArg) { // It's ok to UncheckedUnwrap here, since all we do is get the // JSErrorReport, and consumers are careful with the information they get // from that anyway. Anyone doing things that would expose anything in the // JSErrorReport to page script either does a security check on the // JSErrorReport's principal or also tries to do toString on our object and // will fail if they can't unwrap it.
RootedObject obj(cx, UncheckedUnwrap(objArg)); if (!obj->is<ErrorObject>()) { return nullptr;
}
JSErrorReport* report = obj->as<ErrorObject>().getOrCreateErrorReport(cx); if (!report) {
MOZ_ASSERT(cx->isThrowingOutOfMemory());
cx->recoverFromOutOfMemory();
}
// Throw it.
RootedValue errValue(cx, ObjectValue(*errObject));
Rooted<SavedFrame*> nstack(cx); if (stack) {
nstack = &stack->as<SavedFrame>();
}
cx->setPendingException(errValue, nstack); returntrue;
}
using SniffingBehavior = JS::ErrorReportBuilder::SniffingBehavior;
staticbool IsDuckTypedErrorObject(JSContext* cx, HandleObject exnObject, constchar** filename_strp) { /* * This function is called from ErrorReport::init and so should not generate * any new exceptions.
*/
AutoClearPendingException acpe(cx);
// First try "filename". constchar* filename_str = *filename_strp; if (!JS_HasProperty(cx, exnObject, filename_str, &found)) { returnfalse;
} if (!found) { // If that doesn't work, try "fileName".
filename_str = "fileName"; if (!JS_HasProperty(cx, exnObject, filename_str, &found) || !found) { returnfalse;
}
}
if (!JS_HasProperty(cx, exnObject, "lineNumber", &found) || !found) { returnfalse;
}
*filename_strp = filename_str; returntrue;
}
staticbool GetPropertyNoException(JSContext* cx, HandleObject obj,
SniffingBehavior behavior,
Handle<PropertyName*> name,
MutableHandleValue vp) { // This function has no side-effects so always use it. if (GetPropertyPure(cx, obj, NameToId(name), vp.address())) { returntrue;
}
// Create a new error message similar to what Error.prototype.toString would // produce when called on an object with those property values for name and // message. static JSString* FormatErrorMessage(JSContext* cx, HandleString name,
HandleString message) { if (name && message) {
AutoClearPendingException acpe(cx);
JSStringBuilder sb(cx);
// Prefix the message with the error type, if it exists. if (!sb.append(name) || !sb.append(": ") || !sb.append(message)) { return nullptr;
}
return sb.finishString();
}
return name ? name : message;
}
static JSString* ErrorReportToString(JSContext* cx, HandleObject exn,
JSErrorReport* reportp,
SniffingBehavior behavior) { // The error object might have custom `name` overwriting the exnType in the // error report. Try getting that property and use the exnType as a fallback.
RootedString name(cx);
RootedValue nameV(cx); if (GetPropertyNoException(cx, exn, behavior, cx->names().name, &nameV) &&
nameV.isString()) {
name = nameV.toString();
}
// We do NOT want to use GetErrorTypeName() here because it will not do the // "right thing" for JSEXN_INTERNALERR. That is, the caller of this API // expects that "InternalError: " will be prepended but GetErrorTypeName // goes out of its way to avoid this. if (!name) {
JSExnType type = static_cast<JSExnType>(reportp->exnType); if (type != JSEXN_WARN && type != JSEXN_NOTE) {
name = ClassName(GetExceptionProtoKey(type), cx);
}
}
if (exnStack.exception().isObject()) { // Because ToString below could error and an exception object could become // unrooted, we must root our exception object, if any.
exnObject = &exnStack.exception().toObject();
reportp = ErrorFromException(cx, exnObject);
}
// Be careful not to invoke ToString if we've already successfully extracted // an error report, since the exception might be wrapped in a security // wrapper, and ToString-ing it might throw.
RootedString str(cx); if (reportp) {
str = ErrorReportToString(cx, exnObject, reportp, sniffingBehavior);
} elseif (exnStack.exception().isSymbol()) {
RootedValue strVal(cx); if (js::SymbolDescriptiveString(cx, exnStack.exception().toSymbol(),
&strVal)) {
str = strVal.toString();
} else {
str = nullptr;
}
} elseif (exnObject && sniffingBehavior == NoSideEffects) {
str = cx->names().Object;
} else {
str = js::ToString<CanGC>(cx, exnStack.exception());
}
if (!str) {
cx->clearPendingException();
}
// If ErrorFromException didn't get us a JSErrorReport, then the object // was not an ErrorObject, security-wrapped or otherwise. However, it might // still quack like one. Give duck-typing a chance. We start by looking for // "filename" (all lowercase), since that's where DOMExceptions store their // filename. Then we check "fileName", which is where Errors store it. We // have to do it in that order, because DOMExceptions have Error.prototype // on their proto chain, and hence also have a "fileName" property, but its // value is "". constchar* filename_str = "filename"; if (!reportp && exnObject && sniffingBehavior == WithSideEffects &&
IsDuckTypedErrorObject(cx, exnObject, &filename_str)) { // Temporary value for pulling properties off of duck-typed objects.
RootedValue val(cx);
RootedString name(cx); if (JS_GetProperty(cx, exnObject, "name", &val) && val.isString()) {
name = val.toString();
} else {
cx->clearPendingException();
}
// If we have the right fields, override the ToString we performed on // the exception object above with something built out of its quacks // (i.e. as much of |NameQuack: MessageQuack| as we can make).
str = FormatErrorMessage(cx, name, msg);
{
AutoClearPendingException acpe(cx); if (JS_GetProperty(cx, exnObject, filename_str, &val)) {
RootedString tmp(cx, js::ToString<CanGC>(cx, val)); if (tmp) {
filename = JS_EncodeStringToUTF8(cx, tmp);
}
}
} if (!filename) {
filename = DuplicateString(""); if (!filename) {
ReportOutOfMemory(cx); returnfalse;
}
}
if (str) { // Note that using |str| for |message_| here is kind of wrong, // because |str| is supposed to be of the format // |ErrorName: ErrorMessage|, and |message_| is supposed to // correspond to |ErrorMessage|. But this is what we've // historically done for duck-typed error objects. // // If only this stuff could get specced one day... if (auto utf8 = JS_EncodeStringToUTF8(cx, str)) {
ownedReport.initOwnedMessage(utf8.release());
} else {
cx->clearPendingException();
str = nullptr;
}
}
}
constchar* utf8Message = nullptr; if (str) {
toStringResultBytesStorage = JS_EncodeStringToUTF8(cx, str);
utf8Message = toStringResultBytesStorage.get(); if (!utf8Message) {
cx->clearPendingException();
}
} if (!utf8Message) {
utf8Message = "unknown (can't convert to string)";
}
if (!reportp) { // This is basically an inlined version of // // JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, // JSMSG_UNCAUGHT_EXCEPTION, utf8Message); // // but without the reporting bits. Instead it just puts all // the stuff we care about in our ownedReport and message_. if (!populateUncaughtExceptionReportUTF8(cx, exnStack.stack(),
utf8Message)) { // Just give up. We're out of memory or something; not much we can // do here. returnfalse;
}
} else {
toStringResult_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message));
}
// This function must always return a non-null string. If the conversion to // string fails due to OOM, we return this string instead. static constexpr char ErrorConvertingToStringMsg[] = "<>";
RootedString str(cx, JS_ValueToSource(cx, val)); if (!str) { return ErrorConvertingToStringMsg;
}
JSStringBuilder sb(cx); if (val.hasObjectPayload()) {
RootedObject valObj(cx, &val.getObjectPayload());
ESClass cls; if (!JS::GetBuiltinClass(cx, valObj, &cls)) { return"<>";
} constchar* s; if (cls == ESClass::Array) {
s = "the array ";
} elseif (cls == ESClass::ArrayBuffer) {
s = "the array buffer ";
} elseif (JS_IsArrayBufferViewObject(valObj)) {
s = "the typed array "; #ifdef ENABLE_RECORD_TUPLE
} elseif (cls == ESClass::Record) {
s = "the record ";
} elseif (cls == ESClass::Tuple) {
s = "the tuple "; #endif
} else {
s = "the object ";
} if (!sb.append(s, strlen(s))) { return ErrorConvertingToStringMsg;
}
} elseif (val.isNumber()) { if (!sb.append("the number ")) { return ErrorConvertingToStringMsg;
}
} elseif (val.isString()) { if (!sb.append("the string ")) { return ErrorConvertingToStringMsg;
}
} elseif (val.isBigInt()) { if (!sb.append("the BigInt ")) { return ErrorConvertingToStringMsg;
}
} else {
MOZ_ASSERT(val.isBoolean() || val.isSymbol());
bytes = StringToNewUTF8CharsZ(cx, *str); if (!bytes) { return ErrorConvertingToStringMsg;
} return bytes.get();
} if (!sb.append(str)) { return ErrorConvertingToStringMsg;
}
str = sb.finishString(); if (!str) { return ErrorConvertingToStringMsg;
}
bytes = StringToNewUTF8CharsZ(cx, *str); if (!bytes) { return ErrorConvertingToStringMsg;
} return bytes.get();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.