Handle the now ordinary error prototype object in stack

This commit is contained in:
janekptacijarabaci 2018-03-17 16:58:26 +01:00 committed by Roy Tam
commit 4fad420bb8
3 changed files with 48 additions and 31 deletions

View file

@ -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;

View file

@ -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();
// 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.
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()));
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;

View file

@ -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);
};