From 8095d9d618920705e897375f8655759e83810be3 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 4 Jul 2014 12:41:28 -0700 Subject: [PATCH] Bug 1029933 - Introduce the concept of "dependent" standard classes and handle them in the ClassSpec infrastructure. r=Waldo --- js/src/jsfriendapi.h | 17 ++++++++++++++++ js/src/jsobj.h | 3 --- js/src/vm/GlobalObject.cpp | 29 +++++++++++++++------------ js/xpconnect/wrappers/XrayWrapper.cpp | 22 +++++++++++++++----- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index f7fd8b68462..9a87a89d3ef 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -609,6 +609,23 @@ GetObjectJSClass(JSObject *obj) return js::Jsvalify(GetObjectClass(obj)); } +JS_FRIEND_API(const Class *) +ProtoKeyToClass(JSProtoKey key); + +// Returns true if the standard class identified by |key| inherits from +// another standard class with the same js::Class. This basically means +// that the various properties described by our js::Class are intended +// to live higher up on the proto chain. +// +// In practice, this only returns true for Error subtypes. +inline bool +StandardClassIsDependent(JSProtoKey key) +{ + JSProtoKey keyFromClass = JSCLASS_CACHED_PROTO_KEY(ProtoKeyToClass(key)); + MOZ_ASSERT(keyFromClass); + return key != keyFromClass; +} + inline bool IsInnerObject(JSObject *obj) { return !!GetObjectClass(obj)->ext.outerObject; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4153f0987f4..1d586f84940 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1249,9 +1249,6 @@ GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject bool GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp); -const Class * -ProtoKeyToClass(JSProtoKey key); - JSObject * GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 037e76f1279..211a4f2777c 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -63,7 +63,7 @@ static const ProtoTableEntry protoTable[JSProto_LIMIT] = { #undef INIT_FUNC }; -const js::Class * +JS_FRIEND_API(const js::Class *) js::ProtoKeyToClass(JSProtoKey key) { MOZ_ASSERT(key < JSProto_LIMIT); @@ -166,18 +166,21 @@ GlobalObject::resolveConstructor(JSContext *cx, Handle global, JS global->setConstructor(key, ObjectValue(*ctor)); global->setConstructorPropertySlot(key, ObjectValue(*ctor)); - // Define any specified functions and properties. - if (const JSFunctionSpec *funs = clasp->spec.prototypeFunctions) { - if (!JS_DefineFunctions(cx, proto, funs)) - return false; - } - if (const JSPropertySpec *props = clasp->spec.prototypeProperties) { - if (!JS_DefineProperties(cx, proto, props)) - return false; - } - if (const JSFunctionSpec *funs = clasp->spec.constructorFunctions) { - if (!JS_DefineFunctions(cx, ctor, funs)) - return false; + // Define any specified functions and properties, unless we're a dependent + // standard class (in which case they live on the prototype). + if (!StandardClassIsDependent(key)) { + if (const JSFunctionSpec *funs = clasp->spec.prototypeFunctions) { + if (!JS_DefineFunctions(cx, proto, funs)) + return false; + } + if (const JSPropertySpec *props = clasp->spec.prototypeProperties) { + if (!JS_DefineProperties(cx, proto, props)) + return false; + } + if (const JSFunctionSpec *funs = clasp->spec.constructorFunctions) { + if (!JS_DefineFunctions(cx, ctor, funs)) + return false; + } } // If the prototype exists, link it with the constructor. diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index b013a32fcd4..9d6eac95155 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -616,9 +616,8 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper, // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. const js::Class *clasp = js::GetObjectClass(target); - JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); - MOZ_ASSERT(protoKey == getProtoKey(holder)); MOZ_ASSERT(clasp->spec.defined()); + JSProtoKey protoKey = getProtoKey(holder); // Handle the 'constructor' property. if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { @@ -638,6 +637,11 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper, return true; } + // Bail out for dependent classes, since all the rest of the properties we'll + // resolve here will live on the parent prototype. + if (js::StandardClassIsDependent(protoKey)) + return true; + // Compute the property name we're looking for. We'll handle indexed // properties when we start supporting arrays. if (!JSID_IS_STRING(id)) @@ -867,8 +871,17 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. const js::Class *clasp = js::GetObjectClass(target); - MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder)); MOZ_ASSERT(clasp->spec.defined()); + JSProtoKey protoKey = getProtoKey(holder); + + // Add the 'constructor' property. + if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR))) + return false; + + // Bail out for dependent classes, since all the rest of the properties we'll + // resolve here will live on the parent prototype. + if (js::StandardClassIsDependent(protoKey)) + return true; // Intern all the strings, and pass theme to the caller. for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) { @@ -893,8 +906,7 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags return false; } - // Add the 'constructor' property. - return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)); + return true; } JSObject*