Bug 1029933 - Introduce the concept of "dependent" standard classes and handle them in the ClassSpec infrastructure. r=Waldo
authorBobby Holley <bobbyholley@gmail.com>
Fri, 04 Jul 2014 12:41:28 -0700
changeset 192461 3ff6b5e30818ab1ecaf861d850d50d74cab88692
parent 192460 650197ade3b341e9f5f2a61d4684a500c9f17ee6
child 192462 30c45b56a2ef3e4a3ceac6d9328922606bc1cfd5
push id27086
push userttaubert@mozilla.com
push dateSun, 06 Jul 2014 16:11:25 +0000
treeherdermozilla-central@9f59e39f70a5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1029933
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1029933 - Introduce the concept of "dependent" standard classes and handle them in the ClassSpec infrastructure. r=Waldo
js/src/jsfriendapi.h
js/src/jsobj.h
js/src/vm/GlobalObject.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -604,16 +604,33 @@ GetObjectClass(JSObject *obj)
 }
 
 inline const JSClass *
 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;
 }
 
 inline bool
 IsOuterObject(JSObject *obj) {
     return !!GetObjectClass(obj)->ext.innerObject;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1244,19 +1244,16 @@ typedef JSObject *(*ClassInitializerOp)(
 
 /* Fast access to builtin constructors and prototypes. */
 bool
 GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp);
 
 bool
 GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp);
 
-const Class *
-ProtoKeyToClass(JSProtoKey key);
-
 JSObject *
 GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey);
 
 extern bool
 SetClassAndProto(JSContext *cx, HandleObject obj,
                  const Class *clasp, Handle<TaggedProto> proto, bool *succeeded);
 
 /*
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -58,17 +58,17 @@ js_InitViaClassSpec(JSContext *cx, Handl
 static const ProtoTableEntry protoTable[JSProto_LIMIT] = {
 #define INIT_FUNC(name,code,init,clasp) { clasp, init },
 #define INIT_FUNC_DUMMY(name,code,init,clasp) { nullptr, nullptr },
     JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY)
 #undef INIT_FUNC_DUMMY
 #undef INIT_FUNC
 };
 
-const js::Class *
+JS_FRIEND_API(const js::Class *)
 js::ProtoKeyToClass(JSProtoKey key)
 {
     MOZ_ASSERT(key < JSProto_LIMIT);
     return protoTable[key].clasp;
 }
 
 // This method is not in the header file to avoid having to include
 // TypedObject.h from GlobalObject.h. It is not generally perf
@@ -161,28 +161,31 @@ GlobalObject::resolveConstructor(JSConte
 
     RootedId id(cx, NameToId(ClassName(key, cx)));
     if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0))
         return false;
 
     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.
     if (proto && !LinkConstructorAndPrototype(cx, ctor, proto))
         return false;
 
     // Call the post-initialization hook, if provided.
     if (clasp->spec.finishInit && !clasp->spec.finishInit(cx, ctor, proto))
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -611,19 +611,18 @@ JSXrayTraits::resolveOwnProperty(JSConte
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     // 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)) {
         RootedObject constructor(cx);
         {
             JSAutoCompartment ac(cx, target);
             if (!JS_GetClassObject(cx, protoKey, &constructor))
                 return false;
@@ -633,16 +632,21 @@ JSXrayTraits::resolveOwnProperty(JSConte
         desc.object().set(wrapper);
         desc.setAttributes(0);
         desc.setGetter(nullptr);
         desc.setSetter(nullptr);
         desc.value().setObject(*constructor);
         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))
         return true;
     Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id));
 
     // Scan through the functions.
     const JSFunctionSpec *fsMatch = nullptr;
@@ -862,18 +866,27 @@ JSXrayTraits::enumerateNames(JSContext *
         }
 
         // The rest of this function applies only to prototypes.
         return true;
     }
 
     // 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) {
         RootedString str(cx, JS_InternString(cx, fs->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
@@ -888,18 +901,17 @@ JSXrayTraits::enumerateNames(JSContext *
                    "module owner about adding test coverage");
         RootedString str(cx, JS_InternString(cx, ps->name));
         if (!str)
             return false;
         if (!props.append(INTERNED_STRING_TO_JSID(cx, str)))
             return false;
     }
 
-    // Add the 'constructor' property.
-    return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR));
+    return true;
 }
 
 JSObject*
 JSXrayTraits::createHolder(JSContext *cx, JSObject *wrapper)
 {
     RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper));
     RootedObject target(cx, getTargetObject(wrapper));
     RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass,