michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sw=4 et tw=78: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/ErrorObject-inl.h" michael@0: michael@0: #include "jsexn.h" michael@0: michael@0: #include "vm/GlobalObject.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Shape-inl.h" michael@0: michael@0: using namespace js; michael@0: using mozilla::PodZero; michael@0: michael@0: /* static */ Shape * michael@0: js::ErrorObject::assignInitialShape(ExclusiveContext *cx, Handle obj) michael@0: { michael@0: MOZ_ASSERT(obj->nativeEmpty()); michael@0: michael@0: if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0)) michael@0: return nullptr; michael@0: if (!obj->addDataProperty(cx, cx->names().lineNumber, LINENUMBER_SLOT, 0)) michael@0: return nullptr; michael@0: if (!obj->addDataProperty(cx, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0)) michael@0: return nullptr; michael@0: return obj->addDataProperty(cx, cx->names().stack, STACK_SLOT, 0); michael@0: } michael@0: michael@0: /* static */ bool michael@0: js::ErrorObject::init(JSContext *cx, Handle obj, JSExnType type, michael@0: ScopedJSFreePtr *errorReport, HandleString fileName, michael@0: HandleString stack, uint32_t lineNumber, uint32_t columnNumber, michael@0: HandleString message) michael@0: { michael@0: // Null out early in case of error, for exn_finalize's sake. michael@0: obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr)); michael@0: michael@0: if (!EmptyShape::ensureInitialCustomShape(cx, obj)) michael@0: return false; michael@0: michael@0: // The .message property isn't part of the initial shape because it's michael@0: // present in some error objects -- |Error.prototype|, |new Error("f")|, michael@0: // |new Error("")| -- but not in others -- |new Error(undefined)|, michael@0: // |new Error()|. michael@0: RootedShape messageShape(cx); michael@0: if (message) { michael@0: messageShape = obj->addDataProperty(cx, cx->names().message, MESSAGE_SLOT, 0); michael@0: if (!messageShape) michael@0: return false; michael@0: MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT); michael@0: } michael@0: michael@0: MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().fileName))->slot() == FILENAME_SLOT); michael@0: MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().lineNumber))->slot() == LINENUMBER_SLOT); michael@0: MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().columnNumber))->slot() == michael@0: COLUMNNUMBER_SLOT); michael@0: MOZ_ASSERT(obj->nativeLookupPure(NameToId(cx->names().stack))->slot() == STACK_SLOT); michael@0: MOZ_ASSERT_IF(message, michael@0: obj->nativeLookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT); michael@0: michael@0: MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_LIMIT); michael@0: michael@0: JSErrorReport *report = errorReport ? errorReport->forget() : nullptr; michael@0: obj->initReservedSlot(EXNTYPE_SLOT, Int32Value(type)); michael@0: obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report)); michael@0: obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName)); michael@0: obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber)); michael@0: obj->initReservedSlot(COLUMNNUMBER_SLOT, Int32Value(columnNumber)); michael@0: obj->initReservedSlot(STACK_SLOT, StringValue(stack)); michael@0: if (message) michael@0: obj->nativeSetSlotWithType(cx, messageShape, StringValue(message)); michael@0: michael@0: if (report && report->originPrincipals) michael@0: JS_HoldPrincipals(report->originPrincipals); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ ErrorObject * michael@0: js::ErrorObject::create(JSContext *cx, JSExnType errorType, HandleString stack, michael@0: HandleString fileName, uint32_t lineNumber, uint32_t columnNumber, michael@0: ScopedJSFreePtr *report, HandleString message) michael@0: { michael@0: Rooted proto(cx, GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType)); michael@0: if (!proto) michael@0: return nullptr; michael@0: michael@0: Rooted errObject(cx); michael@0: { michael@0: JSObject* obj = NewObjectWithGivenProto(cx, &ErrorObject::class_, proto, nullptr); michael@0: if (!obj) michael@0: return nullptr; michael@0: errObject = &obj->as(); michael@0: } michael@0: michael@0: if (!ErrorObject::init(cx, errObject, errorType, report, fileName, stack, michael@0: lineNumber, columnNumber, message)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: return errObject; michael@0: } michael@0: michael@0: JSErrorReport * michael@0: js::ErrorObject::getOrCreateErrorReport(JSContext *cx) michael@0: { michael@0: if (JSErrorReport *r = getErrorReport()) michael@0: return r; michael@0: michael@0: // We build an error report on the stack and then use CopyErrorReport to do michael@0: // the nitty-gritty malloc stuff. michael@0: JSErrorReport report; michael@0: PodZero(&report); michael@0: michael@0: // Type. michael@0: JSExnType type_ = type(); michael@0: report.exnType = type_; michael@0: michael@0: // Filename. michael@0: JSAutoByteString filenameStr; michael@0: if (!filenameStr.encodeLatin1(cx, fileName(cx))) michael@0: return nullptr; michael@0: report.filename = filenameStr.ptr(); michael@0: michael@0: // Coordinates. michael@0: report.lineno = lineNumber(); michael@0: report.column = columnNumber(); michael@0: michael@0: // Message. Note that |new Error()| will result in an undefined |message| michael@0: // slot, so we need to explicitly substitute the empty string in that case. michael@0: RootedString message(cx, getMessage()); michael@0: if (!message) michael@0: message = cx->runtime()->emptyString; michael@0: if (!message->ensureFlat(cx)) michael@0: return nullptr; michael@0: report.ucmessage = message->asFlat().chars(); michael@0: michael@0: // Cache and return. michael@0: JSErrorReport *copy = CopyErrorReport(cx, &report); michael@0: if (!copy) michael@0: return nullptr; michael@0: setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy)); michael@0: return copy; michael@0: }