Bug 1067459 - Only install a small subset of the standard builtins in the self-hosting global. r=jorendorff
authorTill Schneidereit <till@tillschneidereit.net>
Tue, 16 Sep 2014 10:01:09 -0400
changeset 208190 ba00c79e932c9b720c2441ba84d95d561649c8e3
parent 208189 bef7426ecdb2afdd17c449f6a4e8eb86147a82ca
child 208191 1c6f050126d7200a72802ebec3c51dc7e9f904f9
push id27580
push userkwierso@gmail.com
push dateWed, 01 Oct 2014 23:26:55 +0000
treeherderautoland@af6c928893c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1067459
milestone35.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 1067459 - Only install a small subset of the standard builtins in the self-hosting global. r=jorendorff
js/src/builtin/Intl.cpp
js/src/builtin/Intl.js
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/builtin/Object.cpp
js/src/builtin/Object.h
js/src/builtin/Utilities.js
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsdate.cpp
js/src/jsdate.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jsweakmap.cpp
js/src/jsweakmap.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/StructuredClone.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -2050,27 +2050,22 @@ js_InitIntlClass(JSContext *cx, HandleOb
                                   JS_PropertyStub, JS_StrictPropertyStub, 0))
     {
         return nullptr;
     }
 
     if (!JS_DefineFunctions(cx, Intl, intl_static_methods))
         return nullptr;
 
-    // Skip initialization of the Intl constructors during initialization of the
-    // self-hosting global as we may get here before self-hosted code is compiled,
-    // and no core code refers to the Intl classes.
-    if (!cx->runtime()->isSelfHostingGlobal(cx->global())) {
-        if (!InitCollatorClass(cx, Intl, global))
-            return nullptr;
-        if (!InitNumberFormatClass(cx, Intl, global))
-            return nullptr;
-        if (!InitDateTimeFormatClass(cx, Intl, global))
-            return nullptr;
-    }
+    if (!InitCollatorClass(cx, Intl, global))
+        return nullptr;
+    if (!InitNumberFormatClass(cx, Intl, global))
+        return nullptr;
+    if (!InitDateTimeFormatClass(cx, Intl, global))
+        return nullptr;
 
     global->setConstructor(JSProto_Intl, ObjectValue(*Intl));
 
     return Intl;
 }
 
 bool
 GlobalObject::initIntlObject(JSContext *cx, Handle<GlobalObject*> global)
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1269,17 +1269,17 @@ function InitializeCollator(collator, lo
 
     // Steps 11-12.
     var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
     opt.localeMatcher = matcher;
 
     // Step 13, unrolled.
     var numericValue = GetOption(options, "numeric", "boolean", undefined, undefined);
     if (numericValue !== undefined)
-        numericValue = callFunction(std_Boolean_toString, numericValue);
+        numericValue = numericValue ? 'true' : 'false';
     opt.kn = numericValue;
 
     var caseFirstValue = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
     opt.kf = caseFirstValue;
 
     // Compute remaining collation options.
     // Step 20.
     var s = GetOption(options, "sensitivity", "string",
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -844,24 +844,24 @@ class MapIteratorObject : public JSObjec
 {
   public:
     static const Class class_;
 
     enum { TargetSlot, KindSlot, RangeSlot, SlotCount };
     static const JSFunctionSpec methods[];
     static MapIteratorObject *create(JSContext *cx, HandleObject mapobj, ValueMap *data,
                                      MapObject::IteratorKind kind);
+    static bool next(JSContext *cx, unsigned argc, Value *vp);
     static void finalize(FreeOp *fop, JSObject *obj);
 
   private:
     static inline bool is(HandleValue v);
     inline ValueMap::Range *range();
     inline MapObject::IteratorKind kind() const;
     static bool next_impl(JSContext *cx, CallArgs args);
-    static bool next(JSContext *cx, unsigned argc, Value *vp);
 };
 
 } /* anonymous namespace */
 
 const Class MapIteratorObject::class_ = {
     "Map Iterator",
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
@@ -1152,17 +1152,18 @@ WriteBarrierPost(JSRuntime *rt, ValueSet
     if (MOZ_UNLIKELY(key.isObject() && IsInsideNursery(&key.toObject()))) {
         rt->gc.storeBuffer.putGeneric(OrderedHashTableRef<UnbarrieredSet>(
                     reinterpret_cast<UnbarrieredSet *>(set), key));
     }
 #endif
 }
 
 bool
-MapObject::entries(JSContext *cx, HandleObject obj, JS::AutoValueVector *entries)
+MapObject::getKeysAndValuesInterleaved(JSContext *cx, HandleObject obj,
+                                       JS::AutoValueVector *entries)
 {
     ValueMap *map = obj->as<MapObject>().getData();
     if (!map)
         return false;
 
     for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
         if (!entries->append(r.front().key.get()) ||
             !entries->append(r.front().value))
@@ -1492,24 +1493,24 @@ class SetIteratorObject : public JSObjec
 {
   public:
     static const Class class_;
 
     enum { TargetSlot, KindSlot, RangeSlot, SlotCount };
     static const JSFunctionSpec methods[];
     static SetIteratorObject *create(JSContext *cx, HandleObject setobj, ValueSet *data,
                                      SetObject::IteratorKind kind);
+    static bool next(JSContext *cx, unsigned argc, Value *vp);
     static void finalize(FreeOp *fop, JSObject *obj);
 
   private:
     static inline bool is(HandleValue v);
     inline ValueSet::Range *range();
     inline SetObject::IteratorKind kind() const;
     static bool next_impl(JSContext *cx, CallArgs args);
-    static bool next(JSContext *cx, unsigned argc, Value *vp);
 };
 
 } /* anonymous namespace */
 
 const Class SetIteratorObject::class_ = {
     "Set Iterator",
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount),
@@ -1961,8 +1962,20 @@ SetObject::clear(JSContext *cx, unsigned
     return CallNonGenericMethod(cx, is, clear_impl, args);
 }
 
 JSObject *
 js_InitSetClass(JSContext *cx, HandleObject obj)
 {
     return SetObject::initClass(cx, obj);
 }
+
+const JSFunctionSpec selfhosting_collection_iterator_methods[] = {
+    JS_FN("std_Map_iterator_next", MapIteratorObject::next, 0, 0),
+    JS_FN("std_Set_iterator_next", SetIteratorObject::next, 0, 0),
+    JS_FS_END
+};
+
+bool
+js::InitSelfHostingCollectionIteratorFunctions(JSContext *cx, HandleObject obj)
+{
+    return JS_DefineFunctions(cx, obj, selfhosting_collection_iterator_methods);
+}
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -87,19 +87,21 @@ typedef OrderedHashSet<HashableValue,
 
 class MapObject : public JSObject {
   public:
     enum IteratorKind { Keys, Values, Entries };
 
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static const Class class_;
 
-    // Entries is every key followed by value.
-    static bool entries(JSContext *cx, HandleObject obj, JS::AutoValueVector *entries);
+    static bool getKeysAndValuesInterleaved(JSContext *cx, HandleObject obj,
+                                            JS::AutoValueVector *entries);
+    static bool entries(JSContext *cx, unsigned argc, Value *vp);
     static bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue value);
+    static bool has(JSContext *cx, unsigned argc, Value *vp);
     static MapObject* create(JSContext *cx);
 
   private:
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     ValueMap *getData() { return static_cast<ValueMap *>(getPrivate()); }
     static ValueMap & extract(CallReceiver call);
     static void mark(JSTracer *trc, JSObject *obj);
@@ -110,39 +112,39 @@ class MapObject : public JSObject {
 
     static bool iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind);
 
     static bool size_impl(JSContext *cx, CallArgs args);
     static bool size(JSContext *cx, unsigned argc, Value *vp);
     static bool get_impl(JSContext *cx, CallArgs args);
     static bool get(JSContext *cx, unsigned argc, Value *vp);
     static bool has_impl(JSContext *cx, CallArgs args);
-    static bool has(JSContext *cx, unsigned argc, Value *vp);
     static bool set_impl(JSContext *cx, CallArgs args);
     static bool set(JSContext *cx, unsigned argc, Value *vp);
     static bool delete_impl(JSContext *cx, CallArgs args);
     static bool delete_(JSContext *cx, unsigned argc, Value *vp);
     static bool keys_impl(JSContext *cx, CallArgs args);
     static bool keys(JSContext *cx, unsigned argc, Value *vp);
     static bool values_impl(JSContext *cx, CallArgs args);
     static bool values(JSContext *cx, unsigned argc, Value *vp);
     static bool entries_impl(JSContext *cx, CallArgs args);
-    static bool entries(JSContext *cx, unsigned argc, Value *vp);
     static bool clear_impl(JSContext *cx, CallArgs args);
     static bool clear(JSContext *cx, unsigned argc, Value *vp);
 };
 
 class SetObject : public JSObject {
   public:
     enum IteratorKind { Values, Entries };
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static const Class class_;
 
     static bool keys(JSContext *cx, HandleObject obj, JS::AutoValueVector *keys);
+    static bool values(JSContext *cx, unsigned argc, Value *vp);
     static bool add(JSContext *cx, HandleObject obj, HandleValue key);
+    static bool has(JSContext *cx, unsigned argc, Value *vp);
     static SetObject* create(JSContext *cx);
 
   private:
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     ValueSet *getData() { return static_cast<ValueSet *>(getPrivate()); }
     static ValueSet & extract(CallReceiver call);
     static void mark(JSTracer *trc, JSObject *obj);
@@ -151,29 +153,30 @@ class SetObject : public JSObject {
 
     static bool is(HandleValue v);
 
     static bool iterator_impl(JSContext *cx, CallArgs args, IteratorKind kind);
 
     static bool size_impl(JSContext *cx, CallArgs args);
     static bool size(JSContext *cx, unsigned argc, Value *vp);
     static bool has_impl(JSContext *cx, CallArgs args);
-    static bool has(JSContext *cx, unsigned argc, Value *vp);
     static bool add_impl(JSContext *cx, CallArgs args);
     static bool add(JSContext *cx, unsigned argc, Value *vp);
     static bool delete_impl(JSContext *cx, CallArgs args);
     static bool delete_(JSContext *cx, unsigned argc, Value *vp);
     static bool values_impl(JSContext *cx, CallArgs args);
-    static bool values(JSContext *cx, unsigned argc, Value *vp);
     static bool entries_impl(JSContext *cx, CallArgs args);
     static bool entries(JSContext *cx, unsigned argc, Value *vp);
     static bool clear_impl(JSContext *cx, CallArgs args);
     static bool clear(JSContext *cx, unsigned argc, Value *vp);
 };
 
+extern bool
+InitSelfHostingCollectionIteratorFunctions(JSContext *cx, js::HandleObject obj);
+
 } /* namespace js */
 
 extern JSObject *
 js_InitMapClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
 js_InitSetClass(JSContext *cx, js::HandleObject obj);
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -527,18 +527,18 @@ obj_lookupSetter(JSContext *cx, unsigned
                 args.rval().set(shape->setterValue());
         }
     }
     return true;
 }
 #endif /* JS_OLD_GETTER_SETTER_METHODS */
 
 /* ES5 15.2.3.2. */
-static bool
-obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     if (args.length() == 0) {
         js_ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
@@ -698,18 +698,18 @@ obj_unwatch(JSContext *cx, unsigned argc
 
     args.rval().setUndefined();
     return true;
 }
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
 /* ECMA 15.2.4.5. */
-static bool
-obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     HandleValue idValue = args.get(0);
 
     /* Step 1, 2. */
     jsid id;
     if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
@@ -773,18 +773,18 @@ obj_isPrototypeOf(JSContext *cx, unsigne
     bool isDelegate;
     if (!IsDelegate(cx, obj, args[0], &isDelegate))
         return false;
     args.rval().setBoolean(isDelegate);
     return true;
 }
 
 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
-static bool
-obj_create(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::obj_create(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                              "Object.create", "0", "s");
         return false;
     }
 
@@ -821,18 +821,18 @@ obj_create(JSContext *cx, unsigned argc,
             return false;
     }
 
     /* 5. Return obj. */
     args.rval().setObject(*obj);
     return true;
 }
 
-static bool
-obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, args, "Object.getOwnPropertyDescriptor", &obj))
         return false;
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args.get(1), &id))
         return false;
@@ -946,18 +946,18 @@ GetOwnPropertyKeys(JSContext *cx, const 
         return false;
 
     args.rval().setObject(*aobj);
     return true;
 }
 
 } // namespace js
 
-static bool
-obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN);
 }
 
 /* ES6 draft rev 25 (2014 May 22) 19.1.2.8 */
 static bool
 obj_getOwnPropertySymbols(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -23,16 +23,32 @@ extern const JSFunctionSpec object_stati
 
 // Object constructor native. Exposed only so the JIT can know its address.
 bool
 obj_construct(JSContext *cx, unsigned argc, JS::Value *vp);
 
 bool
 obj_valueOf(JSContext *cx, unsigned argc, JS::Value *vp);
 
+// Object methods exposed so they can be installed in the self-hosting global.
+bool
+obj_create(JSContext *cx, unsigned argc, JS::Value *vp);
+
+bool
+obj_getOwnPropertyNames(JSContext *cx, unsigned argc, JS::Value *vp);
+
+bool
+obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, JS::Value *vp);
+
+bool
+obj_getPrototypeOf(JSContext *cx, unsigned argc, JS::Value *vp);
+
+bool
+obj_hasOwnProperty(JSContext *cx, unsigned argc, JS::Value *vp);
+
 // Exposed so SelfHosting.cpp can use it in the OwnPropertyKeys intrinsic
 bool
 GetOwnPropertyKeys(JSContext *cx, const JS::CallArgs &args, unsigned flags);
 
 /*
  * Like IdToValue, but convert int jsids to strings. This is used when
  * exposing a jsid to script for Object.getOwnProperty{Names,Symbols}
  * or scriptable proxy traps.
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -20,81 +20,28 @@
          callFunction: false,
          TO_UINT32: false,
          JSMSG_NOT_FUNCTION: false, JSMSG_MISSING_FUN_ARG: false,
          JSMSG_EMPTY_ARRAY_REDUCE: false, JSMSG_CANT_CONVERT_TO: false,
 */
 
 #include "SelfHostingDefines.h"
 
-// Remove unsafe builtin functions.
-Object.defineProperty = null; // See bug 988416.
-
-// Cache builtin functions so using them doesn't require cloning the whole object they're
-// installed on.
+// All C++-implemented standard builtins library functions used in self-hosted
+// code are installed via the std_functions JSFunctionSpec[] in
+// SelfHosting.cpp.
 //
-// WARNING: Do not make std_ references to builtin constructors (like Array and
-// Object) below. Setting `var std_Array = Array;`, for instance, would cause
-// the entire Array constructor, including its prototype and methods, to be
-// cloned into content compartments.
+// The few items below here are either self-hosted or installing them under a
+// std_Foo name would require ugly contortions, so they just get aliased here.
+var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.
 var std_Array_indexOf = ArrayIndexOf;
-var std_Array_iterator = Array.prototype.iterator;
-var std_Array_join = Array.prototype.join;
-var std_Array_push = Array.prototype.push;
-var std_Array_pop = Array.prototype.pop;
-var std_Array_shift = Array.prototype.shift;
-var std_Array_slice = Array.prototype.slice;
-var std_Array_sort = Array.prototype.sort;
-var std_Array_unshift = Array.prototype.unshift;
-var std_Boolean_toString = Boolean.prototype.toString;
-var Std_Date = Date;
-var std_Date_now = Date.now;
-var std_Date_valueOf = Date.prototype.valueOf;
-var std_Function_bind = Function.prototype.bind;
-var std_Function_apply = Function.prototype.apply;
-var std_Math_floor = Math.floor;
-var std_Math_max = Math.max;
-var std_Math_min = Math.min;
-var std_Math_abs = Math.abs;
-var std_Math_imul = Math.imul;
-var std_Math_log2 = Math.log2;
-var std_Number_valueOf = Number.prototype.valueOf;
-var std_Number_POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
-var std_Object_create = Object.create;
-var std_Object_getOwnPropertyNames = Object.getOwnPropertyNames;
-var std_Object_hasOwnProperty = Object.prototype.hasOwnProperty;
-var std_Object_getPrototypeOf = Object.getPrototypeOf;
-var std_Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
-var std_RegExp_test = RegExp.prototype.test;
-var std_String_fromCharCode = String.fromCharCode;
-var std_String_charCodeAt = String.prototype.charCodeAt;
-var std_String_indexOf = String.prototype.indexOf;
-var std_String_lastIndexOf = String.prototype.lastIndexOf;
-var std_String_match = String.prototype.match;
-var std_String_replace = String.prototype.replace;
-var std_String_split = String.prototype.split;
-var std_String_startsWith = String.prototype.startsWith;
-var std_String_substring = String.prototype.substring;
-var std_String_toLowerCase = String.prototype.toLowerCase;
-var std_String_toUpperCase = String.prototype.toUpperCase;
+// WeakMap is a bare constructor without properties or methods.
 var std_WeakMap = WeakMap;
-var std_WeakMap_get = WeakMap.prototype.get;
-var std_WeakMap_has = WeakMap.prototype.has;
-var std_WeakMap_set = WeakMap.prototype.set;
-var std_WeakMap_clear = WeakMap.prototype.clear;
-var std_WeakMap_delete = WeakMap.prototype.delete;
-var std_Map_has = Map.prototype.has;
-var std_Set_has = Set.prototype.has;
-var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.
+// StopIteration is a bare constructor without properties or methods.
 var std_StopIteration = StopIteration;
-var std_Map_iterator = Map.prototype[std_iterator];
-var std_Set_iterator = Set.prototype[std_iterator];
-var std_Map_iterator_next = Object.getPrototypeOf(Map()[std_iterator]()).next;
-var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
-
 
 
 /********** List specification type **********/
 
 
 /* Spec: ECMAScript Language Specification, 5.1 edition, 8.8 */
 function List() {}
 {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3273,25 +3273,16 @@ JS_DefineProperties(JSContext *cx, Handl
 
             ok = DefineProperty(cx, obj, ps->name, JS::UndefinedHandleValue,
                                 ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0);
         } else {
             // If you have self-hosted getter/setter, you can't have a
             // native one.
             JS_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op);
             JS_ASSERT(ps->flags & JSPROP_GETTER);
-            /*
-             * During creation of the self-hosting global, we ignore all
-             * self-hosted properties, as that means we're currently setting up
-             * the global object that the self-hosted code is then compiled
-             * in. That means that Self-hosted properties can't be used in the
-             * self-hosting global itself, right now.
-             */
-            if (cx->runtime()->isSelfHostingGlobal(cx->global()))
-                continue;
 
             ok = DefineSelfHostedProperty(cx, obj, ps->name,
                                           ps->getter.selfHosted.funname,
                                           ps->setter.selfHosted.funname,
                                           ps->flags, 0);
         }
         if (!ok)
             break;
@@ -4124,25 +4115,16 @@ JS_DefineFunctions(JSContext *cx, Handle
          * Delay cloning self-hosted functions until they are called. This is
          * achieved by passing DefineFunction a nullptr JSNative which
          * produces an interpreted JSFunction where !hasScript. Interpreted
          * call paths then call InitializeLazyFunctionScript if !hasScript.
          */
         if (fs->selfHostedName) {
             JS_ASSERT(!fs->call.op);
             JS_ASSERT(!fs->call.info);
-            /*
-             * 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.
-             */
-            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, shName, atom, fs->nargs, &funVal))
                 return false;
             if (!JSObject::defineGeneric(cx, obj, id, funVal, nullptr, nullptr, flags))
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2241,18 +2241,18 @@ js::array_shift(JSContext *cx, unsigned 
     /* Step 8. */
     if (!DeletePropertyOrThrow(cx, obj, newlen))
         return false;
 
     /* Step 9. */
     return SetLengthProperty(cx, obj, newlen);
 }
 
-static bool
-array_unshift(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::array_unshift(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
@@ -2697,18 +2697,18 @@ js::array_concat(JSContext *cx, unsigned
         if (!SetArrayElement(cx, narr, length, v))
             return false;
         length++;
     }
 
     return SetLengthProperty(cx, narr, length);
 }
 
-static bool
-array_slice(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::array_slice(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     uint32_t length;
@@ -2976,17 +2976,17 @@ array_of(JSContext *cx, unsigned argc, V
 static const JSFunctionSpec array_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,      array_toSource,     0,0),
 #endif
     JS_FN(js_toString_str,      array_toString,     0,0),
     JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
 
     /* Perl-ish methods. */
-    JS_FN("join",               js::array_join,     1,JSFUN_GENERIC_NATIVE),
+    JS_FN("join",               array_join,         1,JSFUN_GENERIC_NATIVE),
     JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
     JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
     JS_FN("push",               array_push,         1,JSFUN_GENERIC_NATIVE),
     JS_FN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE),
     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),
 
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -174,16 +174,22 @@ extern JSString *
 array_join_impl(JSContext *cx, HandleValue array, HandleString sep);
 
 extern void
 ArrayShiftMoveElements(JSObject *obj);
 
 extern bool
 array_shift(JSContext *cx, unsigned argc, js::Value *vp);
 
+extern bool
+array_unshift(JSContext *cx, unsigned argc, js::Value *vp);
+
+extern bool
+array_slice(JSContext *cx, unsigned argc, js::Value *vp);
+
 /*
  * Append the given (non-hole) value to the end of an array.  The array must be
  * a newborn array -- that is, one which has not been exposed to script for
  * arbitrary manipulation.  (This method optimizes on the assumption that
  * extending the array to accommodate the element will never make the array
  * sparse, which requires that the array be completely filled.)
  */
 extern bool
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1190,18 +1190,18 @@ date_parse(JSContext *cx, unsigned argc,
 }
 
 static inline double
 NowAsMillis()
 {
     return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
 }
 
-static bool
-date_now(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::date_now(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setDouble(NowAsMillis());
     return true;
 }
 
 void
 DateObject::setUTCTime(double t)
@@ -2858,18 +2858,18 @@ date_toString(JSContext *cx, unsigned ar
 MOZ_ALWAYS_INLINE bool
 date_valueOf_impl(JSContext *cx, CallArgs args)
 {
     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
     args.rval().set(dateObj->UTCTime());
     return true;
 }
 
-static bool
-date_valueOf(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::date_valueOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
 }
 
 static const JSFunctionSpec date_static_methods[] = {
     JS_FN("UTC",                 date_UTC,                MAXARGS,0),
     JS_FN("parse",               date_parse,              1,0),
--- a/js/src/jsdate.h
+++ b/js/src/jsdate.h
@@ -55,9 +55,20 @@ js_DateGetMinutes(JSContext *cx, JSObjec
 
 extern JS_FRIEND_API(int)
 js_DateGetSeconds(JSObject *obj);
 
 /* Date constructor native. Exposed only so the JIT can know its address. */
 bool
 js_Date(JSContext *cx, unsigned argc, JS::Value *vp);
 
+namespace js {
+
+/* Date methods exposed so they can be installed in the self-hosting global. */
+bool
+date_now(JSContext *cx, unsigned argc, JS::Value *vp);
+
+bool
+date_valueOf(JSContext *cx, unsigned argc, JS::Value *vp);
+
+} /* namespace js */
+
 #endif /* jsdate_h */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1577,18 +1577,18 @@ fun_isGenerator(JSContext *cx, unsigned 
         return true;
     }
 
     args.rval().setBoolean(fun->isGenerator());
     return true;
 }
 
 /* ES5 15.3.4.5. */
-static bool
-fun_bind(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::fun_bind(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     RootedValue thisv(cx, args.thisv());
 
     /* Step 2. */
     if (!IsCallable(thisv)) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -531,16 +531,19 @@ bool
 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
 
 extern bool
 fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
 
 extern bool
 fun_toString(JSContext *cx, unsigned argc, Value *vp);
 
+extern bool
+fun_bind(JSContext *cx, unsigned argc, Value *vp);
+
 /*
  * 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/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -2094,27 +2094,34 @@ GlobalObject::initIteratorClasses(JSCont
         if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
             return false;
 
         global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
         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;
+    return GlobalObject::initStopIterationClass(cx, global);
+}
+
+/* static */ bool
+GlobalObject::initStopIterationClass(JSContext *cx, Handle<GlobalObject *> global)
+{
+    if (!global->getPrototype(JSProto_StopIteration).isUndefined())
+        return true;
 
-        // This should use a non-JSProtoKey'd slot, but this is easier for now.
-        if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
-            return false;
+    RootedObject proto(cx, global->createBlankPrototype(cx, &StopIterationObject::class_));
+    if (!proto || !JSObject::freeze(cx, proto))
+        return false;
 
-        global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
-    }
+    // This should use a non-JSProtoKey'd slot, but this is easier for now.
+    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
+        return false;
+
+    global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
 
     return true;
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -131,17 +131,18 @@ FinishObjectClassInit(JSContext *cx, JS:
     /* 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)
         return false;
     self->setOriginalEval(evalobj);
 
     RootedObject intrinsicsHolder(cx);
-    if (cx->runtime()->isSelfHostingGlobal(self)) {
+    bool isSelfHostingGlobal = cx->runtime()->isSelfHostingGlobal(self);
+    if (isSelfHostingGlobal) {
         intrinsicsHolder = self;
     } else {
         intrinsicsHolder = NewObjectWithGivenProto(cx, &JSObject::class_, proto, self,
                                                    TenuredObject);
         if (!intrinsicsHolder)
             return false;
     }
     self->setIntrinsicsHolder(intrinsicsHolder);
@@ -153,18 +154,20 @@ FinishObjectClassInit(JSContext *cx, JS:
     {
         return false;
     }
 
     /*
      * Define self-hosted functions after setting the intrinsics holder
      * (which is needed to define self-hosted functions)
      */
-    if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
-        return false;
+    if (!isSelfHostingGlobal) {
+        if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
+            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
      * [[Prototype]] before standard classes have been initialized.  For now,
      * only set the [[Prototype]] if it hasn't already been set.
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -622,18 +622,18 @@ DoSubstr(JSContext *cx, JSString *str, s
             return nullptr;
 
         return JSRope::new_<CanGC>(cx, lhs, rhs, len);
     }
 
     return NewDependentString(cx, str, begin, len);
 }
 
-static bool
-str_substring(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_substring(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSString *str = ThisToStringForStringProto(cx, args);
     if (!str)
         return false;
 
     int32_t length, begin, end;
@@ -750,18 +750,18 @@ ToLowerCaseHelper(JSContext *cx, CallRec
         str = ToLowerCase<char16_t>(cx, linear);
     if (!str)
         return false;
 
     call.rval().setString(str);
     return true;
 }
 
-static bool
-str_toLowerCase(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_toLowerCase(JSContext *cx, unsigned argc, Value *vp)
 {
     return ToLowerCaseHelper(cx, CallArgsFromVp(argc, vp));
 }
 
 static bool
 str_toLocaleLowerCase(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -901,18 +901,18 @@ ToUpperCaseHelper(JSContext *cx, CallRec
         str = ToUpperCase<char16_t>(cx, linear);
     if (!str)
         return false;
 
     call.rval().setString(str);
     return true;
 }
 
-static bool
-str_toUpperCase(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_toUpperCase(JSContext *cx, unsigned argc, Value *vp)
 {
     return ToUpperCaseHelper(cx, CallArgsFromVp(argc, vp));
 }
 
 static bool
 str_toLocaleUpperCase(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1648,18 +1648,18 @@ str_contains(JSContext *cx, unsigned arg
     if (!text)
         return false;
 
     args.rval().setBoolean(StringMatch(text, searchStr, start) != -1);
     return true;
 }
 
 /* ES6 20120927 draft 15.5.4.7. */
-static bool
-str_indexOf(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_indexOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
@@ -1721,18 +1721,18 @@ LastIndexOfImpl(const TextChar *text, si
             return static_cast<int32_t>(t - text);
         }
       break_continue:;
     }
 
     return -1;
 }
 
-static bool
-str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedString textstr(cx, ThisToStringForStringProto(cx, args));
     if (!textstr)
         return false;
 
     RootedLinearString pat(cx, ArgToRootedString(cx, args, 0));
     if (!pat)
@@ -1815,18 +1815,18 @@ HasSubstringAt(JSLinearString *text, JSL
     const char16_t *textChars = text->twoByteChars(nogc) + start;
     if (pat->hasTwoByteChars())
         return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
 
     return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
 }
 
 /* ES6 20131108 draft 21.1.3.18. */
-static bool
-str_startsWith(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::str_startsWith(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -295,16 +295,36 @@ extern bool
 str_replace(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 str_fromCharCode(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
 str_fromCharCode_one_arg(JSContext *cx, HandleValue code, MutableHandleValue rval);
 
+/* String methods exposed so they can be installed in the self-hosting global. */
+
+extern bool
+str_indexOf(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+str_startsWith(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+str_substring(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+str_toLowerCase(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+str_toUpperCase(JSContext *cx, unsigned argc, Value *vp);
+
 } /* namespace js */
 
 extern bool
 js_str_toString(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 js_str_charAt(JSContext *cx, unsigned argc, js::Value *vp);
 
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -233,18 +233,18 @@ WeakMap_has_impl(JSContext *cx, CallArgs
             return true;
         }
     }
 
     args.rval().setBoolean(false);
     return true;
 }
 
-static bool
-WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
 }
 
 MOZ_ALWAYS_INLINE bool
 WeakMap_clear_impl(JSContext *cx, CallArgs args)
 {
@@ -254,18 +254,18 @@ WeakMap_clear_impl(JSContext *cx, CallAr
     // used by the Cycle Collector.
     if (ObjectValueMap *map = args.thisv().toObject().as<WeakMapObject>().getMap())
         map->clear();
 
     args.rval().setUndefined();
     return true;
 }
 
-static bool
-WeakMap_clear(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::WeakMap_clear(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsWeakMap, WeakMap_clear_impl>(cx, args);
 }
 
 MOZ_ALWAYS_INLINE bool
 WeakMap_get_impl(JSContext *cx, CallArgs args)
 {
@@ -286,18 +286,18 @@ WeakMap_get_impl(JSContext *cx, CallArgs
             return true;
         }
     }
 
     args.rval().set((args.length() > 1) ? args[1] : UndefinedValue());
     return true;
 }
 
-static bool
-WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
 }
 
 MOZ_ALWAYS_INLINE bool
 WeakMap_delete_impl(JSContext *cx, CallArgs args)
 {
@@ -319,18 +319,18 @@ WeakMap_delete_impl(JSContext *cx, CallA
             return true;
         }
     }
 
     args.rval().setBoolean(false);
     return true;
 }
 
-static bool
-WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
 }
 
 static bool
 TryPreserveReflector(JSContext *cx, HandleObject obj)
 {
@@ -428,18 +428,18 @@ WeakMap_set_impl(JSContext *cx, CallArgs
     Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
 
     if (!SetWeakMapEntryInternal(cx, map, key, value))
         return false;
     args.rval().set(args.thisv());
     return true;
 }
 
-static bool
-WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
+bool
+js::WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
 }
 
 JS_FRIEND_API(bool)
 JS_NondeterministicGetWeakMapKeys(JSContext *cx, HandleObject objArg, MutableHandleObject ret)
 {
@@ -565,18 +565,18 @@ static const JSFunctionSpec weak_map_met
     JS_FN("has",    WeakMap_has, 1, 0),
     JS_FN("get",    WeakMap_get, 2, 0),
     JS_FN("delete", WeakMap_delete, 1, 0),
     JS_FN("set",    WeakMap_set, 2, 0),
     JS_FN("clear",  WeakMap_clear, 0, 0),
     JS_FS_END
 };
 
-JSObject *
-js_InitWeakMapClass(JSContext *cx, HandleObject obj)
+static JSObject *
+InitWeakMapClass(JSContext *cx, HandleObject obj, bool defineMembers)
 {
     JS_ASSERT(obj->isNative());
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     RootedObject weakMapProto(cx, global->createBlankPrototype(cx, &WeakMapObject::class_));
     if (!weakMapProto)
         return nullptr;
@@ -584,16 +584,30 @@ js_InitWeakMapClass(JSContext *cx, Handl
     RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
                                                       cx->names().WeakMap, 1));
     if (!ctor)
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, ctor, weakMapProto))
         return nullptr;
 
-    if (!DefinePropertiesAndFunctions(cx, weakMapProto, nullptr, weak_map_methods))
-        return nullptr;
+    if (defineMembers) {
+        if (!DefinePropertiesAndFunctions(cx, weakMapProto, nullptr, weak_map_methods))
+            return nullptr;
+    }
 
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, weakMapProto))
         return nullptr;
     return weakMapProto;
 }
 
+JSObject *
+js_InitWeakMapClass(JSContext *cx, HandleObject obj)
+{
+    return InitWeakMapClass(cx, obj, true);
+}
+
+JSObject *
+js::InitBareWeakMapCtor(JSContext *cx, HandleObject obj)
+{
+    return InitWeakMapClass(cx, obj, false);
+}
+
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -274,14 +274,34 @@ protected:
             JS_ASSERT(!gc::IsAboutToBeFinalized(&k));
             JS_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
             JS_ASSERT(k == r.front().key());
         }
 #endif
     }
 };
 
+/* WeakMap methods exposed so they can be installed in the self-hosting global. */
+
+extern JSObject *
+InitBareWeakMapCtor(JSContext *cx, js::HandleObject obj);
+
+extern bool
+WeakMap_has(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+WeakMap_get(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+WeakMap_set(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+WeakMap_delete(JSContext *cx, unsigned argc, Value *vp);
+
+extern bool
+WeakMap_clear(JSContext *cx, unsigned argc, Value *vp);
+
 } /* namespace js */
 
 extern JSObject *
 js_InitWeakMapClass(JSContext *cx, js::HandleObject obj);
 
 #endif /* jsweakmap_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -279,16 +279,67 @@ GlobalObject::initStandardClasses(JSCont
 
     for (size_t k = 0; k < JSProto_LIMIT; ++k) {
         if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
             return false;
     }
     return true;
 }
 
+/**
+ * Initializes a builtin constructor and its prototype without defining any
+ * properties or functions on it.
+ *
+ * Used in self-hosting to install the few builtin constructors required by
+ * self-hosted builtins.
+ */
+static bool
+InitBareBuiltinCtor(JSContext *cx, Handle<GlobalObject*> global, JSProtoKey protoKey)
+{
+    MOZ_ASSERT(cx->runtime()->isSelfHostingGlobal(global));
+    const Class *clasp = ProtoKeyToClass(protoKey);
+    RootedObject proto(cx);
+    proto = clasp->spec.createPrototype(cx, protoKey);
+    if (!proto)
+        return false;
+
+    RootedObject ctor(cx, clasp->spec.createConstructor(cx, protoKey));
+    if (!ctor)
+        return false;
+
+    return GlobalObject::initBuiltinConstructor(cx, global, protoKey, ctor, proto);
+}
+
+/**
+ * The self-hosting global only gets a small subset of all standard classes.
+ * Even those are only created as bare constructors without any properties
+ * or functions.
+ */
+/* static */ bool
+GlobalObject::initSelfHostingBuiltins(JSContext *cx, Handle<GlobalObject*> global,
+                                      const JSFunctionSpec *builtins)
+{
+    /* Define a top-level property 'undefined' with the undefined value. */
+    if (!JSObject::defineProperty(cx, global, cx->names().undefined, UndefinedHandleValue,
+                                  JS_PropertyStub, JS_StrictPropertyStub,
+                                  JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return false;
+    }
+
+    return InitBareBuiltinCtor(cx, global, JSProto_Array) &&
+           InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
+           InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
+           InitBareBuiltinCtor(cx, global, JSProto_Uint32Array) &&
+           InitBareWeakMapCtor(cx, global) &&
+           initStopIterationClass(cx, global) &&
+           InitSelfHostingCollectionIteratorFunctions(cx, global) &&
+           JS_DefineFunctions(cx, global, builtins);
+}
+
 /* static */ bool
 GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle<GlobalObject*> global)
 {
     HeapSlot &v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
     if (v.isUndefined()) {
         /*
          * If there are callbacks, make sure that the CSP callback is installed
          * and that it permits runtime code generation, then cache the result.
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -606,31 +606,34 @@ class GlobalObject : public JSObject
     static bool getOrCreateEval(JSContext *cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(Value val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
+    static bool initStopIterationClass(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext *cx, Handle<GlobalObject*> global);
     static bool initCollatorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initDateTimeFormatProto(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/TypedObject.cpp
     static bool initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global);
 
     static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
+    static bool initSelfHostingBuiltins(JSContext *cx, Handle<GlobalObject*> global,
+                                        const JSFunctionSpec *builtins);
 
     typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;
 
     /*
      * The collection of Debugger objects debugging this global. If this global
      * is not a debuggee, this returns either nullptr or an empty vector.
      */
     DebuggerVector *getDebuggers();
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -3,19 +3,21 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SelfHosting.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
+#include "jsdate.h"
 #include "jsfriendapi.h"
 #include "jshashutil.h"
 #include "jsobj.h"
+#include "jsweakmap.h"
 #include "jswrapper.h"
 #include "selfhosted.out.h"
 
 #include "builtin/Intl.h"
 #include "builtin/Object.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
@@ -798,17 +800,82 @@ js::intrinsic_IsConstructing(JSContext *
     MOZ_ASSERT(args.length() == 0);
 
     ScriptFrameIter iter(cx);
     bool isConstructing = iter.isConstructing();
     args.rval().setBoolean(isConstructing);
     return true;
 }
 
+// The self-hosting global isn't initialized with the normal set of builtins.
+// Instead, individual C++-implemented functions that're required by
+// self-hosted code are defined as global functions. Accessing these
+// functions via a content compartment's builtins would be unsafe, because
+// content script might have changed the builtins' prototypes' members.
+// Installing the whole set of builtins in the self-hosting compartment, OTOH,
+// would be wasteful: it increases memory usage and initialization time for
+// self-hosting compartment.
+//
+// Additionally, a set of C++-implemented helper functions is defined on the
+// self-hosting global.
 static const JSFunctionSpec intrinsic_functions[] = {
+    JS_FN("std_Array_join",                      array_join,                   1,0),
+    JS_FN("std_Array_push",                      array_push,                   1,0),
+    JS_FN("std_Array_pop",                       array_pop,                    0,0),
+    JS_FN("std_Array_shift",                     array_shift,                  0,0),
+    JS_FN("std_Array_unshift",                   array_unshift,                1,0),
+    JS_FN("std_Array_slice",                     array_slice,                  2,0),
+    JS_FN("std_Array_sort",                      array_sort,                   1,0),
+
+    JS_FN("std_Date_now",                        date_now,                     0,0),
+    JS_FN("std_Date_valueOf",                    date_valueOf,                 0,0),
+
+    JS_FN("std_Function_bind",                   fun_bind,                     1,0),
+    JS_FN("std_Function_apply",                  js_fun_apply,                 1,0),
+
+    JS_FN("std_Math_floor",                      math_floor,                   1,0),
+    JS_FN("std_Math_max",                        math_max,                     2,0),
+    JS_FN("std_Math_min",                        math_min,                     2,0),
+    JS_FN("std_Math_abs",                        math_abs,                     1,0),
+    JS_FN("std_Math_imul",                       math_imul,                    2,0),
+    JS_FN("std_Math_log2",                       math_log2,                    1,0),
+
+    JS_FN("std_Map_has",                         MapObject::has,               1,0),
+    JS_FN("std_Map_iterator",                    MapObject::entries,           0,0),
+
+    JS_FN("std_Number_valueOf",                  js_num_valueOf,               0,0),
+
+    JS_FN("std_Object_create",                   obj_create,                   2,0),
+    JS_FN("std_Object_getPrototypeOf",           obj_getPrototypeOf,           1,0),
+    JS_FN("std_Object_getOwnPropertyNames",      obj_getOwnPropertyNames,      1,0),
+    JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
+    JS_FN("std_Object_hasOwnProperty",           obj_hasOwnProperty,           2,0),
+
+    JS_FN("std_Set_has",                         SetObject::has,               1,0),
+    JS_FN("std_Set_iterator",                    SetObject::values,            0,0),
+
+    JS_FN("std_String_fromCharCode",             str_fromCharCode,             1,0),
+    JS_FN("std_String_charCodeAt",               js_str_charCodeAt,            1,0),
+    JS_FN("std_String_indexOf",                  str_indexOf,                  1,0),
+    JS_FN("std_String_lastIndexOf",              str_lastIndexOf,              1,0),
+    JS_FN("std_String_match",                    str_match,                    1,0),
+    JS_FN("std_String_replace",                  str_replace,                  2,0),
+    JS_FN("std_String_split",                    str_split,                    2,0),
+    JS_FN("std_String_startsWith",               str_startsWith,               2,0),
+    JS_FN("std_String_substring",                str_substring,                2,0),
+    JS_FN("std_String_toLowerCase",              str_toLowerCase,              0,0),
+    JS_FN("std_String_toUpperCase",              str_toUpperCase,              0,0),
+
+    JS_FN("std_WeakMap_has",                     WeakMap_has,                  1,0),
+    JS_FN("std_WeakMap_get",                     WeakMap_get,                  2,0),
+    JS_FN("std_WeakMap_set",                     WeakMap_set,                  2,0),
+    JS_FN("std_WeakMap_delete",                  WeakMap_delete,               1,0),
+    JS_FN("std_WeakMap_clear",                   WeakMap_clear,                0,0),
+
+    // Helper funtions after this point.
     JS_FN("ToObject",                intrinsic_ToObject,                1,0),
     JS_FN("IsObject",                intrinsic_IsObject,                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("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
@@ -1008,25 +1075,18 @@ JSRuntime::initSelfHosting(JSContext *cx
     if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class,
                                                   nullptr, JS::DontFireOnNewGlobalHook,
                                                   compartmentOptions)))
         return false;
     JSAutoCompartment ac(cx, selfHostingGlobal_);
     Rooted<GlobalObject*> shg(cx, &selfHostingGlobal_->as<GlobalObject>());
     selfHostingGlobal_->compartment()->isSelfHosting = true;
     selfHostingGlobal_->compartment()->isSystem = true;
-    /*
-     * During initialization of standard classes for the self-hosting global,
-     * all self-hosted functions are ignored. Thus, we don't create cyclic
-     * dependencies in the order of initialization.
-     */
-    if (!GlobalObject::initStandardClasses(cx, shg))
-        return false;
 
-    if (!JS_DefineFunctions(cx, shg, intrinsic_functions))
+    if (!GlobalObject::initSelfHostingBuiltins(cx, shg, intrinsic_functions))
         return false;
 
     JS_FireOnNewGlobalObject(cx, shg);
 
     CompileOptions options(cx);
     FillSelfHostingCompileOptions(options);
 
     /*
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -971,17 +971,17 @@ bool
 JSStructuredCloneWriter::traverseMap(HandleObject obj)
 {
     AutoValueVector newEntries(context());
     {
         // If there is no wrapper, the compartment munging is a no-op.
         RootedObject unwrapped(context(), CheckedUnwrap(obj));
         MOZ_ASSERT(unwrapped);
         JSAutoCompartment ac(context(), unwrapped);
-        if (!MapObject::entries(context(), unwrapped, &newEntries))
+        if (!MapObject::getKeysAndValuesInterleaved(context(), unwrapped, &newEntries))
             return false;
     }
     if (!context()->compartment()->wrap(context(), newEntries))
         return false;
 
     for (size_t i = newEntries.length(); i > 0; --i) {
         if (!entries.append(newEntries[i - 1]))
             return false;