mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-20 22:27:31 +09:00
Issue #2089 - Move ErrorObject parts from jsexn.cpp to ErrorObject.cpp
Based-on: m-c 1568903/2
This commit is contained in:
parent
92906d4da5
commit
209c8e06a2
4 changed files with 362 additions and 369 deletions
362
js/src/jsexn.cpp
362
js/src/jsexn.cpp
|
|
@ -45,162 +45,6 @@ using namespace js::gc;
|
||||||
using mozilla::ArrayLength;
|
using mozilla::ArrayLength;
|
||||||
using mozilla::PodArrayZero;
|
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
|
size_t
|
||||||
ExtraMallocSize(JSErrorReport* report)
|
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
|
// Cut off the stack if it gets too deep (most commonly for infinite recursion
|
||||||
// errors).
|
// errors).
|
||||||
|
|
@ -388,14 +233,6 @@ js::ComputeStackString(JSContext* cx)
|
||||||
return str.get();
|
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*
|
JSErrorReport*
|
||||||
js::ErrorFromException(JSContext* cx, HandleObject objArg)
|
js::ErrorFromException(JSContext* cx, HandleObject objArg)
|
||||||
{
|
{
|
||||||
|
|
@ -429,199 +266,6 @@ ExceptionStackOrNull(HandleObject objArg)
|
||||||
return obj->as<ErrorObject>().stack();
|
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_FRIEND_API(JSFlatString*)
|
||||||
js::GetErrorTypeName(JSContext* cx, int16_t exnType)
|
js::GetErrorTypeName(JSContext* cx, int16_t exnType)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,9 @@ CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
|
||||||
JSErrorReport*
|
JSErrorReport*
|
||||||
CopyErrorReport(JSContext* cx, JSErrorReport* report);
|
CopyErrorReport(JSContext* cx, JSErrorReport* report);
|
||||||
|
|
||||||
|
bool
|
||||||
|
CaptureStack(JSContext* cx, MutableHandleObject stack);
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
ComputeStackString(JSContext* cx);
|
ComputeStackString(JSContext* cx);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "js/CallArgs.h"
|
#include "js/CallArgs.h"
|
||||||
#include "js/CharacterEncoding.h"
|
#include "js/CharacterEncoding.h"
|
||||||
|
#include "vm/StringBuffer.h"
|
||||||
#include "vm/GlobalObject.h"
|
#include "vm/GlobalObject.h"
|
||||||
#include "vm/String.h"
|
#include "vm/String.h"
|
||||||
|
|
||||||
|
|
@ -23,6 +24,283 @@
|
||||||
|
|
||||||
using namespace js;
|
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*
|
/* static */ Shape*
|
||||||
js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> obj)
|
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 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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,6 @@
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the exception constructor/prototype hierarchy.
|
|
||||||
*/
|
|
||||||
extern JSObject*
|
|
||||||
InitExceptionClasses(JSContext* cx, HandleObject obj);
|
|
||||||
|
|
||||||
class ErrorObject : public NativeObject
|
class ErrorObject : public NativeObject
|
||||||
{
|
{
|
||||||
static JSObject*
|
static JSObject*
|
||||||
|
|
@ -28,10 +22,6 @@ class ErrorObject : public NativeObject
|
||||||
static JSObject*
|
static JSObject*
|
||||||
createConstructor(JSContext* cx, JSProtoKey key);
|
createConstructor(JSContext* cx, JSProtoKey key);
|
||||||
|
|
||||||
/* For access to createProto. */
|
|
||||||
friend JSObject*
|
|
||||||
js::InitExceptionClasses(JSContext* cx, HandleObject global);
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
|
init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
|
||||||
ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName, HandleObject stack,
|
ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName, HandleObject stack,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue