Bug 904723, part 1 - Add new IsConstructor intrinsic for self-hosting. r=till.
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 06 Jun 2014 11:15:21 -0400
changeset 206445 dbec390460f655702a1d953efaefae07bcad21ac
parent 206444 3825abd9a302a6270bd41f83bf666313af5cd758
child 206446 55f0e2c4340631727047cbc6df00f4c0b4649164
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs904723
milestone32.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 904723, part 1 - Add new IsConstructor intrinsic for self-hosting. r=till. This also renames js_IsCallable to js::IsCallable and does some related light cleaning work.
js/src/builtin/Object.cpp
js/src/jsarray.cpp
js/src/jsdate.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsproxy.cpp
js/src/jsstr.cpp
js/src/vm/Debugger.cpp
js/src/vm/ObjectImpl.cpp
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -366,17 +366,17 @@ enum DefineType { GetterAccessor, Setter
 template<DefineType Type>
 static bool
 DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!BoxNonStrictThis(cx, args))
         return false;
 
-    if (args.length() < 2 || !js_IsCallable(args[1])) {
+    if (args.length() < 2 || !IsCallable(args[1])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_BAD_GETTER_OR_SETTER,
                              Type == GetterAccessor ? js_getter_str : js_setter_str);
         return false;
     }
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1135,17 +1135,17 @@ array_toString(JSContext *cx, unsigned a
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     RootedValue join(cx, args.calleev());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
         return false;
 
-    if (!js_IsCallable(join)) {
+    if (!IsCallable(join)) {
         JSString *str = JS_BasicObjectToString(cx, obj);
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     InvokeArgs args2(cx);
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2474,17 +2474,17 @@ date_toJSON(JSContext *cx, unsigned argc
     }
 
     /* Step 4. */
     RootedValue toISO(cx);
     if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
         return false;
 
     /* Step 5. */
-    if (!js_IsCallable(toISO)) {
+    if (!IsCallable(toISO)) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
                                      JSMSG_BAD_TOISOSTRING_PROP);
         return false;
     }
 
     /* Step 6. */
     InvokeArgs args2(cx);
     if (!args2.init(0))
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1045,17 +1045,17 @@ fun_toSource(JSContext *cx, unsigned arg
 #endif
 
 bool
 js_fun_call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     HandleValue fval = args.thisv();
-    if (!js_IsCallable(fval)) {
+    if (!IsCallable(fval)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     args.setCallee(fval);
     args.setThis(args.get(0));
 
     if (args.length() > 0) {
@@ -1070,17 +1070,17 @@ js_fun_call(JSContext *cx, unsigned argc
 // ES5 15.3.4.3
 bool
 js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     HandleValue fval = args.thisv();
-    if (!js_IsCallable(fval)) {
+    if (!IsCallable(fval)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     // Step 2.
     if (args.length() < 2 || args[1].isNullOrUndefined())
         return js_fun_call(cx, (args.length() > 0) ? 1 : 0, vp);
 
@@ -1424,17 +1424,17 @@ static bool
 fun_bind(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     Value thisv = args.thisv();
 
     /* Step 2. */
-    if (!js_IsCallable(thisv)) {
+    if (!IsCallable(thisv)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     /* Step 3. */
     Value *boundArgs = nullptr;
     unsigned argslen = 0;
     if (args.length() > 1) {
@@ -1938,33 +1938,16 @@ js::DefineFunction(JSContext *cx, Handle
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!JSObject::defineGeneric(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return nullptr;
 
     return fun;
 }
 
-bool
-js::IsConstructor(const Value &v)
-{
-    // Step 2.
-    if (!v.isObject())
-        return false;
-
-    // Step 3-4, a bit complex for us, since we have several flavors of
-    // [[Construct]] internal method.
-    JSObject &obj = v.toObject();
-    if (obj.is<JSFunction>()) {
-        JSFunction &fun = obj.as<JSFunction>();
-        return fun.isNativeConstructor() || fun.isInterpretedConstructor();
-    }
-    return obj.getClass()->construct != nullptr;
-}
-
 void
 js::ReportIncompatibleMethod(JSContext *cx, CallReceiver call, const Class *clasp)
 {
     RootedValue thisv(cx, call.thisv());
 
 #ifdef DEBUG
     if (thisv.isObject()) {
         JS_ASSERT(thisv.toObject().getClass() != clasp ||
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -527,19 +527,16 @@ DefineFunction(JSContext *cx, HandleObje
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
 
 extern bool
 fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
 
-// ES6 9.2.5 IsConstructor
-bool IsConstructor(const Value &v);
-
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
  * storage is required (no malloc'ed reserved slots).
  */
 class FunctionExtended : public JSFunction
 {
   public:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3426,16 +3426,26 @@ js::FindClassObject(ExclusiveContext *cx
         if (shape->hasSlot())
             v = pobj->nativeGetSlot(shape->slot());
     }
     if (v.isObject())
         protop.set(&v.toObject());
     return true;
 }
 
+bool
+JSObject::isConstructor() const
+{
+    if (is<JSFunction>()) {
+        const JSFunction &fun = as<JSFunction>();
+        return fun.isNativeConstructor() || fun.isInterpretedConstructor();
+    }
+    return getClass()->construct != nullptr;
+}
+
 /* static */ bool
 JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
 
     uint32_t slot = obj->slotSpan();
     JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
 
@@ -5377,17 +5387,17 @@ js::HasDataProperty(JSContext *cx, JSObj
  * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
  * or steps 3-4.
  */
 static bool
 MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     if (!JSObject::getGeneric(cx, obj, obj, id, vp))
         return false;
-    if (!js_IsCallable(vp)) {
+    if (!IsCallable(vp)) {
         vp.setObject(*obj);
         return true;
     }
     return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
 }
 
 JS_FRIEND_API(bool)
 js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
@@ -5973,16 +5983,17 @@ js_DumpBacktrace(JSContext *cx)
         unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
         JSScript *script = i.script();
         sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
                         depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
                         script, script->pcToOffset(i.pc()));
     }
     fprintf(stdout, "%s", sprinter.string());
 }
+
 void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     if (hasDynamicSlots())
         sizes->mallocHeapSlots += mallocSizeOf(slots);
 
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -802,16 +802,17 @@ class JSObject : public js::ObjectImpl
     static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
 
     /*
      * Back to generic stuff.
      */
     bool isCallable() {
         return getClass()->isCallable();
     }
+    bool isConstructor() const;
 
     inline void finish(js::FreeOp *fop);
     MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 
     static inline bool hasProperty(JSContext *cx, js::HandleObject obj,
                                    js::HandleId id, bool *foundp);
 
     /*
@@ -1179,22 +1180,31 @@ operator!=(const JSObject &lhs, const JS
 }
 
 struct JSObject_Slots2 : JSObject { js::Value fslots[2]; };
 struct JSObject_Slots4 : JSObject { js::Value fslots[4]; };
 struct JSObject_Slots8 : JSObject { js::Value fslots[8]; };
 struct JSObject_Slots12 : JSObject { js::Value fslots[12]; };
 struct JSObject_Slots16 : JSObject { js::Value fslots[16]; };
 
-static inline bool
-js_IsCallable(const js::Value &v)
+namespace js {
+
+inline bool
+IsCallable(const Value &v)
 {
     return v.isObject() && v.toObject().isCallable();
 }
 
+// ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
+inline bool
+IsConstructor(const Value &v)
+{
+    return v.isObject() && v.toObject().isConstructor();
+}
+
 inline JSObject *
 GetInnerObject(JSObject *obj)
 {
     if (js::InnerObjectOp op = obj->getClass()->ext.innerObject) {
         JS::AutoSuppressGCAnalysis nogc;
         return op(obj);
     }
     return obj;
@@ -1203,16 +1213,18 @@ GetInnerObject(JSObject *obj)
 inline JSObject *
 GetOuterObject(JSContext *cx, js::HandleObject obj)
 {
     if (js::ObjectOp op = obj->getClass()->ext.outerObject)
         return op(cx, obj);
     return obj;
 }
 
+} /* namespace js */
+
 class JSValueArray {
   public:
     const jsval *array;
     size_t length;
 
     JSValueArray(const jsval *v, size_t c) : array(v), length(c) {}
 };
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -646,18 +646,18 @@ PropDesc::PropDesc(const Value &getter, 
     get_(getter), set_(setter),
     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
           (enumerable ? JSPROP_ENUMERATE : 0) |
           (configurable ? 0 : JSPROP_PERMANENT)),
     hasGet_(true), hasSet_(true),
     hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
     isUndefined_(false)
 {
-    MOZ_ASSERT(getter.isUndefined() || js_IsCallable(getter));
-    MOZ_ASSERT(setter.isUndefined() || js_IsCallable(setter));
+    MOZ_ASSERT(getter.isUndefined() || IsCallable(getter));
+    MOZ_ASSERT(setter.isUndefined() || IsCallable(setter));
 }
 
 static MOZ_ALWAYS_INLINE bool
 IsFunctionObject(const js::Value &v)
 {
     return v.isObject() && v.toObject().is<JSFunction>();
 }
 
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -190,17 +190,17 @@ PreprocessValue(JSContext *cx, HandleObj
 
     /* Step 2. */
     if (vp.isObject()) {
         RootedValue toJSON(cx);
         RootedObject obj(cx, &vp.toObject());
         if (!JSObject::getProperty(cx, obj, obj, cx->names().toJSON, &toJSON))
             return false;
 
-        if (js_IsCallable(toJSON)) {
+        if (IsCallable(toJSON)) {
             keyStr = KeyStringifier<KeyType>::toString(cx, key);
             if (!keyStr)
                 return false;
 
             InvokeArgs args(cx);
             if (!args.init(1))
                 return false;
 
@@ -262,17 +262,17 @@ PreprocessValue(JSContext *cx, HandleObj
  * gauntlet will result in Str returning |undefined|.  This function is used to
  * properly omit properties resulting in such values when stringifying objects,
  * while properly stringifying such properties as null when they're encountered
  * in arrays.
  */
 static inline bool
 IsFilteredValue(const Value &v)
 {
-    return v.isUndefined() || js_IsCallable(v);
+    return v.isUndefined() || IsCallable(v);
 }
 
 /* ES5 15.12.3 JO. */
 static bool
 JO(JSContext *cx, HandleObject obj, StringifyContext *scx)
 {
     /*
      * This method implements the JO algorithm in ES5 15.12.3, but:
@@ -778,17 +778,17 @@ js::ParseJSONWithReviver(JSContext *cx, 
                          HandleValue reviver, MutableHandleValue vp)
 {
     /* 15.12.2 steps 2-3. */
     JSONParser parser(cx, chars, length);
     if (!parser.parse(vp))
         return false;
 
     /* 15.12.2 steps 4-5. */
-    if (js_IsCallable(reviver))
+    if (IsCallable(reviver))
         return Revive(cx, reviver, vp);
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static bool
 json_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -910,30 +910,30 @@ ScriptedIndirectProxyHandler::enumerate(
 
 bool
 ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::has(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
 ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
 ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, MutableHandleValue vp)
@@ -945,17 +945,17 @@ ScriptedIndirectProxyHandler::get(JSCont
         return false;
     RootedValue value(cx, StringValue(str));
     JS::AutoValueArray<2> argv(cx);
     argv[0].setObjectOrNull(receiver);
     argv[1].set(value);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval, 2, argv.begin(), vp);
 }
 
 bool
 ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, bool strict, MutableHandleValue vp)
 {
@@ -967,43 +967,43 @@ ScriptedIndirectProxyHandler::set(JSCont
     RootedValue value(cx, StringValue(str));
     JS::AutoValueArray<3> argv(cx);
     argv[0].setObjectOrNull(receiver);
     argv[1].set(value);
     argv[2].set(vp);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
     return Trap(cx, handler, fval, 3, argv.begin(), &value);
 }
 
 bool
 ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
         return false;
-    if (!js_IsCallable(value))
+    if (!IsCallable(value))
         return BaseProxyHandler::keys(cx, proxy, props);
     return Trap(cx, handler, value, 0, nullptr, &value) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
 ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                                       MutableHandleValue vp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
         return false;
-    if (!js_IsCallable(value))
+    if (!IsCallable(value))
         return BaseProxyHandler::iterate(cx, proxy, flags, vp);
     return Trap(cx, handler, value, 0, nullptr, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp);
 }
 
 bool
 ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
 {
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3355,17 +3355,17 @@ js::str_replace(JSContext *cx, unsigned 
     rdata.str = ThisToStringForStringProto(cx, args);
     if (!rdata.str)
         return false;
 
     if (!rdata.g.init(cx, args))
         return false;
 
     /* Extract replacement string/function. */
-    if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) {
+    if (args.length() >= ReplaceOptArg && IsCallable(args[1])) {
         rdata.setReplacementFunction(&args[1].toObject());
 
         if (!LambdaIsGetElem(cx, *rdata.lambda, &rdata.elembase))
             return false;
     } else {
         JSLinearString *string = ArgToRootedString(cx, args, 1);
         if (!string)
             return false;
@@ -4343,17 +4343,17 @@ js::ValueToSource(JSContext *cx, HandleV
         }
         return ToString<CanGC>(cx, v);
     }
 
     RootedValue fval(cx);
     RootedObject obj(cx, &v.toObject());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().toSource, &fval))
         return nullptr;
-    if (js_IsCallable(fval)) {
+    if (IsCallable(fval)) {
         RootedValue rval(cx);
         if (!Invoke(cx, ObjectValue(*obj), fval, 0, nullptr, &rval))
             return nullptr;
         return ToString<CanGC>(cx, rval);
     }
 
     return ObjectToSource(cx, obj);
 }
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1042,17 +1042,17 @@ CallMethodIfPresent(JSContext *cx, Handl
     rval.setUndefined();
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     RootedId id(cx, AtomToId(atom));
     RootedValue fval(cx);
     return JSObject::getGeneric(cx, obj, obj, id, &fval) &&
-           (!js_IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
+           (!IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
 }
 
 JSTrapStatus
 Debugger::fireDebuggerStatement(JSContext *cx, MutableHandleValue vp)
 {
     RootedObject hook(cx, getHook(OnDebuggerStatement));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -40,30 +40,30 @@ PropDesc::setUndefined()
     isUndefined_ = true;
 }
 
 
 bool
 PropDesc::checkGetter(JSContext *cx)
 {
     if (hasGet_) {
-        if (!js_IsCallable(get_) && !get_.isUndefined()) {
+        if (!IsCallable(get_) && !get_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_getter_str);
             return false;
         }
     }
     return true;
 }
 
 bool
 PropDesc::checkSetter(JSContext *cx)
 {
     if (hasSet_) {
-        if (!js_IsCallable(set_) && !set_.isUndefined()) {
+        if (!IsCallable(set_) && !set_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_setter_str);
             return false;
         }
     }
     return true;
 }
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -83,19 +83,25 @@ js::intrinsic_ToString(JSContext *cx, un
     args.rval().setString(str);
     return true;
 }
 
 bool
 js::intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    Value val = args[0];
-    bool isCallable = val.isObject() && val.toObject().isCallable();
-    args.rval().setBoolean(isCallable);
+    args.rval().setBoolean(IsCallable(args[0]));
+    return true;
+}
+
+static bool
+intrinsic_IsConstructor(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(IsConstructor(args[0]));
     return true;
 }
 
 bool
 js::intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
@@ -752,16 +758,17 @@ intrinsic_RuntimeDefaultLocale(JSContext
     return true;
 }
 
 static const JSFunctionSpec intrinsic_functions[] = {
     JS_FN("ToObject",                intrinsic_ToObject,                1,0),
     JS_FN("ToInteger",               intrinsic_ToInteger,               1,0),
     JS_FN("ToString",                intrinsic_ToString,                1,0),
     JS_FN("IsCallable",              intrinsic_IsCallable,              1,0),
+    JS_FN("IsConstructor",           intrinsic_IsConstructor,           1,0),
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("SetScriptHints",          intrinsic_SetScriptHints,          2,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
 
     JS_FN("UnsafePutElements",       intrinsic_UnsafePutElements,       3,0),