LCOV - code coverage report
Current view: top level - js/src/vm - ErrorObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 43 134 32.1 %
Date: 2017-07-14 16:53:18 Functions: 3 10 30.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sw=4 et tw=78:
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       7             : 
       8             : #include "vm/ErrorObject-inl.h"
       9             : 
      10             : #include "mozilla/Range.h"
      11             : 
      12             : #include "jsexn.h"
      13             : 
      14             : #include "js/CallArgs.h"
      15             : #include "js/CharacterEncoding.h"
      16             : #include "vm/GlobalObject.h"
      17             : #include "vm/String.h"
      18             : 
      19             : #include "jsobjinlines.h"
      20             : 
      21             : #include "vm/NativeObject-inl.h"
      22             : #include "vm/SavedStacks-inl.h"
      23             : #include "vm/Shape-inl.h"
      24             : 
      25             : using namespace js;
      26             : 
      27             : /* static */ Shape*
      28           3 : js::ErrorObject::assignInitialShape(JSContext* cx, Handle<ErrorObject*> obj)
      29             : {
      30           3 :     MOZ_ASSERT(obj->empty());
      31             : 
      32           3 :     if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName, FILENAME_SLOT, 0))
      33           0 :         return nullptr;
      34           3 :     if (!NativeObject::addDataProperty(cx, obj, cx->names().lineNumber, LINENUMBER_SLOT, 0))
      35           0 :         return nullptr;
      36           3 :     return NativeObject::addDataProperty(cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0);
      37             : }
      38             : 
      39             : /* static */ bool
      40           3 : js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
      41             :                       ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName,
      42             :                       HandleObject stack, uint32_t lineNumber, uint32_t columnNumber,
      43             :                       HandleString message)
      44             : {
      45           3 :     AssertObjectIsSavedFrameOrWrapper(cx, stack);
      46           3 :     assertSameCompartment(cx, obj, stack);
      47             : 
      48             :     // Null out early in case of error, for exn_finalize's sake.
      49           3 :     obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr));
      50             : 
      51           3 :     if (!EmptyShape::ensureInitialCustomShape<ErrorObject>(cx, obj))
      52           0 :         return false;
      53             : 
      54             :     // The .message property isn't part of the initial shape because it's
      55             :     // present in some error objects -- |Error.prototype|, |new Error("f")|,
      56             :     // |new Error("")| -- but not in others -- |new Error(undefined)|,
      57             :     // |new Error()|.
      58           6 :     RootedShape messageShape(cx);
      59           3 :     if (message) {
      60           2 :         messageShape = NativeObject::addDataProperty(cx, obj, cx->names().message, MESSAGE_SLOT, 0);
      61           2 :         if (!messageShape)
      62           0 :             return false;
      63           2 :         MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT);
      64             :     }
      65             : 
      66           3 :     MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().fileName))->slot() == FILENAME_SLOT);
      67           3 :     MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().lineNumber))->slot() == LINENUMBER_SLOT);
      68           3 :     MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().columnNumber))->slot() ==
      69             :                COLUMNNUMBER_SLOT);
      70           3 :     MOZ_ASSERT_IF(message,
      71             :                   obj->lookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT);
      72             : 
      73           3 :     MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_LIMIT);
      74             : 
      75           3 :     JSErrorReport* report = errorReport ? errorReport->forget() : nullptr;
      76           3 :     obj->initReservedSlot(EXNTYPE_SLOT, Int32Value(type));
      77           3 :     obj->initReservedSlot(STACK_SLOT, ObjectOrNullValue(stack));
      78           3 :     obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report));
      79           3 :     obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName));
      80           3 :     obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber));
      81           3 :     obj->initReservedSlot(COLUMNNUMBER_SLOT, Int32Value(columnNumber));
      82           3 :     if (message)
      83           2 :         obj->setSlotWithType(cx, messageShape, StringValue(message));
      84             : 
      85           3 :     return true;
      86             : }
      87             : 
      88             : /* static */ ErrorObject*
      89           3 : js::ErrorObject::create(JSContext* cx, JSExnType errorType, HandleObject stack,
      90             :                         HandleString fileName, uint32_t lineNumber, uint32_t columnNumber,
      91             :                         ScopedJSFreePtr<JSErrorReport>* report, HandleString message,
      92             :                         HandleObject protoArg /* = nullptr */)
      93             : {
      94           3 :     AssertObjectIsSavedFrameOrWrapper(cx, stack);
      95             : 
      96           6 :     RootedObject proto(cx, protoArg);
      97           3 :     if (!proto) {
      98           3 :         proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(), errorType);
      99           3 :         if (!proto)
     100           0 :             return nullptr;
     101             :     }
     102             : 
     103           6 :     Rooted<ErrorObject*> errObject(cx);
     104             :     {
     105           3 :         const Class* clasp = ErrorObject::classForType(errorType);
     106           3 :         JSObject* obj = NewObjectWithGivenProto(cx, clasp, proto);
     107           3 :         if (!obj)
     108           0 :             return nullptr;
     109           3 :         errObject = &obj->as<ErrorObject>();
     110             :     }
     111             : 
     112           3 :     if (!ErrorObject::init(cx, errObject, errorType, report, fileName, stack,
     113             :                            lineNumber, columnNumber, message))
     114             :     {
     115           0 :         return nullptr;
     116             :     }
     117             : 
     118           3 :     return errObject;
     119             : }
     120             : 
     121             : JSErrorReport*
     122           0 : js::ErrorObject::getOrCreateErrorReport(JSContext* cx)
     123             : {
     124           0 :     if (JSErrorReport* r = getErrorReport())
     125           0 :         return r;
     126             : 
     127             :     // We build an error report on the stack and then use CopyErrorReport to do
     128             :     // the nitty-gritty malloc stuff.
     129           0 :     JSErrorReport report;
     130             : 
     131             :     // Type.
     132           0 :     JSExnType type_ = type();
     133           0 :     report.exnType = type_;
     134             : 
     135             :     // Filename.
     136           0 :     JSAutoByteString filenameStr;
     137           0 :     if (!filenameStr.encodeLatin1(cx, fileName(cx)))
     138           0 :         return nullptr;
     139           0 :     report.filename = filenameStr.ptr();
     140             : 
     141             :     // Coordinates.
     142           0 :     report.lineno = lineNumber();
     143           0 :     report.column = columnNumber();
     144             : 
     145             :     // Message. Note that |new Error()| will result in an undefined |message|
     146             :     // slot, so we need to explicitly substitute the empty string in that case.
     147           0 :     RootedString message(cx, getMessage());
     148           0 :     if (!message)
     149           0 :         message = cx->runtime()->emptyString;
     150           0 :     if (!message->ensureFlat(cx))
     151           0 :         return nullptr;
     152             : 
     153           0 :     UniquePtr<char[], JS::FreePolicy> utf8 = StringToNewUTF8CharsZ(cx, *message);
     154           0 :     if (!utf8)
     155           0 :         return nullptr;
     156           0 :     report.initOwnedMessage(utf8.release());
     157             : 
     158             :     // Cache and return.
     159           0 :     JSErrorReport* copy = CopyErrorReport(cx, &report);
     160           0 :     if (!copy)
     161           0 :         return nullptr;
     162           0 :     setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy));
     163           0 :     return copy;
     164             : }
     165             : 
     166             : static bool
     167           0 : FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result)
     168             : {
     169             :     // Walk up the prototype chain until we find an error object instance or
     170             :     // prototype object. This allows code like:
     171             :     //  Object.create(Error.prototype).stack
     172             :     // or
     173             :     //   function NYI() { }
     174             :     //   NYI.prototype = new Error;
     175             :     //   (new NYI).stack
     176             :     // to continue returning stacks that are useless, but at least don't throw.
     177             : 
     178           0 :     RootedObject target(cx, CheckedUnwrap(obj));
     179           0 :     if (!target) {
     180           0 :         ReportAccessDenied(cx);
     181           0 :         return false;
     182             :     }
     183             : 
     184           0 :     RootedObject proto(cx);
     185           0 :     while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
     186           0 :         if (!GetPrototype(cx, target, &proto))
     187           0 :             return false;
     188             : 
     189           0 :         if (!proto) {
     190             :             // We walked the whole prototype chain and did not find an Error
     191             :             // object.
     192             :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
     193           0 :                                       js_Error_str, "(get stack)", obj->getClass()->name);
     194           0 :             return false;
     195             :         }
     196             : 
     197           0 :         target = CheckedUnwrap(proto);
     198           0 :         if (!target) {
     199           0 :             ReportAccessDenied(cx);
     200           0 :             return false;
     201             :         }
     202             :     }
     203             : 
     204           0 :     result.set(target);
     205           0 :     return true;
     206             : }
     207             : 
     208             : 
     209             : static MOZ_ALWAYS_INLINE bool
     210           0 : IsObject(HandleValue v)
     211             : {
     212           0 :     return v.isObject();
     213             : }
     214             : 
     215             : /* static */ bool
     216           0 : js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp)
     217             : {
     218           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     219             :     // We accept any object here, because of poor-man's subclassing of Error.
     220           0 :     return CallNonGenericMethod<IsObject, getStack_impl>(cx, args);
     221             : }
     222             : 
     223             : /* static */ bool
     224           0 : js::ErrorObject::getStack_impl(JSContext* cx, const CallArgs& args)
     225             : {
     226           0 :     RootedObject thisObj(cx, &args.thisv().toObject());
     227             : 
     228           0 :     RootedObject obj(cx);
     229           0 :     if (!FindErrorInstanceOrPrototype(cx, thisObj, &obj))
     230           0 :         return false;
     231             : 
     232           0 :     if (!obj->is<ErrorObject>()) {
     233           0 :         args.rval().setString(cx->runtime()->emptyString);
     234           0 :         return true;
     235             :     }
     236             : 
     237           0 :     RootedObject savedFrameObj(cx, obj->as<ErrorObject>().stack());
     238           0 :     RootedString stackString(cx);
     239           0 :     if (!BuildStackString(cx, savedFrameObj, &stackString))
     240           0 :         return false;
     241             : 
     242           0 :     if (cx->runtime()->stackFormat() == js::StackFormat::V8) {
     243             :         // When emulating V8 stack frames, we also need to prepend the
     244             :         // stringified Error to the stack string.
     245           0 :         HandlePropertyName name = cx->names().ErrorToStringWithTrailingNewline;
     246           0 :         RootedValue val(cx);
     247           0 :         if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), name, name, 0, &val))
     248           0 :             return false;
     249             : 
     250           0 :         RootedValue rval(cx);
     251           0 :         if (!js::Call(cx, val, args.thisv(), &rval))
     252           0 :             return false;
     253             : 
     254           0 :         if (!rval.isString())
     255           0 :             return false;
     256             : 
     257           0 :         RootedString stringified(cx, rval.toString());
     258           0 :         stackString = ConcatStrings<CanGC>(cx, stringified, stackString);
     259             :     }
     260             : 
     261           0 :     args.rval().setString(stackString);
     262           0 :     return true;
     263             : }
     264             : 
     265             : /* static */ bool
     266           0 : js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp)
     267             : {
     268           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     269             :     // We accept any object here, because of poor-man's subclassing of Error.
     270           0 :     return CallNonGenericMethod<IsObject, setStack_impl>(cx, args);
     271             : }
     272             : 
     273             : /* static */ bool
     274           0 : js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args)
     275             : {
     276           0 :     RootedObject thisObj(cx, &args.thisv().toObject());
     277             : 
     278           0 :     if (!args.requireAtLeast(cx, "(set stack)", 1))
     279           0 :         return false;
     280           0 :     RootedValue val(cx, args[0]);
     281             : 
     282           0 :     return DefineProperty(cx, thisObj, cx->names().stack, val);
     283             : }

Generated by: LCOV version 1.13