summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--js/src/jsexn.h9
-rw-r--r--js/src/vm/ErrorObject.cpp69
-rw-r--r--js/src/vm/ErrorObject.h1
3 files changed, 48 insertions, 31 deletions
diff --git a/js/src/jsexn.h b/js/src/jsexn.h
index a63c70909..ae6335209 100644
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -85,10 +85,17 @@ ExnTypeFromProtoKey(JSProtoKey key)
{
JSExnType type = static_cast<JSExnType>(key - JSProto_Error);
MOZ_ASSERT(type >= JSEXN_ERR);
- MOZ_ASSERT(type < JSEXN_WARN);
+ MOZ_ASSERT(type < JSEXN_ERROR_LIMIT);
return type;
}
+static inline bool
+IsErrorProtoKey(JSProtoKey key)
+{
+ JSExnType type = static_cast<JSExnType>(key - JSProto_Error);
+ return type >= JSEXN_ERR && type < JSEXN_ERROR_LIMIT;
+}
+
class AutoClearPendingException
{
JSContext* cx;
diff --git a/js/src/vm/ErrorObject.cpp b/js/src/vm/ErrorObject.cpp
index 47b61b57b..d8d29830b 100644
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -164,29 +164,25 @@ js::ErrorObject::getOrCreateErrorReport(JSContext* cx)
}
static bool
-ErrorObject_checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName,
- MutableHandle<ErrorObject*> error)
+FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result)
{
- const Value& thisValue = args.thisv();
-
- if (!thisValue.isObject()) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
- InformalValueTypeName(thisValue));
- return false;
- }
-
- // Walk up the prototype chain until we find the first ErrorObject that has
- // the slots we need. This allows us to support the poor-man's subclassing
- // of error: Object.create(Error.prototype).
-
- RootedObject target(cx, CheckedUnwrap(&thisValue.toObject()));
+ // Walk up the prototype chain until we find an error object instance or
+ // prototype object. This allows code like:
+ // Object.create(Error.prototype).stack
+ // or
+ // function NYI() { }
+ // NYI.prototype = new Error;
+ // (new NYI).stack
+ // to continue returning stacks that are useless, but at least don't throw.
+
+ RootedObject target(cx, CheckedUnwrap(obj));
if (!target) {
JS_ReportErrorASCII(cx, "Permission denied to access object");
return false;
}
RootedObject proto(cx);
- while (!target->is<ErrorObject>()) {
+ while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
if (!GetPrototype(cx, target, &proto))
return false;
@@ -194,7 +190,7 @@ ErrorObject_checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName
// We walked the whole prototype chain and did not find an Error
// object.
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
- js_Error_str, fnName, thisValue.toObject().getClass()->name);
+ js_Error_str, "(get stack)", obj->getClass()->name);
return false;
}
@@ -205,19 +201,40 @@ ErrorObject_checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName
}
}
- error.set(&target->as<ErrorObject>());
+ result.set(target);
return true;
}
+
+static MOZ_ALWAYS_INLINE bool
+IsObject(HandleValue v)
+{
+ return v.isObject();
+}
+
/* static */ bool
js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- Rooted<ErrorObject*> error(cx);
- if (!ErrorObject_checkAndUnwrapThis(cx, args, "(get stack)", &error))
+ // We accept any object here, because of poor-man's subclassing of Error.
+ return CallNonGenericMethod<IsObject, getStack_impl>(cx, args);
+}
+
+/* static */ bool
+js::ErrorObject::getStack_impl(JSContext* cx, const CallArgs& args)
+{
+ RootedObject thisObj(cx, &args.thisv().toObject());
+
+ RootedObject obj(cx);
+ if (!FindErrorInstanceOrPrototype(cx, thisObj, &obj))
return false;
- RootedObject savedFrameObj(cx, error->stack());
+ if (!obj->is<ErrorObject>()) {
+ args.rval().setString(cx->runtime()->emptyString);
+ return true;
+ }
+
+ RootedObject savedFrameObj(cx, obj->as<ErrorObject>().stack());
RootedString stackString(cx);
if (!BuildStackString(cx, savedFrameObj, &stackString))
return false;
@@ -245,12 +262,6 @@ js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp)
return true;
}
-static MOZ_ALWAYS_INLINE bool
-IsObject(HandleValue v)
-{
- return v.isObject();
-}
-
/* static */ bool
js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp)
{
@@ -262,9 +273,7 @@ js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp)
/* static */ bool
js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args)
{
- const Value& thisValue = args.thisv();
- MOZ_ASSERT(thisValue.isObject());
- RootedObject thisObj(cx, &thisValue.toObject());
+ RootedObject thisObj(cx, &args.thisv().toObject());
if (!args.requireAtLeast(cx, "(set stack)", 1))
return false;
diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h
index cfd804e2e..0c2d00610 100644
--- a/js/src/vm/ErrorObject.h
+++ b/js/src/vm/ErrorObject.h
@@ -106,6 +106,7 @@ class ErrorObject : public NativeObject
// Getter and setter for the Error.prototype.stack accessor.
static bool getStack(JSContext* cx, unsigned argc, Value* vp);
+ static bool getStack_impl(JSContext* cx, const CallArgs& args);
static bool setStack(JSContext* cx, unsigned argc, Value* vp);
static bool setStack_impl(JSContext* cx, const CallArgs& args);
};