diff options
-rw-r--r-- | js/src/jsexn.cpp | 362 | ||||
-rw-r--r-- | js/src/jsexn.h | 3 | ||||
-rw-r--r-- | js/src/vm/ErrorObject.cpp | 356 | ||||
-rw-r--r-- | js/src/vm/ErrorObject.h | 10 |
4 files changed, 362 insertions, 369 deletions
diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 2eb8e7d105..5329fd4eb8 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -45,162 +45,6 @@ using namespace js::gc; using mozilla::ArrayLength; using mozilla::PodArrayZero; -static void -exn_finalize(FreeOp* fop, JSObject* obj); - -static bool -exn_toSource(JSContext* cx, unsigned argc, Value* vp); - -#define IMPLEMENT_ERROR_PROTO_CLASS(name) \ - { \ - js_Object_str, \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \ - JS_NULL_CLASS_OPS, \ - &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \ - } - -const Class -ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = { - IMPLEMENT_ERROR_PROTO_CLASS(Error), - - IMPLEMENT_ERROR_PROTO_CLASS(InternalError), - IMPLEMENT_ERROR_PROTO_CLASS(EvalError), - IMPLEMENT_ERROR_PROTO_CLASS(RangeError), - IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError), - IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError), - IMPLEMENT_ERROR_PROTO_CLASS(TypeError), - IMPLEMENT_ERROR_PROTO_CLASS(URIError), - - IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun), - IMPLEMENT_ERROR_PROTO_CLASS(CompileError), - IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError) -}; - -static const JSFunctionSpec error_methods[] = { -#if JS_HAS_TOSOURCE - JS_FN(js_toSource_str, exn_toSource, 0, 0), -#endif - JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0), - JS_FS_END -}; - -static const JSPropertySpec error_properties[] = { - JS_STRING_PS("message", "", 0), - JS_STRING_PS("name", "Error", 0), - // Only Error.prototype has .stack! - JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0), - JS_PS_END -}; - -#define IMPLEMENT_ERROR_PROPERTIES(name) \ - { \ - JS_STRING_PS("message", "", 0), \ - JS_STRING_PS("name", #name, 0), \ - JS_PS_END \ - } - -static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = { - IMPLEMENT_ERROR_PROPERTIES(InternalError), - IMPLEMENT_ERROR_PROPERTIES(EvalError), - IMPLEMENT_ERROR_PROPERTIES(RangeError), - IMPLEMENT_ERROR_PROPERTIES(ReferenceError), - IMPLEMENT_ERROR_PROPERTIES(SyntaxError), - IMPLEMENT_ERROR_PROPERTIES(TypeError), - IMPLEMENT_ERROR_PROPERTIES(URIError), - IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun), - IMPLEMENT_ERROR_PROPERTIES(CompileError), - IMPLEMENT_ERROR_PROPERTIES(RuntimeError) -}; - -#define IMPLEMENT_NATIVE_ERROR_SPEC(name) \ - { \ - ErrorObject::createConstructor, \ - ErrorObject::createProto, \ - nullptr, \ - nullptr, \ - nullptr, \ - other_error_properties[JSProto_##name - JSProto_Error - 1], \ - nullptr, \ - JSProto_Error \ - } - -#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \ - { \ - ErrorObject::createConstructor, \ - ErrorObject::createProto, \ - nullptr, \ - nullptr, \ - nullptr, \ - other_error_properties[JSProto_##name - JSProto_Error - 1], \ - nullptr, \ - JSProto_Error | ClassSpec::DontDefineConstructor \ - } - -const ClassSpec -ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = { - { - ErrorObject::createConstructor, - ErrorObject::createProto, - nullptr, - nullptr, - error_methods, - error_properties - }, - - IMPLEMENT_NATIVE_ERROR_SPEC(InternalError), - IMPLEMENT_NATIVE_ERROR_SPEC(EvalError), - IMPLEMENT_NATIVE_ERROR_SPEC(RangeError), - IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError), - IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError), - IMPLEMENT_NATIVE_ERROR_SPEC(TypeError), - IMPLEMENT_NATIVE_ERROR_SPEC(URIError), - - IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun), - IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError), - IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError) -}; - -#define IMPLEMENT_ERROR_CLASS(name) \ - { \ - js_Error_str, /* yes, really */ \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \ - JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \ - JSCLASS_BACKGROUND_FINALIZE, \ - &ErrorObjectClassOps, \ - &ErrorObject::classSpecs[JSProto_##name - JSProto_Error ] \ - } - -static const ClassOps ErrorObjectClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - exn_finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - nullptr, /* trace */ -}; - -const Class -ErrorObject::classes[JSEXN_ERROR_LIMIT] = { - IMPLEMENT_ERROR_CLASS(Error), - IMPLEMENT_ERROR_CLASS(InternalError), - IMPLEMENT_ERROR_CLASS(EvalError), - IMPLEMENT_ERROR_CLASS(RangeError), - IMPLEMENT_ERROR_CLASS(ReferenceError), - IMPLEMENT_ERROR_CLASS(SyntaxError), - IMPLEMENT_ERROR_CLASS(TypeError), - IMPLEMENT_ERROR_CLASS(URIError), - // These Error subclasses are not accessible via the global object: - IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun), - IMPLEMENT_ERROR_CLASS(CompileError), - IMPLEMENT_ERROR_CLASS(RuntimeError) -}; - size_t ExtraMallocSize(JSErrorReport* report) { @@ -361,8 +205,9 @@ struct SuppressErrorsGuard } }; -static bool -CaptureStack(JSContext* cx, MutableHandleObject stack) + +bool +js::CaptureStack(JSContext* cx, MutableHandleObject stack) { // Cut off the stack if it gets too deep (most commonly for infinite recursion // errors). @@ -388,14 +233,6 @@ js::ComputeStackString(JSContext* cx) return str.get(); } -static void -exn_finalize(FreeOp* fop, JSObject* obj) -{ - MOZ_ASSERT(fop->maybeOffMainThread()); - if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) - fop->delete_(report); -} - JSErrorReport* js::ErrorFromException(JSContext* cx, HandleObject objArg) { @@ -429,199 +266,6 @@ ExceptionStackOrNull(HandleObject objArg) return obj->as<ErrorObject>().stack(); } -bool -Error(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString - RootedObject proto(cx); - if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) - return false; - - /* Compute the error message, if any. */ - RootedString message(cx, nullptr); - if (args.hasDefined(0)) { - message = ToString<CanGC>(cx, args[0]); - if (!message) - return false; - } - - /* Find the scripted caller, but only ones we're allowed to know about. */ - NonBuiltinFrameIter iter(cx, cx->compartment()->principals()); - - /* Set the 'fileName' property. */ - RootedString fileName(cx); - if (args.length() > 1) { - fileName = ToString<CanGC>(cx, args[1]); - } else { - fileName = cx->runtime()->emptyString; - if (!iter.done()) { - if (const char* cfilename = iter.filename()) - fileName = JS_NewStringCopyZ(cx, cfilename); - } - } - if (!fileName) - return false; - - /* Set the 'lineNumber' property. */ - uint32_t lineNumber, columnNumber = 0; - if (args.length() > 2) { - if (!ToUint32(cx, args[2], &lineNumber)) - return false; - } else { - lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber); - // XXX: Make the column 1-based as in other browsers, instead of 0-based - // which is how SpiderMonkey stores it internally. This will be - // unnecessary once bug 1144340 is fixed. - ++columnNumber; - } - - RootedObject stack(cx); - if (!CaptureStack(cx, &stack)) - return false; - - /* - * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when - * called as functions, without operator new. But as we do not give - * each constructor a distinct JSClass, we must get the exception type - * ourselves. - */ - JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32()); - - RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, - lineNumber, columnNumber, nullptr, message, proto)); - if (!obj) - return false; - - args.rval().setObject(*obj); - return true; -} - -#if JS_HAS_TOSOURCE -/* - * Return a string that may eval to something similar to the original object. - */ -static bool -exn_toSource(JSContext* cx, unsigned argc, Value* vp) -{ - JS_CHECK_RECURSION(cx, return false); - CallArgs args = CallArgsFromVp(argc, vp); - - RootedObject obj(cx, ToObject(cx, args.thisv())); - if (!obj) - return false; - - RootedValue nameVal(cx); - RootedString name(cx); - if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) || - !(name = ToString<CanGC>(cx, nameVal))) - { - return false; - } - - RootedValue messageVal(cx); - RootedString message(cx); - if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) || - !(message = ValueToSource(cx, messageVal))) - { - return false; - } - - RootedValue filenameVal(cx); - RootedString filename(cx); - if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) || - !(filename = ValueToSource(cx, filenameVal))) - { - return false; - } - - RootedValue linenoVal(cx); - uint32_t lineno; - if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) || - !ToUint32(cx, linenoVal, &lineno)) - { - return false; - } - - StringBuffer sb(cx); - if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) - return false; - - if (!sb.append(message)) - return false; - - if (!filename->empty()) { - if (!sb.append(", ") || !sb.append(filename)) - return false; - } - if (lineno != 0) { - /* We have a line, but no filename, add empty string */ - if (filename->empty() && !sb.append(", \"\"")) - return false; - - JSString* linenumber = ToString<CanGC>(cx, linenoVal); - if (!linenumber) - return false; - if (!sb.append(", ") || !sb.append(linenumber)) - return false; - } - - if (!sb.append("))")) - return false; - - JSString* str = sb.finishString(); - if (!str) - return false; - args.rval().setString(str); - return true; -} -#endif - -/* static */ JSObject* -ErrorObject::createProto(JSContext* cx, JSProtoKey key) -{ - JSExnType type = ExnTypeFromProtoKey(key); - - if (type == JSEXN_ERR) { - return GlobalObject::createBlankPrototype(cx, cx->global(), - &ErrorObject::protoClasses[JSEXN_ERR]); - } - - RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global())); - if (!protoProto) - return nullptr; - - return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(), - &ErrorObject::protoClasses[type], - protoProto); -} - -/* static */ JSObject* -ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) -{ - JSExnType type = ExnTypeFromProtoKey(key); - RootedObject ctor(cx); - - if (type == JSEXN_ERR) { - ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(cx, key); - } else { - RootedFunction proto(cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global())); - if (!proto) - return nullptr; - - ctor = NewFunctionWithProto(cx, Error, 1, JSFunction::NATIVE_CTOR, nullptr, - ClassName(key, cx), proto, gc::AllocKind::FUNCTION_EXTENDED, - SingletonObject); - } - - if (!ctor) - return nullptr; - - ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type)); - return ctor; -} - JS_FRIEND_API(JSFlatString*) js::GetErrorTypeName(JSContext* cx, int16_t exnType) { diff --git a/js/src/jsexn.h b/js/src/jsexn.h index 16380c696a..a1ed55b4a1 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -23,6 +23,9 @@ CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note); JSErrorReport* CopyErrorReport(JSContext* cx, JSErrorReport* report); +bool +CaptureStack(JSContext* cx, MutableHandleObject stack); + JSString* ComputeStackString(JSContext* cx); diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp index 8976d81d27..192b0758db 100644 --- a/js/src/vm/ErrorObject.cpp +++ b/js/src/vm/ErrorObject.cpp @@ -12,6 +12,7 @@ #include "js/CallArgs.h" #include "js/CharacterEncoding.h" +#include "vm/StringBuffer.h" #include "vm/GlobalObject.h" #include "vm/String.h" @@ -23,6 +24,283 @@ using namespace js; +#define IMPLEMENT_ERROR_PROTO_CLASS(name) \ + { \ + js_Object_str, \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \ + JS_NULL_CLASS_OPS, \ + &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \ + } + +const Class +ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = { + IMPLEMENT_ERROR_PROTO_CLASS(Error), + + IMPLEMENT_ERROR_PROTO_CLASS(InternalError), + IMPLEMENT_ERROR_PROTO_CLASS(EvalError), + IMPLEMENT_ERROR_PROTO_CLASS(RangeError), + IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError), + IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError), + IMPLEMENT_ERROR_PROTO_CLASS(TypeError), + IMPLEMENT_ERROR_PROTO_CLASS(URIError), + + IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun), + IMPLEMENT_ERROR_PROTO_CLASS(CompileError), + IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError) +}; + +static bool +exn_toSource(JSContext* cx, unsigned argc, Value* vp); + +static const JSFunctionSpec error_methods[] = { +#if JS_HAS_TOSOURCE + JS_FN(js_toSource_str, exn_toSource, 0, 0), +#endif + JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0), + JS_FS_END +}; + +static const JSPropertySpec error_properties[] = { + JS_STRING_PS("message", "", 0), + JS_STRING_PS("name", "Error", 0), + // Only Error.prototype has .stack! + JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0), + JS_PS_END +}; + +#define IMPLEMENT_ERROR_PROPERTIES(name) \ + { \ + JS_STRING_PS("message", "", 0), \ + JS_STRING_PS("name", #name, 0), \ + JS_PS_END \ + } + +static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = { + IMPLEMENT_ERROR_PROPERTIES(InternalError), + IMPLEMENT_ERROR_PROPERTIES(EvalError), + IMPLEMENT_ERROR_PROPERTIES(RangeError), + IMPLEMENT_ERROR_PROPERTIES(ReferenceError), + IMPLEMENT_ERROR_PROPERTIES(SyntaxError), + IMPLEMENT_ERROR_PROPERTIES(TypeError), + IMPLEMENT_ERROR_PROPERTIES(URIError), + IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun), + IMPLEMENT_ERROR_PROPERTIES(CompileError), + IMPLEMENT_ERROR_PROPERTIES(RuntimeError) +}; + +#define IMPLEMENT_NATIVE_ERROR_SPEC(name) \ + { \ + ErrorObject::createConstructor, \ + ErrorObject::createProto, \ + nullptr, \ + nullptr, \ + nullptr, \ + other_error_properties[JSProto_##name - JSProto_Error - 1], \ + nullptr, \ + JSProto_Error \ + } + +#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \ + { \ + ErrorObject::createConstructor, \ + ErrorObject::createProto, \ + nullptr, \ + nullptr, \ + nullptr, \ + other_error_properties[JSProto_##name - JSProto_Error - 1], \ + nullptr, \ + JSProto_Error | ClassSpec::DontDefineConstructor \ + } + +const ClassSpec +ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = { + { + ErrorObject::createConstructor, + ErrorObject::createProto, + nullptr, + nullptr, + error_methods, + error_properties + }, + + IMPLEMENT_NATIVE_ERROR_SPEC(InternalError), + IMPLEMENT_NATIVE_ERROR_SPEC(EvalError), + IMPLEMENT_NATIVE_ERROR_SPEC(RangeError), + IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError), + IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError), + IMPLEMENT_NATIVE_ERROR_SPEC(TypeError), + IMPLEMENT_NATIVE_ERROR_SPEC(URIError), + + IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun), + IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError), + IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError) +}; + +#define IMPLEMENT_ERROR_CLASS(name) \ + { \ + js_Error_str, /* yes, really */ \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \ + JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \ + JSCLASS_BACKGROUND_FINALIZE, \ + &ErrorObjectClassOps, \ + &ErrorObject::classSpecs[JSProto_##name - JSProto_Error ] \ + } + +static void +exn_finalize(FreeOp* fop, JSObject* obj); + +static const ClassOps ErrorObjectClassOps = { + nullptr, /* addProperty */ + nullptr, /* delProperty */ + nullptr, /* getProperty */ + nullptr, /* setProperty */ + nullptr, /* enumerate */ + nullptr, /* resolve */ + nullptr, /* mayResolve */ + exn_finalize, + nullptr, /* call */ + nullptr, /* hasInstance */ + nullptr, /* construct */ + nullptr, /* trace */ +}; + +const Class +ErrorObject::classes[JSEXN_ERROR_LIMIT] = { + IMPLEMENT_ERROR_CLASS(Error), + IMPLEMENT_ERROR_CLASS(InternalError), + IMPLEMENT_ERROR_CLASS(EvalError), + IMPLEMENT_ERROR_CLASS(RangeError), + IMPLEMENT_ERROR_CLASS(ReferenceError), + IMPLEMENT_ERROR_CLASS(SyntaxError), + IMPLEMENT_ERROR_CLASS(TypeError), + IMPLEMENT_ERROR_CLASS(URIError), + // These Error subclasses are not accessible via the global object: + IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun), + IMPLEMENT_ERROR_CLASS(CompileError), + IMPLEMENT_ERROR_CLASS(RuntimeError) +}; + +static void +exn_finalize(FreeOp* fop, JSObject* obj) +{ + MOZ_ASSERT(fop->maybeOffMainThread()); + if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) + fop->delete_(report); +} + +bool +Error(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString + RootedObject proto(cx); + if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) + return false; + + /* Compute the error message, if any. */ + RootedString message(cx, nullptr); + if (args.hasDefined(0)) { + message = ToString<CanGC>(cx, args[0]); + if (!message) + return false; + } + + /* Find the scripted caller, but only ones we're allowed to know about. */ + NonBuiltinFrameIter iter(cx, cx->compartment()->principals()); + + /* Set the 'fileName' property. */ + RootedString fileName(cx); + if (args.length() > 1) { + fileName = ToString<CanGC>(cx, args[1]); + } else { + fileName = cx->runtime()->emptyString; + if (!iter.done()) { + if (const char* cfilename = iter.filename()) + fileName = JS_NewStringCopyZ(cx, cfilename); + } + } + if (!fileName) + return false; + + /* Set the 'lineNumber' property. */ + uint32_t lineNumber, columnNumber = 0; + if (args.length() > 2) { + if (!ToUint32(cx, args[2], &lineNumber)) + return false; + } else { + lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber); + // XXX: Make the column 1-based as in other browsers, instead of 0-based + // which is how SpiderMonkey stores it internally. This will be + // unnecessary once bug 1144340 is fixed. + ++columnNumber; + } + + RootedObject stack(cx); + if (!CaptureStack(cx, &stack)) + return false; + + /* + * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when + * called as functions, without operator new. But as we do not give + * each constructor a distinct JSClass, we must get the exception type + * ourselves. + */ + JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32()); + + RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, + lineNumber, columnNumber, nullptr, message, proto)); + if (!obj) + return false; + + args.rval().setObject(*obj); + return true; +} + +/* static */ JSObject* +ErrorObject::createProto(JSContext* cx, JSProtoKey key) +{ + JSExnType type = ExnTypeFromProtoKey(key); + + if (type == JSEXN_ERR) { + return GlobalObject::createBlankPrototype(cx, cx->global(), + &ErrorObject::protoClasses[JSEXN_ERR]); + } + + RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global())); + if (!protoProto) + return nullptr; + + return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(), + &ErrorObject::protoClasses[type], + protoProto); +} + +/* static */ JSObject* +ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) +{ + JSExnType type = ExnTypeFromProtoKey(key); + RootedObject ctor(cx); + + if (type == JSEXN_ERR) { + ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(cx, key); + } else { + RootedFunction proto(cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global())); + if (!proto) + return nullptr; + + ctor = NewFunctionWithProto(cx, Error, 1, JSFunction::NATIVE_CTOR, nullptr, + ClassName(key, cx), proto, gc::AllocKind::FUNCTION_EXTENDED, + SingletonObject); + } + + if (!ctor) + return nullptr; + + ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type)); + return ctor; +} + /* static */ Shape* js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> obj) { @@ -280,3 +558,81 @@ js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args) return DefineProperty(cx, thisObj, cx->names().stack, val); } + +/* + * Return a string that may eval to something similar to the original object. + */ +static bool +exn_toSource(JSContext* cx, unsigned argc, Value* vp) +{ + JS_CHECK_RECURSION(cx, return false); + CallArgs args = CallArgsFromVp(argc, vp); + + RootedObject obj(cx, ToObject(cx, args.thisv())); + if (!obj) + return false; + + RootedValue nameVal(cx); + RootedString name(cx); + if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) || + !(name = ToString<CanGC>(cx, nameVal))) + { + return false; + } + + RootedValue messageVal(cx); + RootedString message(cx); + if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) || + !(message = ValueToSource(cx, messageVal))) + { + return false; + } + + RootedValue filenameVal(cx); + RootedString filename(cx); + if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) || + !(filename = ValueToSource(cx, filenameVal))) + { + return false; + } + + RootedValue linenoVal(cx); + uint32_t lineno; + if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) || + !ToUint32(cx, linenoVal, &lineno)) + { + return false; + } + + StringBuffer sb(cx); + if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) + return false; + + if (!sb.append(message)) + return false; + + if (!filename->empty()) { + if (!sb.append(", ") || !sb.append(filename)) + return false; + } + if (lineno != 0) { + /* We have a line, but no filename, add empty string */ + if (filename->empty() && !sb.append(", \"\"")) + return false; + + JSString* linenumber = ToString<CanGC>(cx, linenoVal); + if (!linenumber) + return false; + if (!sb.append(", ") || !sb.append(linenumber)) + return false; + } + + if (!sb.append("))")) + return false; + + JSString* str = sb.finishString(); + if (!str) + return false; + args.rval().setString(str); + return true; +} diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h index 1c32538c11..6e99dfa2c6 100644 --- a/js/src/vm/ErrorObject.h +++ b/js/src/vm/ErrorObject.h @@ -14,12 +14,6 @@ namespace js { -/* - * Initialize the exception constructor/prototype hierarchy. - */ -extern JSObject* -InitExceptionClasses(JSContext* cx, HandleObject obj); - class ErrorObject : public NativeObject { static JSObject* @@ -28,10 +22,6 @@ class ErrorObject : public NativeObject static JSObject* createConstructor(JSContext* cx, JSProtoKey key); - /* For access to createProto. */ - friend JSObject* - js::InitExceptionClasses(JSContext* cx, HandleObject global); - static bool init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type, ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName, HandleObject stack, |