Bug 923836 - Set aside the first 3 reserved slots of global objects for application use. r=Waldo.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 14 Oct 2013 15:24:11 -0500
changeset 164534 0a16850fbd85f06db425675422a2700667b2d146
parent 164533 57cfa6f88b8e00596db0d62e90d8a9419298fc6d
child 164535 27921f21cddf7abbba678f62e4a348f280312454
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs923836
milestone27.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 923836 - Set aside the first 3 reserved slots of global objects for application use. r=Waldo.
js/public/Class.h
js/src/builtin/Intl.cpp
js/src/builtin/TypedObject.cpp
js/src/jsapi.cpp
js/src/jsiter.cpp
js/src/jsmath.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsproxy.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -553,19 +553,19 @@ struct JSClass {
 // ECMA-262 requires that most constructors used internally create objects
 // with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
 // member initial value.  The "original ... value" verbiage is there because
 // in ECMA-262, global properties naming class objects are read/write and
 // deleteable, for the most part.
 //
 // Implementing this efficiently requires that global objects have classes
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
-// prevously allowed, but is now an ES5 violation and thus unsupported.
+// previously allowed, but is now an ES5 violation and thus unsupported.
 //
-#define JSCLASS_GLOBAL_SLOT_COUNT      (JSProto_LIMIT * 3 + 26)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (3 + JSProto_LIMIT * 3 + 26)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -2033,25 +2033,25 @@ js_InitIntlClass(JSContext *cx, HandleOb
         if (!InitCollatorClass(cx, Intl, global))
             return nullptr;
         if (!InitNumberFormatClass(cx, Intl, global))
             return nullptr;
         if (!InitDateTimeFormatClass(cx, Intl, global))
             return nullptr;
     }
 
-    MarkStandardClassInitializedNoProto(global, &IntlClass);
+    global->markStandardClassInitializedNoProto(&IntlClass);
 
     return Intl;
 }
 
 bool
 GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
 {
     RootedObject Intl(cx);
     Intl = NewObjectWithGivenProto(cx, &IntlClass, global->getOrCreateObjectPrototype(cx),
                                    global, SingletonObject);
     if (!Intl)
         return false;
 
-    global->setReservedSlot(JSProto_Intl, ObjectValue(*Intl));
+    global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
     return true;
 }
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1328,17 +1328,17 @@ GlobalObject::initDataObject(JSContext *
 
     if (!LinkConstructorAndPrototype(cx, DataCtor, DataProto))
         return false;
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Data,
                                        DataCtor, DataProto))
         return false;
 
-    global->setReservedSlot(JSProto_Data, ObjectValue(*DataCtor));
+    global->setConstructor(JSProto_Data, ObjectValue(*DataCtor));
     return true;
 }
 
 bool
 GlobalObject::initTypeObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedObject TypeProto(cx, global->getOrCreateDataObject(cx));
     if (!TypeProto)
@@ -1353,28 +1353,28 @@ GlobalObject::initTypeObject(JSContext *
 
     if (!LinkConstructorAndPrototype(cx, TypeCtor, TypeProto))
         return false;
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_Type,
                                        TypeCtor, TypeProto))
         return false;
 
-    global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor));
+    global->setConstructor(JSProto_Type, ObjectValue(*TypeCtor));
     return true;
 }
 
 bool
 GlobalObject::initArrayTypeObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedFunction ctor(cx,
         global->createConstructor(cx, ArrayType::construct,
                                   cx->names().ArrayType, 2));
 
-    global->setReservedSlot(JSProto_ArrayTypeObject, ObjectValue(*ctor));
+    global->setConstructor(JSProto_ArrayTypeObject, ObjectValue(*ctor));
     return true;
 }
 
 static JSObject *
 SetupComplexHeirarchy(JSContext *cx, Handle<GlobalObject*> global, JSProtoKey protoKey,
                       HandleObject complexObject, MutableHandleObject proto,
                       MutableHandleObject protoProto)
 {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1548,16 +1548,17 @@ JS_ResolveStandardClass(JSContext *cx, H
     JSRuntime *rt;
     JSAtom *atom;
     const JSStdName *stdnm;
     unsigned i;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
+    JS_ASSERT(obj->is<GlobalObject>());
     *resolved = false;
 
     rt = cx->runtime();
     if (!rt->hasContexts() || !JSID_IS_ATOM(id))
         return true;
 
     RootedString idstr(cx, JSID_TO_STRING(id));
 
@@ -1617,36 +1618,36 @@ JS_ResolveStandardClass(JSContext *cx, H
         }
     }
 
     if (stdnm) {
         /*
          * If this standard class is anonymous, then we don't want to resolve
          * by name.
          */
-        JS_ASSERT(obj->is<GlobalObject>());
         if (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS)
             return true;
 
-        if (IsStandardClassResolved(obj, stdnm->clasp))
+        if (obj->as<GlobalObject>().isStandardClassResolved(stdnm->clasp))
             return true;
 
         if (!stdnm->init(cx, obj))
             return false;
         *resolved = true;
     }
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_EnumerateStandardClasses(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
+    MOZ_ASSERT(obj->is<GlobalObject>());
 
     /*
      * Check whether we need to bind 'undefined' and define it if so.
      * Since ES5 15.1.1.3 undefined can't be deleted.
      */
     HandlePropertyName undefinedName = cx->names().undefined;
     RootedValue undefinedValue(cx, UndefinedValue());
     if (!obj->nativeContains(cx, undefinedName) &&
@@ -1654,17 +1655,17 @@ JS_EnumerateStandardClasses(JSContext *c
                                   JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY)) {
         return false;
     }
 
     /* Initialize any classes that have not been initialized yet. */
     for (unsigned i = 0; standard_class_atoms[i].init; i++) {
         const JSStdName &stdnm = standard_class_atoms[i];
-        if (!js::IsStandardClassResolved(obj, stdnm.clasp)) {
+        if (!obj->as<GlobalObject>().isStandardClassResolved(stdnm.clasp)) {
             if (!stdnm.init(cx, obj))
                 return false;
         }
     }
 
     return true;
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1944,30 +1944,30 @@ GlobalObject::initIteratorClasses(JSCont
                                                           JSFunction::NATIVE_CTOR, global, name,
                                                           &function.toObject()));
         if (!genFunction)
             return false;
         if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
             return false;
 
         global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
-        global->setSlot(JSProto_GeneratorFunction, ObjectValue(*genFunction));
-        global->setSlot(JSProto_GeneratorFunction + JSProto_LIMIT, ObjectValue(*genFunctionProto));
+        global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
+        global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
     }
 
     if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
         proto = global->createBlankPrototype(cx, &StopIterationObject::class_);
         if (!proto || !JSObject::freeze(cx, proto))
             return false;
 
         /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
         if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
             return false;
 
-        MarkStandardClassInitializedNoProto(global, &StopIterationObject::class_);
+        global->markStandardClassInitializedNoProto(&StopIterationObject::class_);
     }
 
     return true;
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
 {
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1490,12 +1490,12 @@ js_InitMathClass(JSContext *cx, HandleOb
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, Math, math_static_methods))
         return nullptr;
     if (!JS_DefineConstDoubles(cx, Math, math_constants))
         return nullptr;
 
-    MarkStandardClassInitializedNoProto(obj, &MathClass);
+    obj->as<GlobalObject>().markStandardClassInitializedNoProto(&MathClass);
 
     return Math;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2181,18 +2181,19 @@ DefineStandardSlot(JSContext *cx, Handle
          * Initializing an actual standard class on a global object. If the
          * property is not yet present, force it into a new one bound to a
          * reserved slot. Otherwise, go through the normal property path.
          */
         JS_ASSERT(obj->is<GlobalObject>());
         JS_ASSERT(obj->isNative());
 
         if (!obj->nativeLookup(cx, id)) {
-            uint32_t slot = 2 * JSProto_LIMIT + key;
-            obj->setReservedSlot(slot, v);
+            obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
+
+            uint32_t slot = GlobalObject::constructorPropertySlot(key);
             if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
                 return false;
             AddTypePropertyId(cx, obj, id, v);
 
             named = true;
             return true;
         }
     }
@@ -2204,29 +2205,29 @@ DefineStandardSlot(JSContext *cx, Handle
 
 static void
 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
 {
     JS_ASSERT(!obj->getParent());
     if (!obj->is<GlobalObject>())
         return;
 
-    obj->setReservedSlot(key, ObjectOrNullValue(cobj));
-    obj->setReservedSlot(JSProto_LIMIT + key, ObjectOrNullValue(proto));
+    obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
+    obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
 }
 
 static void
 ClearClassObject(JSObject *obj, JSProtoKey key)
 {
     JS_ASSERT(!obj->getParent());
     if (!obj->is<GlobalObject>())
         return;
 
-    obj->setSlot(key, UndefinedValue());
-    obj->setSlot(JSProto_LIMIT + key, UndefinedValue());
+    obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
+    obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
 }
 
 JSObject *
 js::DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
                                   JSObject *protoProto, const Class *clasp,
                                   Native constructor, unsigned nargs,
                                   const JSPropertySpec *ps, const JSFunctionSpec *fs,
                                   const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
@@ -2356,47 +2357,16 @@ bad:
         bool succeeded;
         JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded);
     }
     if (cached)
         ClearClassObject(obj, key);
     return nullptr;
 }
 
-/*
- * Lazy standard classes need a way to indicate if they have been initialized.
- * Otherwise, when we delete them, we might accidentally recreate them via a
- * lazy initialization. We use the presence of a ctor or proto in the
- * global object's slot to indicate that they've been constructed, but this only
- * works for classes which have a proto and ctor. Classes which don't have one
- * can call MarkStandardClassInitializedNoProto(), and we can always check
- * whether a class is initialized by calling IsStandardClassResolved().
- */
-bool
-js::IsStandardClassResolved(JSObject *obj, const js::Class *clasp)
-{
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
-
-    /* If the constructor is undefined, then it hasn't been initialized. */
-    return (obj->getReservedSlot(key) != UndefinedValue());
-}
-
-void
-js::MarkStandardClassInitializedNoProto(JSObject *obj, const js::Class *clasp)
-{
-    JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
-
-    /*
-     * We use True so that it's obvious what we're doing (instead of, say,
-     * Null, which might be miscontrued as an error in setting Undefined).
-     */
-    if (obj->getReservedSlot(key) == UndefinedValue())
-        obj->setSlot(key, BooleanValue(true));
-}
-
 JSObject *
 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
              const Class *clasp, Native constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
              JSObject **ctorp, AllocKind ctorKind)
 {
     RootedObject protoProto(cx, protoProto_);
@@ -3039,23 +3009,19 @@ js::SetClassAndProto(JSContext *cx, Hand
 
     obj->setType(type);
     return true;
 }
 
 bool
 js_GetClassObject(ExclusiveContext *cxArg, JSObject *obj, JSProtoKey key, MutableHandleObject objp)
 {
-    RootedObject global(cxArg, &obj->global());
-    if (!global->is<GlobalObject>()) {
-        objp.set(nullptr);
-        return true;
-    }
-
-    Value v = global->getReservedSlot(key);
+    Rooted<GlobalObject*> global(cxArg, &obj->global());
+
+    Value v = global->getConstructor(key);
     if (v.isObject()) {
         objp.set(&v.toObject());
         return true;
     }
 
     // Classes can only be initialized on the main thread.
     if (!cxArg->shouldBeJSContext())
         return false;
@@ -3069,17 +3035,17 @@ js_GetClassObject(ExclusiveContext *cxAr
         objp.set(nullptr);
         return true;
     }
 
     RootedObject cobj(cx, nullptr);
     if (ClassInitializerOp init = lazy_prototype_init[key]) {
         if (!init(cx, global))
             return false;
-        v = global->getReservedSlot(key);
+        v = global->getConstructor(key);
         if (v.isObject())
             cobj = &v.toObject();
     }
 
     objp.set(cobj);
     return true;
 }
 
@@ -3092,18 +3058,18 @@ js_IdentifyClassPrototype(JSObject *obj)
     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).
-    JSObject &global = obj->global();
-    Value v = global.getReservedSlot(JSProto_LIMIT + key);
+    GlobalObject &global = obj->global();
+    Value v = global.getPrototype(key);
     if (v.isObject() && obj == &v.toObject())
         return key;
 
     // False alarm - just an instance.
     return JSProto_Null;
 }
 
 bool
@@ -5234,17 +5200,17 @@ js::IsDelegate(JSContext *cx, HandleObje
 
 JSObject *
 js::GetClassPrototypePure(GlobalObject *global, JSProtoKey protoKey)
 {
     JS_ASSERT(JSProto_Null <= protoKey);
     JS_ASSERT(protoKey < JSProto_LIMIT);
 
     if (protoKey != JSProto_Null) {
-        const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
+        const Value &v = global->getPrototype(protoKey);
         if (v.isObject())
             return &v.toObject();
     }
 
     return nullptr;
 }
 
 /*
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1246,22 +1246,16 @@ DenseRangeRef::mark(JSTracer *trc)
 template <AllowGC allowGC>
 extern bool
 HasOwnProperty(JSContext *cx, LookupGenericOp lookup,
                typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                typename MaybeRooted<jsid, allowGC>::HandleType id,
                typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp);
 
-bool
-IsStandardClassResolved(JSObject *obj, const js::Class *clasp);
-
-void
-MarkStandardClassInitializedNoProto(JSObject *obj, const js::Class *clasp);
-
 typedef JSObject *(*ClassInitializerOp)(JSContext *cx, JS::HandleObject obj);
 
 } /* namespace js */
 
 /*
  * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
  */
 extern const char js_watch_str[];
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -931,25 +931,25 @@ DefineConstructorAndPrototype(JSContext 
     JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */
     JS_ASSERT(ctor);
     JS_ASSERT(proto);
 
     RootedId id(cx, NameToId(ClassName(key, cx)));
     JS_ASSERT(!global->nativeLookup(cx, id));
 
     /* Set these first in case AddTypePropertyId looks for this class. */
-    global->setSlot(key, ObjectValue(*ctor));
-    global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto));
-    global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
+    global->setConstructor(key, ObjectValue(*ctor));
+    global->setPrototype(key, ObjectValue(*proto));
+    global->setConstructorPropertySlot(key, ObjectValue(*ctor));
 
     types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
-    if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) {
-        global->setSlot(key, UndefinedValue());
-        global->setSlot(key + JSProto_LIMIT, UndefinedValue());
-        global->setSlot(key + JSProto_LIMIT * 2, UndefinedValue());
+    if (!global->addDataProperty(cx, id, GlobalObject::constructorPropertySlot(key), 0)) {
+        global->setConstructor(key, UndefinedValue());
+        global->setPrototype(key, UndefinedValue());
+        global->setConstructorPropertySlot(key, UndefinedValue());
         return false;
     }
 
     return true;
 }
 
 inline bool
 ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -881,12 +881,12 @@ js_InitJSONClass(JSContext *cx, HandleOb
 
     if (!JS_DefineProperty(cx, global, js_JSON_str, OBJECT_TO_JSVAL(JSON),
                            JS_PropertyStub, JS_StrictPropertyStub, 0))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
         return nullptr;
 
-    MarkStandardClassInitializedNoProto(global, &JSONClass);
+    global->markStandardClassInitializedNoProto(&JSONClass);
 
     return JSON;
 }
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -3288,24 +3288,24 @@ static const JSFunctionSpec static_metho
     JS_FN("create",         proxy_create,          2, 0),
     JS_FN("createFunction", proxy_createFunction,  3, 0),
     JS_FS_END
 };
 
 JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, HandleObject obj)
 {
-    Rooted<GlobalObject*> global(cx);
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, proxy, cx->names().Proxy, 2);
     if (!ctor)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, ctor, static_methods))
         return nullptr;
     if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(ctor),
                            JS_PropertyStub, JS_StrictPropertyStub, 0)) {
         return nullptr;
     }
 
-    MarkStandardClassInitializedNoProto(obj, &ProxyObject::uncallableClass_);
+    global->markStandardClassInitializedNoProto(&ProxyObject::uncallableClass_);
     return ctor;
 }
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -356,19 +356,19 @@ GlobalObject::initFunctionAndObjectClass
         !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
         !DefinePropertiesAndBrand(cx, functionProto, nullptr, function_methods) ||
         !DefinePropertiesAndBrand(cx, functionCtor, nullptr, nullptr))
     {
         return nullptr;
     }
 
     /* Add the global Function and Object properties now. */
-    if (!self->addDataProperty(cx, cx->names().Object, JSProto_Object + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, cx->names().Object, constructorPropertySlot(JSProto_Object), 0))
         return nullptr;
-    if (!self->addDataProperty(cx, cx->names().Function, JSProto_Function + JSProto_LIMIT * 2, 0))
+    if (!self->addDataProperty(cx, cx->names().Function, constructorPropertySlot(JSProto_Function), 0))
         return nullptr;
 
     /* Heavy lifting done, but lingering tasks remain. */
 
     /* ES5 15.1.2.1. */
     RootedId evalId(cx, NameToId(cx->names().eval));
     JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
     if (!evalobj)
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -30,49 +30,57 @@ js_InitTypedObjectClasses(JSContext *cx,
 
 namespace js {
 
 class Debugger;
 
 /*
  * Global object slots are reserved as follows:
  *
- * [0, JSProto_LIMIT)
+ * [0, APPLICATION_SLOTS)
+ *   Pre-reserved slots in all global objects set aside for the embedding's
+ *   use. As with all reserved slots these start out as UndefinedValue() and
+ *   are traced for GC purposes. Apart from that the engine never touches
+ *   these slots, so the embedding can do whatever it wants with them.
+ * [APPLICATION_SLOTS, APPLICATION_SLOTS + JSProto_LIMIT)
  *   Stores the original value of the constructor for the corresponding
  *   JSProtoKey.
- * [JSProto_LIMIT, 2 * JSProto_LIMIT)
+ * [APPLICATION_SLOTS + JSProto_LIMIT, APPLICATION_SLOTS + 2 * JSProto_LIMIT)
  *   Stores the prototype, if any, for the constructor for the corresponding
  *   JSProtoKey offset from JSProto_LIMIT.
- * [2 * JSProto_LIMIT, 3 * JSProto_LIMIT)
+ * [APPLICATION_SLOTS + 2 * JSProto_LIMIT, APPLICATION_SLOTS + 3 * JSProto_LIMIT)
  *   Stores the current value of the global property named for the JSProtoKey
  *   for the corresponding JSProtoKey offset from 2 * JSProto_LIMIT.
- * [3 * JSProto_LIMIT, RESERVED_SLOTS)
+ * [APPLICATION_SLOTS + 3 * JSProto_LIMIT, RESERVED_SLOTS)
  *   Various one-off values: ES5 13.2.3's [[ThrowTypeError]], RegExp statics,
  *   the original eval for this global object (implementing |var eval =
  *   otherWindow.eval; eval(...)| as an indirect eval), a bit indicating
  *   whether this object has been cleared (see JS_ClearScope), and a cache for
  *   whether eval is allowed (per the global's Content Security Policy).
  *
- * The first two ranges are necessary to implement js::FindClassObject,
- * and spec language speaking in terms of "the original Array prototype
- * object", or "as if by the expression new Array()" referring to the original
- * Array constructor. The third range stores the (writable and even deletable)
- * Object, Array, &c. properties (although a slot won't be used again if its
- * property is deleted and readded).
+ * The first two JSProto_LIMIT-sized ranges are necessary to implement
+ * js::FindClassObject, and spec language speaking in terms of "the original
+ * Array prototype object", or "as if by the expression new Array()" referring
+ * to the original Array constructor. The third range stores the (writable and
+ * even deletable) Object, Array, &c. properties (although a slot won't be used
+ * again if its property is deleted and readded).
  */
 class GlobalObject : public JSObject
 {
+    /* Count of slots set aside for application use. */
+    static const unsigned APPLICATION_SLOTS = 3;
+
     /*
      * Count of slots to store built-in constructors, prototypes, and initial
      * visible properties for the constructors.
      */
     static const unsigned STANDARD_CLASS_SLOTS  = JSProto_LIMIT * 3;
 
     /* Various function values needed by the engine. */
-    static const unsigned EVAL                    = STANDARD_CLASS_SLOTS;
+    static const unsigned EVAL                    = APPLICATION_SLOTS + STANDARD_CLASS_SLOTS;
     static const unsigned CREATE_DATAVIEW_FOR_THIS = EVAL + 1;
     static const unsigned THROWTYPEERROR          = CREATE_DATAVIEW_FOR_THIS + 1;
     static const unsigned PROTO_GETTER            = THROWTYPEERROR + 1;
 
     /*
      * Instances of the internal createArrayFromBuffer function used by the
      * typed array code, one per typed array element type.
      */
@@ -117,33 +125,16 @@ class GlobalObject : public JSObject
     ::js_InitObjectClass(JSContext *cx, js::HandleObject);
     friend JSObject *
     ::js_InitFunctionClass(JSContext *cx, js::HandleObject);
 
     /* Initialize the Function and Object classes.  Must only be called once! */
     JSObject *
     initFunctionAndObjectClasses(JSContext *cx);
 
-    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
-        JS_ASSERT(getSlotRef(key).isUndefined());
-        JS_ASSERT(getSlotRef(JSProto_LIMIT + key).isUndefined());
-        JS_ASSERT(getSlotRef(2 * JSProto_LIMIT + key).isUndefined());
-        setSlot(key, ObjectValue(*ctor));
-        setSlot(JSProto_LIMIT + key, ObjectValue(*proto));
-        setSlot(2 * JSProto_LIMIT + key, ObjectValue(*ctor));
-    }
-
-    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
-        setDetailsForKey(JSProto_Object, ctor, proto);
-    }
-
-    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
-        setDetailsForKey(JSProto_Function, ctor, proto);
-    }
-
     void setThrowTypeError(JSFunction *fun) {
         JS_ASSERT(getSlotRef(THROWTYPEERROR).isUndefined());
         setSlot(THROWTYPEERROR, ObjectValue(*fun));
     }
 
     void setOriginalEval(JSObject *evalobj) {
         JS_ASSERT(getSlotRef(EVAL).isUndefined());
         setSlot(EVAL, ObjectValue(*evalobj));
@@ -154,38 +145,106 @@ class GlobalObject : public JSObject
         setSlot(PROTO_GETTER, ObjectValue(*protoGetter));
     }
 
     void setIntrinsicsHolder(JSObject *obj) {
         JS_ASSERT(getSlotRef(INTRINSICS).isUndefined());
         setSlot(INTRINSICS, ObjectValue(*obj));
     }
 
+  public:
     Value getConstructor(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(key);
+        return getSlot(APPLICATION_SLOTS + key);
+    }
+
+    void setConstructor(JSProtoKey key, const Value &v) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        setSlot(APPLICATION_SLOTS + key, v);
     }
 
     Value getPrototype(JSProtoKey key) const {
         JS_ASSERT(key <= JSProto_LIMIT);
-        return getSlot(JSProto_LIMIT + key);
+        return getSlot(APPLICATION_SLOTS + JSProto_LIMIT + key);
+    }
+
+    void setPrototype(JSProtoKey key, const Value &value) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        setSlot(APPLICATION_SLOTS + JSProto_LIMIT + key, value);
+    }
+
+    static uint32_t constructorPropertySlot(JSProtoKey key) {
+        JS_ASSERT(key <= JSProto_LIMIT);
+        return APPLICATION_SLOTS + JSProto_LIMIT * 2 + key;
+    }
+
+    Value getConstructorPropertySlot(JSProtoKey key) {
+        return getSlot(constructorPropertySlot(key));
+    }
+
+    void setConstructorPropertySlot(JSProtoKey key, const Value &ctor) {
+        setSlot(constructorPropertySlot(key), ctor);
     }
 
     bool classIsInitialized(JSProtoKey key) const {
         bool inited = !getConstructor(key).isUndefined();
         JS_ASSERT(inited == !getPrototype(key).isUndefined());
         return inited;
     }
 
     bool functionObjectClassesInitialized() const {
         bool inited = classIsInitialized(JSProto_Function);
         JS_ASSERT(inited == classIsInitialized(JSProto_Object));
         return inited;
     }
 
+    /*
+     * Lazy standard classes need a way to indicate they have been initialized.
+     * Otherwise, when we delete them, we might accidentally recreate them via
+     * a lazy initialization. We use the presence of a ctor or proto in the
+     * global object's slot to indicate that they've been constructed, but this
+     * only works for classes which have a proto and ctor. Classes which don't
+     * have one can call markStandardClassInitializedNoProto(), and we can
+     * always check whether a class is initialized by calling
+     * isStandardClassResolved().
+     */
+    bool isStandardClassResolved(const js::Class *clasp) const {
+        JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
+
+        // If the constructor is undefined, then it hasn't been initialized.
+        return !getConstructor(key).isUndefined();
+    }
+
+    void markStandardClassInitializedNoProto(const js::Class *clasp) {
+        JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
+
+        // We use true so that it's obvious what we're doing (instead of, say,
+        // null, which might be miscontrued as an error in setting Undefined).
+        if (getConstructor(key).isUndefined())
+            setConstructor(key, BooleanValue(true));
+    }
+
+  private:
+    void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
+        JS_ASSERT(getConstructor(key).isUndefined());
+        JS_ASSERT(getPrototype(key).isUndefined());
+        JS_ASSERT(getConstructorPropertySlot(key).isUndefined());
+        setConstructor(key, ObjectValue(*ctor));
+        setPrototype(key, ObjectValue(*proto));
+        setConstructorPropertySlot(key, ObjectValue(*ctor));
+    }
+
+    void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Object, ctor, proto);
+    }
+
+    void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
+        setDetailsForKey(JSProto_Function, ctor, proto);
+    }
+
     bool arrayClassInitialized() const {
         return classIsInitialized(JSProto_Array);
     }
 
     bool booleanClassInitialized() const {
         return classIsInitialized(JSProto_Boolean);
     }
     bool numberClassInitialized() const {
@@ -344,84 +403,87 @@ class GlobalObject : public JSObject
             return &getPrototype(key).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitExceptionClasses(cx, self))
             return nullptr;
         return &self->getPrototype(key).toObject();
     }
 
     JSObject *getOrCreateIntlObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Intl, initIntlObject);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Intl, initIntlObject);
+    }
+
+    JSObject *getIteratorPrototype() {
+        return &getPrototype(JSProto_Iterator).toObject();
+    }
+
+    JSObject *getOrCreateDataObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Data, initDataObject);
+    }
+
+    JSObject *getOrCreateTypeObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Type, initTypeObject);
+    }
+
+    JSObject *getOrCreateArrayTypeObject(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_ArrayTypeObject,
+                                 initArrayTypeObject);
     }
 
     JSObject *getOrCreateCollatorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, COLLATOR_PROTO, initCollatorProto);
     }
 
     JSObject *getOrCreateNumberFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initNumberFormatProto);
     }
 
     JSObject *getOrCreateDateTimeFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
-    JSObject *getIteratorPrototype() {
-        return &getPrototype(JSProto_Iterator).toObject();
-    }
-
-    JSObject *getOrCreateDataObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Data, initDataObject);
-    }
-
-    JSObject *getOrCreateTypeObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_Type, initTypeObject);
-    }
-
-    JSObject *getOrCreateArrayTypeObject(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_ArrayTypeObject, initArrayTypeObject);
-    }
-
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
         Value v = getSlotRef(slot);
         if (v.isObject())
             return &v.toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!init(cx, self))
             return nullptr;
         return &self->getSlot(slot).toObject();
     }
 
   public:
     JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_LIMIT + JSProto_Iterator, initIteratorClasses);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator,
+                                 initIteratorClasses);
     }
 
     JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorFunctionPrototype(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_LIMIT + JSProto_GeneratorFunction,
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_GeneratorFunction,
                                  initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorFunction(JSContext *cx) {
-        return getOrCreateObject(cx, JSProto_GeneratorFunction, initIteratorClasses);
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_GeneratorFunction,
+                                 initIteratorClasses);
     }
 
     JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, MAP_ITERATOR_PROTO, initMapIteratorProto);
     }
 
     JSObject *getOrCreateSetIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, SET_ITERATOR_PROTO, initSetIteratorProto);