Bug 920433 - Improve API support for self-hosted functions. r=till, r=bz
authorAndy Wingo <wingo@igalia.com>
Thu, 26 Sep 2013 10:11:38 -0400
changeset 148805 f46fd6fcf018cfca94c4d9b52b8a9c1878320aab
parent 148804 ff6fed474ee21287e1ff94a148353063768f52dd
child 148806 5a2bfaac8236615d6c53a54c1397bc4c6ae1c796
push id34339
push userryanvm@gmail.com
push dateThu, 26 Sep 2013 14:11:43 +0000
treeherdermozilla-inbound@9029aa5d60da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, bz
bugs920433
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 920433 - Improve API support for self-hosted functions. r=till, r=bz
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
js/src/builtin/Intl.cpp
js/src/builtin/MapObject.cpp
js/src/builtin/ParallelArray.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsdate.cpp
js/src/jsfun.cpp
js/src/jsnum.cpp
js/src/jsstr.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1014,23 +1014,31 @@ XrayResolveProperty(JSContext* cx, JS::H
     for (method = methods; method->specs; ++method) {
       if (method->isEnabled(cx, obj)) {
         // Set i to be the index into our full list of ids/specs that we're
         // looking at now.
         size_t i = method->specs - methodsSpecs;
         for ( ; methodIds[i] != JSID_VOID; ++i) {
           if (id == methodIds[i]) {
             const JSFunctionSpec& methodSpec = methodsSpecs[i];
-            JSFunction *fun = JS_NewFunctionById(cx, methodSpec.call.op,
-                                                 methodSpec.nargs, 0,
-                                                 wrapper, id);
-            if (!fun) {
-              return false;
+            JSFunction *fun;
+            if (methodSpec.selfHostedName) {
+              fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
+              if (!fun) {
+                return false;
+              }
+              MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
+              MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
+            } else {
+              fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
+              if (!fun) {
+                return false;
+              }
+              SET_JITINFO(fun, methodSpec.call.info);
             }
-            SET_JITINFO(fun, methodSpec.call.info);
             JSObject *funobj = JS_GetFunctionObject(fun);
             desc.value().setObject(*funobj);
             desc.setAttributes(methodSpec.flags);
             desc.object().set(wrapper);
             desc.setSetter(nullptr);
             desc.setGetter(nullptr);
            return true;
           }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1566,27 +1566,35 @@ class MethodDefiner(PropertyDefiner):
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def condition(m):
             return m["condition"]
 
         def specData(m):
-            accessor = m.get("nativeName", m["name"])
-            if m.get("methodInfo", True):
-                jitinfo = ("&%s_methodinfo" % accessor)
-                accessor = "genericMethod"
+            if "selfHostedName" in m:
+                selfHostedName = '"%s"' % m["selfHostedName"]
+                assert not m.get("methodInfo", True)
+                accessor = "nullptr"
+                jitinfo = "nullptr"
             else:
-                jitinfo = "nullptr"
-            return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+                selfHostedName = "nullptr";
+                accessor = m.get("nativeName", m["name"])
+                if m.get("methodInfo", True):
+                    jitinfo = ("&%s_methodinfo" % accessor)
+                    accessor = "genericMethod"
+                else:
+                    jitinfo = "nullptr"
+
+            return (m["name"], accessor, jitinfo, m["length"], m["flags"], selfHostedName)
 
         return self.generatePrefableArray(
             array, name,
-            '  JS_FNINFO("%s", %s, %s, %s, %s)',
+            '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
             condition, specData, doIdArrays)
 
 class AttrDefiner(PropertyDefiner):
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -565,22 +565,22 @@ static bool
 collator_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     vp->setString(cx->names().Collator);
     return true;
 }
 #endif
 
 static const JSFunctionSpec collator_static_methods[] = {
-    {"supportedLocalesOf", JSOP_NULLWRAPPER, 1, JSFunction::INTERPRETED, "Intl_Collator_supportedLocalesOf"},
+    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
     JS_FS_END
 };
 
 static const JSFunctionSpec collator_methods[] = {
-    {"resolvedOptions", JSOP_NULLWRAPPER, 0, JSFunction::INTERPRETED, "Intl_Collator_resolvedOptions"},
+    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, collator_toSource, 0, 0),
 #endif
     JS_FS_END
 };
 
 /**
  * Collator constructor.
@@ -1048,22 +1048,22 @@ static bool
 numberFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     vp->setString(cx->names().NumberFormat);
     return true;
 }
 #endif
 
 static const JSFunctionSpec numberFormat_static_methods[] = {
-    {"supportedLocalesOf", JSOP_NULLWRAPPER, 1, JSFunction::INTERPRETED, "Intl_NumberFormat_supportedLocalesOf"},
+    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_NumberFormat_supportedLocalesOf", 1, 0),
     JS_FS_END
 };
 
 static const JSFunctionSpec numberFormat_methods[] = {
-    {"resolvedOptions", JSOP_NULLWRAPPER, 0, JSFunction::INTERPRETED, "Intl_NumberFormat_resolvedOptions"},
+    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_NumberFormat_resolvedOptions", 0, 0),
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, numberFormat_toSource, 0, 0),
 #endif
     JS_FS_END
 };
 
 /**
  * NumberFormat constructor.
@@ -1505,22 +1505,22 @@ static bool
 dateTimeFormat_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     vp->setString(cx->names().DateTimeFormat);
     return true;
 }
 #endif
 
 static const JSFunctionSpec dateTimeFormat_static_methods[] = {
-    {"supportedLocalesOf", JSOP_NULLWRAPPER, 1, JSFunction::INTERPRETED, "Intl_DateTimeFormat_supportedLocalesOf"},
+    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
     JS_FS_END
 };
 
 static const JSFunctionSpec dateTimeFormat_methods[] = {
-    {"resolvedOptions", JSOP_NULLWRAPPER, 0, JSFunction::INTERPRETED, "Intl_DateTimeFormat_resolvedOptions"},
+    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
 #endif
     JS_FS_END
 };
 
 /**
  * DateTimeFormat constructor.
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -1034,17 +1034,17 @@ const JSPropertySpec MapObject::properti
 const JSFunctionSpec MapObject::methods[] = {
     JS_FN("get", get, 1, 0),
     JS_FN("has", has, 1, 0),
     JS_FN("set", set, 2, 0),
     JS_FN("delete", delete_, 1, 0),
     JS_FN("keys", keys, 0, 0),
     JS_FN("values", values, 0, 0),
     JS_FN("clear", clear, 0, 0),
-    {"forEach", {nullptr, nullptr}, 2, 0, "MapForEach"},
+    JS_SELF_HOSTED_FN("forEach", "MapForEach", 2, 0),
     JS_FS_END
 };
 
 static JSObject *
 InitClass(JSContext *cx, Handle<GlobalObject*> global, const Class *clasp, JSProtoKey key, Native construct,
           const JSPropertySpec *properties, const JSFunctionSpec *methods)
 {
     Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, clasp));
@@ -1595,17 +1595,17 @@ const JSPropertySpec SetObject::properti
 };
 
 const JSFunctionSpec SetObject::methods[] = {
     JS_FN("has", has, 1, 0),
     JS_FN("add", add, 1, 0),
     JS_FN("delete", delete_, 1, 0),
     JS_FN("entries", entries, 0, 0),
     JS_FN("clear", clear, 0, 0),
-    {"forEach", {nullptr, nullptr}, 2, 0, "SetForEach"},
+    JS_SELF_HOSTED_FN("forEach", "SetForEach", 2, 0),
     JS_FS_END
 };
 
 JSObject *
 SetObject::initClass(JSContext *cx, JSObject *obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedObject proto(cx,
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -18,34 +18,34 @@ using namespace js;
 
 //
 // ParallelArrayObject
 //
 
 FixedHeapPtr<PropertyName> ParallelArrayObject::ctorNames[NumCtors];
 
 const JSFunctionSpec ParallelArrayObject::methods[] = {
-    { "map",       JSOP_NULLWRAPPER, 2, 0, "ParallelArrayMap"       },
-    { "reduce",    JSOP_NULLWRAPPER, 2, 0, "ParallelArrayReduce"    },
-    { "scan",      JSOP_NULLWRAPPER, 2, 0, "ParallelArrayScan"      },
-    { "scatter",   JSOP_NULLWRAPPER, 5, 0, "ParallelArrayScatter"   },
-    { "filter",    JSOP_NULLWRAPPER, 2, 0, "ParallelArrayFilter"    },
-    { "partition", JSOP_NULLWRAPPER, 1, 0, "ParallelArrayPartition" },
-    { "flatten",   JSOP_NULLWRAPPER, 0, 0, "ParallelArrayFlatten" },
+    JS_SELF_HOSTED_FN("map",       "ParallelArrayMap",       2, 0),
+    JS_SELF_HOSTED_FN("reduce",    "ParallelArrayReduce",    2, 0),
+    JS_SELF_HOSTED_FN("scan",      "ParallelArrayScan",      2, 0),
+    JS_SELF_HOSTED_FN("scatter",   "ParallelArrayScatter",   5, 0),
+    JS_SELF_HOSTED_FN("filter",    "ParallelArrayFilter",    2, 0),
+    JS_SELF_HOSTED_FN("partition", "ParallelArrayPartition", 1, 0),
+    JS_SELF_HOSTED_FN("flatten",   "ParallelArrayFlatten",   0, 0),
 
     // FIXME #838906. Note that `get()` is not currently defined on this table but
     // rather is assigned to each instance of ParallelArray as an own
     // property.  This is a bit of a hack designed to supply a
     // specialized version of get() based on the dimensionality of the
     // receiver.  In the future we can improve this by (1) extending
     // TI to track the dimensionality of the receiver and (2) using a
     // hint to aggressively inline calls to get().
-    // { "get",      JSOP_NULLWRAPPER, 1, 0, "ParallelArrayGet" },
+    // JS_SELF_HOSTED_FN("get", "ParallelArrayGet", 1, 0),
 
-    { "toString", JSOP_NULLWRAPPER, 0, 0, "ParallelArrayToString" },
+    JS_SELF_HOSTED_FN("toString",  "ParallelArrayToString",  0, 0),
     JS_FS_END
 };
 
 const Class ParallelArrayObject::protoClass = {
     "ParallelArray",
     JSCLASS_HAS_CACHED_PROTO(JSProto_ParallelArray),
     JS_PropertyStub,         // addProperty
     JS_DeletePropertyStub,   // delProperty
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4029,23 +4029,42 @@ JS_NewFunction(JSContext *cx, JSNative n
 
 JS_PUBLIC_API(JSFunction *)
 JS_NewFunctionById(JSContext *cx, JSNative native, unsigned nargs, unsigned flags, JSObject *parentArg,
                    jsid id)
 {
     RootedObject parent(cx, parentArg);
     JS_ASSERT(JSID_IS_STRING(id));
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    JS_ASSERT(native);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
-    RootedAtom atom(cx, JSID_TO_ATOM(id));
+    RootedAtom name(cx, JSID_TO_ATOM(id));
     JSFunction::Flags funFlags = JSAPIToJSFunctionFlags(flags);
-    return NewFunction(cx, NullPtr(), native, nargs, funFlags, parent, atom);
+    return NewFunction(cx, NullPtr(), native, nargs, funFlags, parent, name);
+}
+
+JS_PUBLIC_API(JSFunction *)
+JS::GetSelfHostedFunction(JSContext *cx, const char *selfHostedName, jsid id, unsigned nargs)
+{
+    JS_ASSERT(JSID_IS_STRING(id));
+    JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+
+    RootedAtom name(cx, JSID_TO_ATOM(id));
+    RootedAtom shName(cx, Atomize(cx, selfHostedName, strlen(selfHostedName)));
+    if (!shName)
+        return nullptr;
+    RootedValue funVal(cx);
+    if (!cx->global()->getSelfHostedFunction(cx, shName, name, nargs, &funVal))
+        return nullptr;
+    return &funVal.toObject().as<JSFunction>();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobjArg, JSObject *parentArg)
 {
     RootedObject funobj(cx, funobjArg);
     RootedObject parent(cx, parentArg);
     AssertHeapIsIdle(cx);
@@ -4233,23 +4252,28 @@ JS_DefineFunctions(JSContext *cx, JSObje
         if (fs->selfHostedName) {
             /*
              * During creation of the self-hosting global, we ignore all
              * self-hosted functions, as that means we're currently setting up
              * the global object that the self-hosted code is then compiled
              * in. Self-hosted functions can access each other via their names,
              * but not via the builtin classes they get installed into.
              */
+            JS_ASSERT(!fs->call.op);
+            JS_ASSERT(!fs->call.info);
             if (cx->runtime()->isSelfHostingGlobal(cx->global()))
                 continue;
 
+            RootedAtom shName(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
+            if (!shName)
+                return false;
             RootedValue funVal(cx);
-            if (!cx->global()->getSelfHostedFunction(cx, fs, atom, &funVal))
+            if (!cx->global()->getSelfHostedFunction(cx, shName, atom, fs->nargs, &funVal))
                 return false;
-            if (!JSObject::defineGeneric(cx, obj, id, funVal, NULL, NULL, 0))
+            if (!JSObject::defineGeneric(cx, obj, id, funVal, NULL, NULL, flags))
                 return false;
         } else {
             JSFunction *fun = DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
             if (!fun)
                 return false;
             if (fs->call.info)
                 fun->setJitInfo(fs->call.info);
         }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2378,26 +2378,32 @@ struct JSFunctionSpec {
 
 /*
  * Terminating sentinel initializer to put at the end of a JSFunctionSpec array
  * that's passed to JS_DefineFunctions or JS_InitClass.
  */
 #define JS_FS_END JS_FS(NULL,NULL,0,0)
 
 /*
- * Initializer macros for a JSFunctionSpec array element. JS_FN (whose name
- * pays homage to the old JSNative/JSFastNative split) simply adds the flag
- * JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of JSJitInfos.
+ * Initializer macros for a JSFunctionSpec array element. JS_FN (whose name pays
+ * homage to the old JSNative/JSFastNative split) simply adds the flag
+ * JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of
+ * JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted function. Finally
+ * JS_FNSPEC has slots for all the fields.
  */
 #define JS_FS(name,call,nargs,flags)                                          \
-    {name, JSOP_WRAPPER(call), nargs, flags}
+    JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
 #define JS_FN(name,call,nargs,flags)                                          \
-    {name, JSOP_WRAPPER(call), nargs, (flags) | JSFUN_STUB_GSOPS}
+    JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
 #define JS_FNINFO(name,call,info,nargs,flags)                                 \
-    {name,{call,info},nargs,flags}
+    JS_FNSPEC(name, call, info, nargs, flags, nullptr)
+#define JS_SELF_HOSTED_FN(name,selfHostedName,nargs,flags)                    \
+    JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName)                  \
+    {name, {call, info}, nargs, flags, selfHostedName}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
              const JSClass *clasp, JSNative constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs);
 
 /*
@@ -3096,16 +3102,23 @@ JS_NewFunction(JSContext *cx, JSNative c
 /*
  * Create the function with the name given by the id. JSID_IS_STRING(id) must
  * be true.
  */
 extern JS_PUBLIC_API(JSFunction *)
 JS_NewFunctionById(JSContext *cx, JSNative call, unsigned nargs, unsigned flags,
                    JSObject *parent, jsid id);
 
+namespace JS {
+
+extern JS_PUBLIC_API(JSFunction *)
+GetSelfHostedFunction(JSContext *cx, const char *selfHostedName, jsid id, unsigned nargs);
+
+} /* namespace JS */
+
 extern JS_PUBLIC_API(JSObject *)
 JS_GetFunctionObject(JSFunction *fun);
 
 /*
  * Return the function's identifier as a JSString, or null if fun is unnamed.
  * The returned string lives as long as fun, so you don't need to root a saved
  * reference to it if fun is well-connected or rooted, and provided you bound
  * the use of the saved reference by fun's lifetime.
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2864,59 +2864,59 @@ static const JSFunctionSpec array_method
     JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
     JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
     JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
 
     /* Pythonic sequence methods. */
     JS_FN("concat",             array_concat,       1,JSFUN_GENERIC_NATIVE),
     JS_FN("slice",              array_slice,        2,JSFUN_GENERIC_NATIVE),
 
-         {"lastIndexOf",        {NULL, NULL},       1,0, "ArrayLastIndexOf"},
-         {"indexOf",            {NULL, NULL},       1,0, "ArrayIndexOf"},
-         {"forEach",            {NULL, NULL},       1,0, "ArrayForEach"},
-         {"map",                {NULL, NULL},       1,0, "ArrayMap"},
-         {"reduce",             {NULL, NULL},       1,0, "ArrayReduce"},
-         {"reduceRight",        {NULL, NULL},       1,0, "ArrayReduceRight"},
+    JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
+    JS_SELF_HOSTED_FN("indexOf",     "ArrayIndexOf",     1,0),
+    JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0),
+    JS_SELF_HOSTED_FN("map",         "ArrayMap",         1,0),
+    JS_SELF_HOSTED_FN("reduce",      "ArrayReduce",      1,0),
+    JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
     JS_FN("filter",             array_filter,       1,JSFUN_GENERIC_NATIVE),
-         {"some",               {NULL, NULL},       1,0, "ArraySome"},
-         {"every",              {NULL, NULL},       1,0, "ArrayEvery"},
+    JS_SELF_HOSTED_FN("some",        "ArraySome",        1,0),
+    JS_SELF_HOSTED_FN("every",       "ArrayEvery",       1,0),
 
 #ifdef ENABLE_PARALLEL_JS
     /* Parallelizable and pure methods. */
-         {"mapPar",             {NULL, NULL},       2,0, "ArrayMapPar"},
-         {"reducePar",          {NULL, NULL},       2,0, "ArrayReducePar"},
-         {"scanPar",            {NULL, NULL},       2,0, "ArrayScanPar"},
-         {"scatterPar",         {NULL, NULL},       5,0, "ArrayScatterPar"},
-         {"filterPar",          {NULL, NULL},       2,0, "ArrayFilterPar"},
+    JS_SELF_HOSTED_FN("mapPar",      "ArrayMapPar",      2,0),
+    JS_SELF_HOSTED_FN("reducePar",   "ArrayReducePar",   2,0),
+    JS_SELF_HOSTED_FN("scanPar",     "ArrayScanPar",     2,0),
+    JS_SELF_HOSTED_FN("scatterPar",  "ArrayScatterPar",  5,0),
+    JS_SELF_HOSTED_FN("filterPar",   "ArrayFilterPar",   2,0),
 #endif
 
     /* ES6 additions */
-         {"find",               {NULL, NULL},       1,0, "ArrayFind"},
-         {"findIndex",          {NULL, NULL},       1,0, "ArrayFindIndex"},
+    JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
+    JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
 
     JS_FN("iterator",           JS_ArrayIterator,   0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
-         {"lastIndexOf",        {NULL, NULL},       2,0, "ArrayStaticLastIndexOf"},
-         {"indexOf",            {NULL, NULL},       2,0, "ArrayStaticIndexOf"},
-         {"forEach",            {NULL, NULL},       2,0, "ArrayStaticForEach"},
-         {"map",                {NULL, NULL},       2,0, "ArrayStaticMap"},
-         {"every",              {NULL, NULL},       2,0, "ArrayStaticEvery"},
-         {"some",               {NULL, NULL},       2,0, "ArrayStaticSome"},
-         {"reduce",             {NULL, NULL},       2,0, "ArrayStaticReduce"},
-         {"reduceRight",        {NULL, NULL},       2,0, "ArrayStaticReduceRight"},
+    JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
+    JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
+    JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
+    JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
+    JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
+    JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
+    JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
+    JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
     JS_FN("of",                 array_of,           0,0),
 
 #ifdef ENABLE_PARALLEL_JS
-         {"build",              {NULL, NULL},       2,0, "ArrayStaticBuild"},
+    JS_SELF_HOSTED_FN("build",       "ArrayStaticBuild", 2,0),
     /* Parallelizable and pure static methods. */
-         {"buildPar",           {NULL, NULL},       3,0, "ArrayStaticBuildPar"},
+    JS_SELF_HOSTED_FN("buildPar",    "ArrayStaticBuildPar", 3,0),
 #endif
 
     JS_FS_END
 };
 
 /* ES5 15.4.2 */
 bool
 js_Array(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2941,19 +2941,19 @@ static const JSFunctionSpec date_methods
     JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
     JS_FN("setSeconds",          date_setSeconds,         2,0),
     JS_FN("setUTCSeconds",       date_setUTCSeconds,      2,0),
     JS_FN("setMilliseconds",     date_setMilliseconds,    1,0),
     JS_FN("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0),
     JS_FN("toUTCString",         date_toGMTString,        0,0),
     JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
 #if EXPOSE_INTL_API
-         {js_toLocaleString_str, {NULL, NULL},            0,0, "Date_toLocaleString"},
-         {"toLocaleDateString",  {NULL, NULL},            0,0, "Date_toLocaleDateString"},
-         {"toLocaleTimeString",  {NULL, NULL},            0,0, "Date_toLocaleTimeString"},
+    JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
+    JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
+    JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
 #else
     JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
     JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
     JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
 #endif
     JS_FN("toDateString",        date_toDateString,       0,0),
     JS_FN("toTimeString",        date_toTimeString,       0,0),
     JS_FN("toISOString",         date_toISOString,        0,0),
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1192,18 +1192,17 @@ JSFunction::createScriptForLazilyInterpr
 
         // Remember the compiled script on the lazy script itself, in case
         // there are clones of the function still pointing to the lazy script.
         lazy->initScript(script);
         return true;
     }
 
     /* Lazily cloned self hosted script. */
-    JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(fun->getExtendedSlot(0).toPrivate());
-    RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
+    RootedAtom funAtom(cx, &fun->getExtendedSlot(0).toString()->asAtom());
     if (!funAtom)
         return false;
     Rooted<PropertyName *> funName(cx, funAtom->asPropertyName());
     return cx->runtime()->cloneSelfHostedFunctionScript(cx, funName, fun);
 }
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
 bool
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1004,17 +1004,17 @@ num_toPrecision(JSContext *cx, unsigned 
 }
 
 static const JSFunctionSpec number_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,       num_toSource,          0, 0),
 #endif
     JS_FN(js_toString_str,       js_num_toString,       1, 0),
 #if EXPOSE_INTL_API
-         {js_toLocaleString_str, {NULL, NULL},           0,0, "Number_toLocaleString"},
+    JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0,0),
 #else
     JS_FN(js_toLocaleString_str, num_toLocaleString,     0,0),
 #endif
     JS_FN(js_valueOf_str,        js_num_valueOf,        0, 0),
     JS_FN("toFixed",             num_toFixed,           1, 0),
     JS_FN("toExponential",       num_toExponential,     1, 0),
     JS_FN("toPrecision",         num_toPrecision,       1, 0),
     JS_FS_END
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3619,21 +3619,21 @@ static const JSFunctionSpec string_metho
     JS_FN("startsWith",        str_startsWith,        1,JSFUN_GENERIC_NATIVE),
     JS_FN("endsWith",          str_endsWith,          1,JSFUN_GENERIC_NATIVE),
     JS_FN("trim",              str_trim,              0,JSFUN_GENERIC_NATIVE),
     JS_FN("trimLeft",          str_trimLeft,          0,JSFUN_GENERIC_NATIVE),
     JS_FN("trimRight",         str_trimRight,         0,JSFUN_GENERIC_NATIVE),
     JS_FN("toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE),
     JS_FN("toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE),
 #if EXPOSE_INTL_API
-         {"localeCompare",     {NULL, NULL},          1,0, "String_localeCompare"},
+    JS_SELF_HOSTED_FN("localeCompare", "String_localeCompare", 1,0),
 #else
     JS_FN("localeCompare",     str_localeCompare,     1,JSFUN_GENERIC_NATIVE),
 #endif
-         {"repeat",            {NULL, NULL},          1,0, "String_repeat"},
+    JS_SELF_HOSTED_FN("repeat", "String_repeat",      1,0),
 
     /* Perl-ish methods (search is actually Python-esque). */
     JS_FN("match",             str_match,             1,JSFUN_GENERIC_NATIVE),
     JS_FN("search",            str_search,            1,JSFUN_GENERIC_NATIVE),
     JS_FN("replace",           str_replace,           2,JSFUN_GENERIC_NATIVE),
     JS_FN("split",             str_split,             2,JSFUN_GENERIC_NATIVE),
     JS_FN("substr",            str_substr,            2,JSFUN_GENERIC_NATIVE),
 
@@ -3727,17 +3727,17 @@ js::str_fromCharCode(JSContext *cx, unsi
 }
 
 static const JSFunctionSpec string_static_methods[] = {
     JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
 
     // This must be at the end because of bug 853075: functions listed after
     // self-hosted methods aren't available in self-hosted code.
 #if EXPOSE_INTL_API
-         {"localeCompare",     {NULL, NULL},          2,0, "String_static_localeCompare"},
+    JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0),
 #endif
     JS_FS_END
 };
 
 Shape *
 StringObject::assignInitialShape(JSContext *cx)
 {
     JS_ASSERT(nativeEmpty());
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -626,36 +626,32 @@ GlobalObject::addDebugger(JSContext *cx,
     if (!debuggers->append(dbg)) {
         global->compartment()->removeDebuggee(cx->runtime()->defaultFreeOp(), global);
         return false;
     }
     return true;
 }
 
 bool
-GlobalObject::getSelfHostedFunction(JSContext *cx, const JSFunctionSpec *fs, HandleAtom atom,
-                                    MutableHandleValue funVal)
+GlobalObject::getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
+                                    unsigned nargs, MutableHandleValue funVal)
 {
-    JS_ASSERT(fs->selfHostedName);
-    RootedAtom shAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
-    if (!shAtom)
-        return false;
-    RootedId shId(cx, AtomToId(shAtom));
+    RootedId shId(cx, AtomToId(selfHostedName));
     RootedObject holder(cx, cx->global()->intrinsicsHolder());
 
     if (HasDataProperty(cx, holder, shId, funVal.address()))
         return true;
 
     if (!cx->runtime()->maybeWrappedSelfHostedFunction(cx, shId, funVal))
         return false;
     if (!funVal.isUndefined())
         return true;
 
-    JSFunction *fun = NewFunction(cx, NullPtr(), NULL, fs->nargs, JSFunction::INTERPRETED_LAZY,
-                                  holder, atom, JSFunction::ExtendedFinalizeKind, SingletonObject);
+    JSFunction *fun = NewFunction(cx, NullPtr(), NULL, nargs, JSFunction::INTERPRETED_LAZY,
+                                  holder, name, JSFunction::ExtendedFinalizeKind, SingletonObject);
     if (!fun)
         return false;
     fun->setIsSelfHostedBuiltin();
-    fun->setExtendedSlot(0, PrivateValue(const_cast<JSFunctionSpec*>(fs)));
+    fun->setExtendedSlot(0, StringValue(selfHostedName));
     funVal.setObject(*fun);
 
     return JSObject::defineGeneric(cx, holder, shId, funVal, NULL, NULL, 0);
 }
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -451,18 +451,19 @@ class GlobalObject : public JSObject
 #ifdef DEBUG
         RootedObject self(cx, this);
         JS_ASSERT(cx->runtime()->isSelfHostingGlobal(self));
 #endif
         RootedObject holder(cx, intrinsicsHolder());
         RootedValue valCopy(cx, value);
         return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
     }
-    bool getSelfHostedFunction(JSContext *cx, const JSFunctionSpec *fs, HandleAtom atom,
-                               MutableHandleValue funVal);
+
+    bool getSelfHostedFunction(JSContext *cx, HandleAtom selfHostedName, HandleAtom name,
+                               unsigned nargs, MutableHandleValue funVal);
 
     RegExpStatics *getRegExpStatics() const {
         JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
         return static_cast<RegExpStatics *>(resObj.getPrivate());
     }
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());