Bug 785645 - Support constructors in self-hosted JavaScript. r=luke
authorTill Schneidereit <tschneidereit@gmail.com>
Wed, 29 Aug 2012 00:11:13 +0200
changeset 105759 ab88572c117ccfbc6e9924a4fd787268f8771ef6
parent 105758 a7c3131505d7377d3c109b4b357cbf4fe94307ea
child 105760 3e6ed8c7aaa63c8533c5e4612bec1131ac895298
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersluke
bugs785645
milestone18.0a1
Bug 785645 - Support constructors in self-hosted JavaScript. r=luke
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jsfun.h
js/src/vm/GlobalObject.cpp
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2522,35 +2522,39 @@ class AutoIdRooter : private AutoGCRoote
 #define JSPROP_SHORTID          0x100   /* set in JS_DefineProperty attrs
                                            if getters/setters use a shortid */
 #define JSPROP_NATIVE_ACCESSORS 0x08    /* set in JSPropertyDescriptor.flags
                                            if getters/setters are JSNatives */
 
 /* Function flags, internal use only, returned by JS_GetFunctionFlags. */
 #define JSFUN_LAMBDA            0x08    /* expressed, not declared, function */
 
-#define JSFUN_SELF_HOSTED       0x40    /* function is self-hosted native and
+#define JSFUN_SELF_HOSTED       0x20    /* function is self-hosted builtin and
                                            must not be decompilable nor
                                            constructible. */
 
+#define JSFUN_SELF_HOSTED_CTOR  0x40    /* function is self-hosted builtin
+                                           constructor and must be constructible
+                                           but not decompilable. */
+
 #define JSFUN_HEAVYWEIGHT       0x80    /* activation requires a Call object */
 
 #define JSFUN_HEAVYWEIGHT_TEST(f)  ((f) & JSFUN_HEAVYWEIGHT)
 
 #define JSFUN_HAS_REST          0x0100  /* function has a rest (...) parameter */
 #define JSFUN_CONSTRUCTOR       0x0200  /* native that can be called as a ctor
                                            without creating a this object */
 #define JSFUN_HAS_DEFAULTS      0x0400  /* function has at least one default
                                            parameter */
 
-#define JSFUN_FLAGS_MASK      0x07f8    /* overlay JSFUN_* attributes --
+#define JSFUN_FLAGS_MASK        0x07f8  /* overlay JSFUN_* attributes --
                                            bits 12-15 are used internally to
                                            flag interpreted functions */
 
-#define JSFUN_STUB_GSOPS      0x1000    /* use JS_PropertyStub getter/setter
+#define JSFUN_STUB_GSOPS        0x1000  /* use JS_PropertyStub getter/setter
                                            instead of defaulting to class gsops
                                            for property holding function */
 
 /*
  * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in
  * JSFunctionSpec arrays that specify generic native prototype methods, i.e.,
  * methods of a class prototype that are exposed as static methods taking an
  * extra leading argument: the generic |this| parameter.
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -262,21 +262,29 @@ JSRuntime::cloneSelfHostedValueById(JSCo
     Value funVal;
     {
         RootedObject shg(cx, selfHostedGlobal_);
         AutoCompartment ac(cx, shg);
         if (!JS_GetPropertyById(cx, shg, id, &funVal) || !funVal.isObject())
             return false;
     }
 
-    RootedObject clone(cx, JS_CloneFunctionObject(cx, &funVal.toObject(), cx->global()));
-    if (!clone)
-        return false;
-
-    vp->setObjectOrNull(clone);
+    /*
+     * We don't clone if we're operating in the self-hosting global, as that
+     * means we're currently executing the self-hosting script while
+     * initializing the runtime (see JSRuntime::initSelfHosting).
+     */
+    if (cx->global() == selfHostedGlobal_) {
+        *vp = ObjectValue(funVal.toObject());
+    } else {
+        RootedObject clone(cx, JS_CloneFunctionObject(cx, &funVal.toObject(), cx->global()));
+        if (!clone)
+            return false;
+        *vp = ObjectValue(*clone);
+    }
     DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, *vp, NULL, NULL, 0);
     JS_ASSERT(ok);
     return true;
 }
 
 JSContext *
 js::NewContext(JSRuntime *rt, size_t stackChunkSize)
 {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -75,22 +75,24 @@ struct JSFunction : public JSObject
     js::HeapPtrAtom  atom_;       /* name for diagnostics and decompiling */
   public:
 
     bool hasDefaults()       const { return flags & JSFUN_HAS_DEFAULTS; }
     bool hasRest()           const { return flags & JSFUN_HAS_REST; }
     bool hasGuessedAtom()    const { return flags & JSFUN_HAS_GUESSED_ATOM; }
     bool isInterpreted()     const { return flags & JSFUN_INTERPRETED; }
     bool isNative()          const { return !isInterpreted(); }
-    bool isSelfHostedBuiltin()  const { return flags & JSFUN_SELF_HOSTED; }
+    bool isSelfHostedBuiltin() const { return flags & JSFUN_SELF_HOSTED; }
+    bool isSelfHostedConstructor() const { return flags & JSFUN_SELF_HOSTED_CTOR; }
     bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
     bool isHeavyweight()     const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
     bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
     bool isInterpretedConstructor() const {
-        return isInterpreted() && !isFunctionPrototype() && !isSelfHostedBuiltin();
+        return isInterpreted() && !isFunctionPrototype() &&
+               (!isSelfHostedBuiltin() || isSelfHostedConstructor());
     }
     bool isNamedLambda()     const {
         return (flags & JSFUN_LAMBDA) && atom_ && !hasGuessedAtom();
     }
 
     /* Returns the strictness of this function, which must be interpreted. */
     inline bool inStrictMode() const;
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -226,21 +226,34 @@ intrinsic_ThrowError(JSContext *cx, unsi
 
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
                          errorArgs[0], errorArgs[1], errorArgs[2]);
     for (unsigned i = 0; i < 3; i++)
         cx->free_(errorArgs[i]);
     return false;
 }
 
+static JSBool
+intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args.length() >= 1);
+    JS_ASSERT(args[0].isObject());
+    RootedObject obj(cx, &args[0].toObject());
+    JS_ASSERT(obj->isFunction());
+    obj->toFunction()->flags |= JSFUN_SELF_HOSTED_CTOR;
+    return true;
+}
+
 JSFunctionSpec intrinsic_functions[] = {
-    JS_FN("ToObject",       intrinsic_ToObject,     1,0),
-    JS_FN("ToInteger",      intrinsic_ToInteger,    1,0),
-    JS_FN("IsCallable",     intrinsic_IsCallable,   1,0),
-    JS_FN("ThrowError",     intrinsic_ThrowError,   4,0),
+    JS_FN("ToObject",           intrinsic_ToObject,             1,0),
+    JS_FN("ToInteger",          intrinsic_ToInteger,            1,0),
+    JS_FN("IsCallable",         intrinsic_IsCallable,           1,0),
+    JS_FN("ThrowError",         intrinsic_ThrowError,           4,0),
+    JS_FN("_MakeConstructible", intrinsic_MakeConstructible,    1,0),
     JS_FS_END
 };
 JSObject *
 GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
 {
     Rooted<GlobalObject*> self(cx, this);
 
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);