Bug 975277 - Introduce a mechanism to identify instances of standard classes. r=luke
authorBobby Holley <bobbyholley@gmail.com>
Fri, 21 Feb 2014 16:03:11 -0800
changeset 170339 84904662e2d567085b5c63f57aa2db45580940db
parent 170338 02dffb9d2748f78b2e171a94834f130d36879852
child 170340 924690f9d81b677ab55fa2d52aee828b4191398f
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersluke
bugs975277
milestone30.0a1
Bug 975277 - Introduce a mechanism to identify instances of standard classes. r=luke
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsworkers.cpp
js/xpconnect/wrappers/ChromeObjectWrapper.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1347,23 +1347,16 @@ JS_GetClassObject(JSContext *cx, JSProto
 JS_PUBLIC_API(bool)
 JS_GetClassPrototype(JSContext *cx, JSProtoKey key, MutableHandleObject objp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return js_GetClassPrototype(cx, key, objp);
 }
 
-JS_PUBLIC_API(JSProtoKey)
-JS_IdentifyClassPrototype(JSObject *obj)
-{
-    JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
-    return js_IdentifyClassPrototype(obj);
-}
-
 extern JS_PUBLIC_API(JSProtoKey)
 JS_IdToProtoKey(JSContext *cx, HandleId id)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     if (!JSID_IS_ATOM(id))
         return JSProto_Null;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1765,18 +1765,33 @@ extern JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext *cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassObject(JSContext *cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
 extern JS_PUBLIC_API(bool)
 JS_GetClassPrototype(JSContext *cx, JSProtoKey key, JS::MutableHandle<JSObject*> objp);
 
+namespace JS {
+
+/*
+ * Determine if the given object is an instance or prototype for a standard
+ * class. If so, return the associated JSProtoKey. If not, return JSProto_Null.
+ */
+
 extern JS_PUBLIC_API(JSProtoKey)
-JS_IdentifyClassPrototype(JSObject *obj);
+IdentifyStandardInstance(JSObject *obj);
+
+extern JS_PUBLIC_API(JSProtoKey)
+IdentifyStandardPrototype(JSObject *obj);
+
+extern JS_PUBLIC_API(JSProtoKey)
+IdentifyStandardInstanceOrPrototype(JSObject *obj);
+
+} /* namespace JS */
 
 extern JS_PUBLIC_API(JSProtoKey)
 JS_IdToProtoKey(JSContext *cx, JS::HandleId id);
 
 /*
  * Returns the original value of |Function.prototype| from the global object in
  * which |forObj| was created.
  */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3249,39 +3249,52 @@ js_GetClassPrototype(ExclusiveContext *c
         return false;
 
     Value v = global->getPrototype(key);
     if (v.isObject())
         protop.set(&v.toObject());
     return true;
 }
 
-JSProtoKey
-js_IdentifyClassPrototype(JSObject *obj)
-{
-    // First, get the key off the JSClass. This tells us which prototype we
-    // _might_ be. But we still don't know for sure, since the prototype shares
-    // its JSClass with instances.
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
-    if (key == JSProto_Null)
-        return JSProto_Null;
-
-    // Now, see if the cached object matches |obj|.
-    //
-    // Note that standard class objects are cached in the range [0, JSProto_LIMIT),
-    // and the prototypes are cached in [JSProto_LIMIT, 2*JSProto_LIMIT).
+static bool
+IsStandardPrototype(JSObject *obj, JSProtoKey key)
+{
     GlobalObject &global = obj->global();
     Value v = global.getPrototype(key);
-    if (v.isObject() && obj == &v.toObject())
+    return v.isObject() && obj == &v.toObject();
+}
+
+JSProtoKey
+JS::IdentifyStandardInstance(JSObject *obj)
+{
+    // Note: The prototype shares its JSClass with instances.
+    JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
+    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
+    if (key != JSProto_Null && !IsStandardPrototype(obj, key))
         return key;
-
-    // False alarm - just an instance.
     return JSProto_Null;
 }
 
+JSProtoKey
+JS::IdentifyStandardPrototype(JSObject *obj)
+{
+    // Note: The prototype shares its JSClass with instances.
+    JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
+    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
+    if (key != JSProto_Null && IsStandardPrototype(obj, key))
+        return key;
+    return JSProto_Null;
+}
+
+JSProtoKey
+JS::IdentifyStandardInstanceOrPrototype(JSObject *obj)
+{
+    return JSCLASS_CACHED_PROTO_KEY(obj->getClass());
+}
+
 bool
 js_FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
 {
     MOZ_ASSERT(clasp);
     JSProtoKey protoKey = GetClassProtoKey(clasp);
     RootedId id(cx);
     if (protoKey != JSProto_Null) {
         JS_ASSERT(JSProto_Null < protoKey);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1324,23 +1324,16 @@ extern bool
 js_GetClassObject(js::ExclusiveContext *cx, JSProtoKey key,
                   js::MutableHandleObject objp);
 
 extern bool
 js_GetClassPrototype(js::ExclusiveContext *cx, JSProtoKey key,
                      js::MutableHandleObject objp);
 
 /*
- * Determine if the given object is a prototype for a standard class. If so,
- * return the associated JSProtoKey. If not, return JSProto_Null.
- */
-extern JSProtoKey
-js_IdentifyClassPrototype(JSObject *obj);
-
-/*
  * Property-lookup-based access to interface and prototype objects for classes.
  * If the class is built-in (and has a non-null JSProtoKey), these forward to
  * js_GetClass{Object,Prototype}.
  */
 
 bool
 js_FindClassObject(js::ExclusiveContext *cx, js::MutableHandleObject protop,
                    const js::Class *clasp);
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -635,17 +635,17 @@ GlobalWorkerThreadState::finishParseTask
          !iter.done();
          iter.next())
     {
         types::TypeObject *object = iter.get<types::TypeObject>();
         TaggedProto proto(object->proto());
         if (!proto.isObject())
             continue;
 
-        JSProtoKey key = js_IdentifyClassPrototype(proto.toObject());
+        JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
         if (key == JSProto_Null)
             continue;
 
         JSObject *newProto = GetClassPrototypePure(&parseTask->scopeChain->global(), key);
         JS_ASSERT(newProto);
 
         object->setProtoUnchecked(newProto);
     }
--- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
+++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp
@@ -31,17 +31,17 @@ AllowedByBase(JSContext *cx, HandleObjec
 }
 
 static bool
 PropIsFromStandardPrototype(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
 {
     MOZ_ASSERT(desc.object());
     RootedObject unwrapped(cx, js::UncheckedUnwrap(desc.object()));
     JSAutoCompartment ac(cx, unwrapped);
-    return JS_IdentifyClassPrototype(unwrapped) != JSProto_Null;
+    return IdentifyStandardPrototype(unwrapped) != JSProto_Null;
 }
 
 // Note that we're past the policy enforcement stage, here, so we can query
 // ChromeObjectWrapperBase and get an unfiltered view of the underlying object.
 // This lets us determine whether the property we would have found (given a
 // transparent wrapper) would have come off a standard prototype.
 static bool
 PropIsFromStandardPrototype(JSContext *cx, HandleObject wrapper,
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -169,17 +169,17 @@ WrapperFactory::PrepareForWrapping(JSCon
     // subsumes call below.
     bool subsumes = AccessCheck::subsumes(js::GetContextCompartment(cx),
                                           js::GetObjectCompartment(obj));
     XrayType xrayType = GetXrayType(obj);
     if (!subsumes && xrayType == NotXray) {
         JSProtoKey key = JSProto_Null;
         {
             JSAutoCompartment ac(cx, obj);
-            key = JS_IdentifyClassPrototype(obj);
+            key = IdentifyStandardPrototype(obj);
         }
         if (key != JSProto_Null) {
             RootedObject homeProto(cx);
             if (!JS_GetClassPrototype(cx, key, &homeProto))
                 return nullptr;
             MOZ_ASSERT(homeProto);
             // No need to double-wrap here. We should never have waivers to
             // COWs.