Bug 825199 - Allow us the define self-hosted properties after everything is set-up properly. r=till
authorTom Schuster <evilpies@gmail.com>
Thu, 11 Dec 2014 19:31:10 +0100
changeset 219222 54938d8a3baa3e9cc67a0662f57d5a98c9c74d16
parent 219221 7066f0d9c11061ed22a9e0b605b3f916f4633734
child 219223 4638c344364dbdddd5c1708e59a90523edf858bd
push id52794
push userevilpies@gmail.com
push dateThu, 11 Dec 2014 18:31:37 +0000
treeherdermozilla-inbound@4638c344364d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs825199
milestone37.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 825199 - Allow us the define self-hosted properties after everything is set-up properly. r=till
js/src/builtin/Object.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/GlobalObject.cpp
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1203,16 +1203,17 @@ static const JSFunctionSpec object_metho
 static const JSPropertySpec object_properties[] = {
 #if JS_HAS_OBJ_PROTO_PROP
     JS_PSGS("__proto__", ProtoGetter, ProtoSetter, 0),
 #endif
     JS_PS_END
 };
 
 static const JSFunctionSpec object_static_methods[] = {
+    JS_SELF_HOSTED_FN("assign",        "ObjectStaticAssign",        2,JSPROP_DEFINE_LATE),
     JS_FN("getPrototypeOf",            obj_getPrototypeOf,          1,0),
     JS_FN("setPrototypeOf",            obj_setPrototypeOf,          2,0),
     JS_FN("getOwnPropertyDescriptor",  obj_getOwnPropertyDescriptor,2,0),
     JS_FN("keys",                      obj_keys,                    1,0),
     JS_FN("is",                        obj_is,                      2,0),
     JS_FN("defineProperty",            obj_defineProperty,          3,0),
     JS_FN("defineProperties",          obj_defineProperties,        2,0),
     JS_FN("create",                    obj_create,                  2,0),
@@ -1222,26 +1223,16 @@ static const JSFunctionSpec object_stati
     JS_FN("preventExtensions",         obj_preventExtensions,       1,0),
     JS_FN("freeze",                    obj_freeze,                  1,0),
     JS_FN("isFrozen",                  obj_isFrozen,                1,0),
     JS_FN("seal",                      obj_seal,                    1,0),
     JS_FN("isSealed",                  obj_isSealed,                1,0),
     JS_FS_END
 };
 
-/*
- * For Object, self-hosted functions have to be done at a different
- * time, after the intrinsic holder has been set, so we put them
- * in a different array.
- */
-static const JSFunctionSpec object_static_selfhosted_methods[] = {
-    JS_SELF_HOSTED_FN("assign",        "ObjectStaticAssign",        2,0),
-    JS_FS_END
-};
-
 static JSObject *
 CreateObjectConstructor(JSContext *cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
     if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
         return nullptr;
 
     RootedObject functionProto(cx, &self->getPrototype(JSProto_Function).toObject());
@@ -1313,17 +1304,17 @@ FinishObjectClassInit(JSContext *cx, JS:
         return false;
     }
 
     /*
      * Define self-hosted functions after setting the intrinsics holder
      * (which is needed to define self-hosted functions)
      */
     if (!isSelfHostingGlobal) {
-        if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
+        if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
             return false;
     }
 
     /*
      * The global object should have |Object.prototype| as its [[Prototype]].
      * Eventually we'd like to have standard classes be there from the start,
      * and thus we would know we were always setting what had previously been a
      * null [[Prototype]], but right now some code assumes it can set the
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4008,33 +4008,47 @@ js_generic_native_method_dispatcher(JSCo
 
     /* Clear the last parameter in case too few arguments were passed. */
     vp[2 + --argc].setUndefined();
 
     return fs->call.op(cx, argc, vp);
 }
 
 JS_PUBLIC_API(bool)
-JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs)
+JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs,
+                   PropertyDefinitionBehavior behavior)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     RootedId id(cx);
     for (; fs->name; fs++) {
         if (!PropertySpecNameToId(cx, fs->name, &id))
             return false;
 
+        unsigned flags = fs->flags;
+        switch (behavior) {
+          case DefineAllProperties:
+            break;
+          case OnlyDefineLateProperties:
+            if (!(flags & JSPROP_DEFINE_LATE))
+                continue;
+            break;
+          default:
+            MOZ_ASSERT(behavior == DontDefineLateProperties);
+            if (flags & JSPROP_DEFINE_LATE)
+                continue;
+        }
+
         /*
          * Define a generic arity N+1 static method for the arity N prototype
          * method if flags contains JSFUN_GENERIC_NATIVE.
          */
-        unsigned flags = fs->flags;
         if (flags & JSFUN_GENERIC_NATIVE) {
             // We require that any consumers using JSFUN_GENERIC_NATIVE stash
             // the prototype and constructor in the global slots before invoking
             // JS_DefineFunctions on the proto.
             JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
             MOZ_ASSERT(obj == &obj->global().getPrototype(key).toObject());
             RootedObject ctor(cx, &obj->global().getConstructor(key).toObject());
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -962,17 +962,20 @@ class MOZ_STACK_CLASS SourceBufferHolder
 #define JSPROP_GETTER           0x10    /* property holds getter function */
 #define JSPROP_SETTER           0x20    /* property holds setter function */
 #define JSPROP_SHARED           0x40    /* don't allocate a value slot for this
                                            property; don't copy the property on
                                            set of the same-named property in an
                                            object that delegates to a prototype
                                            containing this property */
 #define JSPROP_INDEX            0x80    /* name is actually (int) index */
-
+#define JSPROP_DEFINE_LATE     0x100    /* Don't define property when initially creating
+                                           the constructor. Some objects like Function/Object
+                                           have self-hosted functions that can only be defined
+                                           after the initialization is already finished. */
 #define JSFUN_STUB_GSOPS       0x200    /* use JS_PropertyStub getter/setter
                                            instead of defaulting to class gsops
                                            for property holding function */
 
 #define JSFUN_CONSTRUCTOR      0x400    /* native that can be called as a ctor */
 
 #define JSPROP_REDEFINE_NONCONFIGURABLE 0x800 /* If set, will allow redefining a
                                                  non-configurable property, but
@@ -3529,18 +3532,28 @@ JS_IsConstructor(JSFunction *fun);
 /*
  * Bind the given callable to use the given object as "this".
  *
  * If |callable| is not callable, will throw and return nullptr.
  */
 extern JS_PUBLIC_API(JSObject*)
 JS_BindCallable(JSContext *cx, JS::Handle<JSObject*> callable, JS::Handle<JSObject*> newThis);
 
-extern JS_PUBLIC_API(bool)
-JS_DefineFunctions(JSContext *cx, JS::Handle<JSObject*> obj, const JSFunctionSpec *fs);
+// This enum is used to select if properties with JSPROP_DEFINE_LATE flag
+// should be defined on the object.
+// Normal JSAPI consumers probably always want DefineAllProperties here.
+enum PropertyDefinitionBehavior {
+    DefineAllProperties,
+    OnlyDefineLateProperties,
+    DontDefineLateProperties
+};
+
+extern JS_PUBLIC_API(bool)
+JS_DefineFunctions(JSContext *cx, JS::Handle<JSObject*> obj, const JSFunctionSpec *fs,
+                   PropertyDefinitionBehavior behavior = DefineAllProperties);
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineFunction(JSContext *cx, JS::Handle<JSObject*> obj, const char *name, JSNative call,
                   unsigned nargs, unsigned attrs);
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineUCFunction(JSContext *cx, JS::Handle<JSObject*> obj,
                     const char16_t *name, size_t namelen, JSNative call,
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -170,25 +170,25 @@ GlobalObject::resolveConstructor(JSConte
 
     global->setConstructor(key, ObjectValue(*ctor));
     global->setConstructorPropertySlot(key, ObjectValue(*ctor));
 
     // 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))
+            if (!JS_DefineFunctions(cx, proto, funs, DontDefineLateProperties))
                 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))
+            if (!JS_DefineFunctions(cx, ctor, funs, DontDefineLateProperties))
                 return false;
         }
     }
 
     // If the prototype exists, link it with the constructor.
     if (proto && !LinkConstructorAndPrototype(cx, ctor, proto))
         return false;