Backed out 16 changesets (bug 1595745) for spidermonkey bustages on testWasm.cpp. CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Fri, 15 Nov 2019 18:17:31 +0200
changeset 502212 ec8cad6891215a6db48cf2bc927ef6c77838035f
parent 502211 bc106b1c9d292c8c8aa72415e18610a1e4a4ef9f
child 502213 8a8024c8caf0674a3c9509323e2a587e53c416be
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1595745
milestone72.0a1
backs out3b5c6aaf5a182cb13691f0917af88da2a0f4df8e
8e8c4124f21990d54d48215611651e6dd0cb3a98
de5c2cca64c6114d1a681b7f6d42b0d6e80e3b47
b11677f10f9d42f360852504ebedd4a3c03a1b32
2ce96c6187c2ad834605f4ea6cfb17688c0ebadd
e089ebe699d2ef3729774765db5c47a58995435f
a514661afdda948c4fb0917375534f06a82ff5b0
29ec5ccb4adf6d914edf9fb7dfaf9f362a902455
3b0e242d762a12cdcb16a0e9f2ac60ce3af21833
fef5c8d5cb909dd147da181f45851603cddeb045
68c5cdced300d92c3102c2fdddab32459e14e95d
d5f5e9091fb049e19001b408e9c784178348fd5b
6c917c2ca4a7b1799ea159d5cc0f88ec85e11148
338ad438e066e879702224ba34317d3956bb2519
61d25028669b165048be8775429f79b1b136e13e
f082e5173ed480d3461749eff791207458c72b5e
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
Backed out 16 changesets (bug 1595745) for spidermonkey bustages on testWasm.cpp. CLOSED TREE Backed out changeset 3b5c6aaf5a18 (bug 1595745) Backed out changeset 8e8c4124f219 (bug 1595745) Backed out changeset de5c2cca64c6 (bug 1595745) Backed out changeset b11677f10f9d (bug 1595745) Backed out changeset 2ce96c6187c2 (bug 1595745) Backed out changeset e089ebe699d2 (bug 1595745) Backed out changeset a514661afdda (bug 1595745) Backed out changeset 29ec5ccb4adf (bug 1595745) Backed out changeset 3b0e242d762a (bug 1595745) Backed out changeset fef5c8d5cb90 (bug 1595745) Backed out changeset 68c5cdced300 (bug 1595745) Backed out changeset d5f5e9091fb0 (bug 1595745) Backed out changeset 6c917c2ca4a7 (bug 1595745) Backed out changeset 338ad438e066 (bug 1595745) Backed out changeset 61d25028669b (bug 1595745) Backed out changeset f082e5173ed4 (bug 1595745)
js/public/ProtoKey.h
js/public/Proxy.h
js/src/builtin/AtomicsObject.cpp
js/src/builtin/AtomicsObject.h
js/src/builtin/Boolean.cpp
js/src/builtin/Boolean.h
js/src/builtin/JSON.cpp
js/src/builtin/JSON.h
js/src/builtin/Reflect.cpp
js/src/builtin/Reflect.h
js/src/builtin/String.cpp
js/src/builtin/Symbol.cpp
js/src/builtin/Symbol.h
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/builtin/intl/IntlObject.cpp
js/src/builtin/intl/IntlObject.h
js/src/jit-test/tests/wasm/globals.js
js/src/jsapi.cpp
js/src/jsexn.h
js/src/jsmath.cpp
js/src/jsmath.h
js/src/jsnum.cpp
js/src/jspubtd.h
js/src/proxy/Proxy.cpp
js/src/proxy/Proxy.h
js/src/vm/AsyncFunction.cpp
js/src/vm/AsyncFunction.h
js/src/vm/AsyncIteration.cpp
js/src/vm/AsyncIteration.h
js/src/vm/BooleanObject.h
js/src/vm/GeneratorObject.cpp
js/src/vm/GeneratorObject.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/JSAtom.cpp
js/src/vm/JSAtom.h
js/src/vm/JSObject.cpp
js/src/vm/JSObject.h
js/src/vm/NumberObject.h
js/src/vm/Runtime.h
js/src/vm/StringObject.h
js/src/wasm/WasmJS.cpp
js/src/wasm/WasmJS.h
--- a/js/public/ProtoKey.h
+++ b/js/public/ProtoKey.h
@@ -5,18 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ProtoKey_h
 #define js_ProtoKey_h
 
 /* A higher-order macro for enumerating all JSProtoKey values. */
 /*
  * Consumers define macros as follows:
- * MACRO(name, clasp)
+ * MACRO(name, init, clasp)
  *   name:    The canonical name of the class.
+ *   init:    Initialization function. These are |extern "C";|, and clients
+ *            should use |extern "C" {}| as appropriate when using this macro.
  *   clasp:   The JSClass for this object, or "dummy" if it doesn't exist.
  *
  *
  * Consumers wishing to iterate over all the JSProtoKey values, can use
  * JS_FOR_EACH_PROTOTYPE. However, there are certain values that don't
  * correspond to real constructors, like Null or constructors that are disabled
  * via preprocessor directives. We still need to include these in the JSProtoKey
  * list in order to maintain binary XDR compatibility, but we need to provide a
@@ -41,97 +43,105 @@
 #endif
 
 #ifdef ENABLE_TYPED_OBJECTS
 #  define IF_TYPEDOBJ(REAL, IMAGINARY) REAL
 #else
 #  define IF_TYPEDOBJ(REAL, IMAGINARY) IMAGINARY
 #endif
 
-#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_BDATA)      \
-  IMAGINARY(Null, dummy)                                                      \
-  REAL(Object, OCLASP(Plain))                                                 \
-  REAL(Function, &JSFunction::class_)                                         \
-  REAL(Array, OCLASP(Array))                                                  \
-  REAL(Boolean, OCLASP(Boolean))                                              \
-  REAL(JSON, CLASP(JSON))                                                     \
-  REAL(Date, OCLASP(Date))                                                    \
-  REAL(Math, CLASP(Math))                                                     \
-  REAL(Number, OCLASP(Number))                                                \
-  REAL(String, OCLASP(String))                                                \
-  REAL(RegExp, OCLASP(RegExp))                                                \
-  REAL(Error, ERROR_CLASP(JSEXN_ERR))                                         \
-  REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR))                         \
-  REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR))                                 \
-  REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR))                               \
-  REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR))                       \
-  REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR))                             \
-  REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR))                                 \
-  REAL(URIError, ERROR_CLASP(JSEXN_URIERR))                                   \
-  REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN))                 \
-  REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR))                     \
-  REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR))                           \
-  REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR))                     \
-  REAL(ArrayBuffer, OCLASP(ArrayBuffer))                                      \
-  REAL(Int8Array, TYPED_ARRAY_CLASP(Int8))                                    \
-  REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8))                                  \
-  REAL(Int16Array, TYPED_ARRAY_CLASP(Int16))                                  \
-  REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16))                                \
-  REAL(Int32Array, TYPED_ARRAY_CLASP(Int32))                                  \
-  REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32))                                \
-  REAL(Float32Array, TYPED_ARRAY_CLASP(Float32))                              \
-  REAL(Float64Array, TYPED_ARRAY_CLASP(Float64))                              \
-  REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped))                    \
-  REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64))                            \
-  REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64))                          \
-  REAL(BigInt, OCLASP(BigInt))                                                \
-  REAL(Proxy, CLASP(Proxy))                                                   \
-  REAL(WeakMap, OCLASP(WeakMap))                                              \
-  REAL(Map, OCLASP(Map))                                                      \
-  REAL(Set, OCLASP(Set))                                                      \
-  REAL(DataView, OCLASP(DataView))                                            \
-  REAL(Symbol, OCLASP(Symbol))                                                \
-  REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer))                          \
-  REAL_IF_INTL(Intl, CLASP(Intl))                                             \
-  REAL_IF_INTL(Collator, OCLASP(Collator))                                    \
-  REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat))                        \
-  REAL_IF_INTL(ListFormat, OCLASP(ListFormat))                                \
-  REAL_IF_INTL(Locale, OCLASP(Locale))                                        \
-  REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat))                            \
-  REAL_IF_INTL(PluralRules, OCLASP(PluralRules))                              \
-  REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat))                \
-  REAL_IF_BDATA(TypedObject, OCLASP(TypedObjectModule))                       \
-  REAL(Reflect, CLASP(Reflect))                                               \
-  REAL(WeakSet, OCLASP(WeakSet))                                              \
-  REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass)     \
-  REAL(Atomics, OCLASP(Atomics))                                              \
-  REAL(SavedFrame, &js::SavedFrame::class_)                                   \
-  REAL(Promise, OCLASP(Promise))                                              \
-  REAL(AsyncFunction, CLASP(AsyncFunction))                                   \
-  REAL(GeneratorFunction, CLASP(GeneratorFunction))                           \
-  REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction))                 \
-  REAL(ReadableStream, &js::ReadableStream::class_)                           \
-  REAL(ReadableStreamDefaultReader, &js::ReadableStreamDefaultReader::class_) \
-  REAL(ReadableStreamDefaultController,                                       \
-       &js::ReadableStreamDefaultController::class_)                          \
-  REAL(ReadableByteStreamController,                                          \
-       &js::ReadableByteStreamController::class_)                             \
-  REAL(WritableStream, &js::WritableStream::class_)                           \
-  REAL(WritableStreamDefaultController,                                       \
-       &js::WritableStreamDefaultController::class_)                          \
-  REAL(WritableStreamDefaultWriter, &js::WritableStreamDefaultWriter::class_) \
-  REAL(ByteLengthQueuingStrategy, &js::ByteLengthQueuingStrategy::class_)     \
-  REAL(CountQueuingStrategy, &js::CountQueuingStrategy::class_)               \
-  REAL(WebAssembly, CLASP(WebAssembly))                                       \
-  REAL(WasmModule, OCLASP(WasmModule))                                        \
-  REAL(WasmInstance, OCLASP(WasmInstance))                                    \
-  REAL(WasmMemory, OCLASP(WasmMemory))                                        \
-  REAL(WasmTable, OCLASP(WasmTable))                                          \
-  REAL(WasmGlobal, OCLASP(WasmGlobal))                                        \
-  REAL(FinalizationGroup, OCLASP(FinalizationGroup))
+#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_BDATA)     \
+  IMAGINARY(Null, InitNullClass, dummy)                                      \
+  REAL(Object, InitViaClassSpec, OCLASP(Plain))                              \
+  REAL(Function, InitViaClassSpec, &JSFunction::class_)                      \
+  REAL(Array, InitViaClassSpec, OCLASP(Array))                               \
+  REAL(Boolean, InitBooleanClass, OCLASP(Boolean))                           \
+  REAL(JSON, InitJSONClass, CLASP(JSON))                                     \
+  REAL(Date, InitViaClassSpec, OCLASP(Date))                                 \
+  REAL(Math, InitMathClass, CLASP(Math))                                     \
+  REAL(Number, InitNumberClass, OCLASP(Number))                              \
+  REAL(String, InitStringClass, OCLASP(String))                              \
+  REAL(RegExp, InitViaClassSpec, OCLASP(RegExp))                             \
+  REAL(Error, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR))                      \
+  REAL(InternalError, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR))      \
+  REAL(EvalError, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR))              \
+  REAL(RangeError, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR))            \
+  REAL(ReferenceError, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR))    \
+  REAL(SyntaxError, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR))          \
+  REAL(TypeError, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR))              \
+  REAL(URIError, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR))                \
+  REAL(DebuggeeWouldRun, InitViaClassSpec,                                   \
+       ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN))                                  \
+  REAL(CompileError, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMCOMPILEERROR))  \
+  REAL(LinkError, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMLINKERROR))        \
+  REAL(RuntimeError, InitViaClassSpec, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR))  \
+  REAL(ArrayBuffer, InitViaClassSpec, OCLASP(ArrayBuffer))                   \
+  REAL(Int8Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8))                 \
+  REAL(Uint8Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8))               \
+  REAL(Int16Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int16))               \
+  REAL(Uint16Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint16))             \
+  REAL(Int32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Int32))               \
+  REAL(Uint32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint32))             \
+  REAL(Float32Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float32))           \
+  REAL(Float64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(Float64))           \
+  REAL(Uint8ClampedArray, InitViaClassSpec, TYPED_ARRAY_CLASP(Uint8Clamped)) \
+  REAL(BigInt64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(BigInt64))         \
+  REAL(BigUint64Array, InitViaClassSpec, TYPED_ARRAY_CLASP(BigUint64))       \
+  REAL(BigInt, InitViaClassSpec, OCLASP(BigInt))                             \
+  REAL(Proxy, InitProxyClass, &js::ProxyClass)                               \
+  REAL(WeakMap, InitViaClassSpec, OCLASP(WeakMap))                           \
+  REAL(Map, InitViaClassSpec, OCLASP(Map))                                   \
+  REAL(Set, InitViaClassSpec, OCLASP(Set))                                   \
+  REAL(DataView, InitViaClassSpec, OCLASP(DataView))                         \
+  REAL(Symbol, InitSymbolClass, OCLASP(Symbol))                              \
+  REAL(SharedArrayBuffer, InitViaClassSpec, OCLASP(SharedArrayBuffer))       \
+  REAL_IF_INTL(Intl, InitIntlClass, CLASP(Intl))                             \
+  REAL_IF_INTL(Collator, InitViaClassSpec, OCLASP(Collator))                 \
+  REAL_IF_INTL(DateTimeFormat, InitViaClassSpec, OCLASP(DateTimeFormat))     \
+  REAL_IF_INTL(Locale, InitViaClassSpec, OCLASP(Locale))                     \
+  REAL_IF_INTL(ListFormat, InitViaClassSpec, OCLASP(ListFormat))             \
+  REAL_IF_INTL(NumberFormat, InitViaClassSpec, OCLASP(NumberFormat))         \
+  REAL_IF_INTL(PluralRules, InitViaClassSpec, OCLASP(PluralRules))           \
+  REAL_IF_INTL(RelativeTimeFormat, InitViaClassSpec,                         \
+               OCLASP(RelativeTimeFormat))                                   \
+  REAL_IF_BDATA(TypedObject, InitTypedObjectModuleObject,                    \
+                OCLASP(TypedObjectModule))                                   \
+  REAL(Reflect, InitReflect, nullptr)                                        \
+  REAL(WeakSet, InitViaClassSpec, OCLASP(WeakSet))                           \
+  REAL(TypedArray, InitViaClassSpec,                                         \
+       &js::TypedArrayObject::sharedTypedArrayPrototypeClass)                \
+  REAL(Atomics, InitAtomicsClass, OCLASP(Atomics))                           \
+  REAL(SavedFrame, InitViaClassSpec, &js::SavedFrame::class_)                \
+  REAL(Promise, InitViaClassSpec, OCLASP(Promise))                           \
+  REAL(AsyncFunction, InitAsyncFunction, nullptr)                            \
+  REAL(GeneratorFunction, InitGeneratorFunction, nullptr)                    \
+  REAL(AsyncGeneratorFunction, InitAsyncGeneratorFunction, nullptr)          \
+  REAL(ReadableStream, InitViaClassSpec, &js::ReadableStream::class_)        \
+  REAL(ReadableStreamDefaultReader, InitViaClassSpec,                        \
+       &js::ReadableStreamDefaultReader::class_)                             \
+  REAL(ReadableStreamDefaultController, InitViaClassSpec,                    \
+       &js::ReadableStreamDefaultController::class_)                         \
+  REAL(ReadableByteStreamController, InitViaClassSpec,                       \
+       &js::ReadableByteStreamController::class_)                            \
+  REAL(WritableStream, InitViaClassSpec, &js::WritableStream::class_)        \
+  REAL(WritableStreamDefaultController, InitViaClassSpec,                    \
+       &js::WritableStreamDefaultController::class_)                         \
+  REAL(WritableStreamDefaultWriter, InitViaClassSpec,                        \
+       &js::WritableStreamDefaultWriter::class_)                             \
+  REAL(ByteLengthQueuingStrategy, InitViaClassSpec,                          \
+       &js::ByteLengthQueuingStrategy::class_)                               \
+  REAL(CountQueuingStrategy, InitViaClassSpec,                               \
+       &js::CountQueuingStrategy::class_)                                    \
+  REAL(WebAssembly, InitWebAssemblyClass, CLASP(WebAssembly))                \
+  IMAGINARY(WasmModule, dummy, dummy)                                        \
+  IMAGINARY(WasmInstance, dummy, dummy)                                      \
+  IMAGINARY(WasmMemory, dummy, dummy)                                        \
+  IMAGINARY(WasmTable, dummy, dummy)                                         \
+  IMAGINARY(WasmGlobal, dummy, dummy)                                        \
+  REAL(FinalizationGroup, InitViaClassSpec, OCLASP(FinalizationGroup))
 
 #define JS_FOR_PROTOTYPES(REAL, IMAGINARY)                      \
   JS_FOR_PROTOTYPES_(REAL, IMAGINARY, IF_INTL(REAL, IMAGINARY), \
                      IF_TYPEDOBJ(REAL, IMAGINARY))
 
 #define JS_FOR_EACH_PROTOTYPE(MACRO) JS_FOR_PROTOTYPES(MACRO, MACRO)
 
 #endif /* js_ProtoKey_h */
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -720,28 +720,25 @@ constexpr unsigned CheckProxyFlags() {
   // always have finalizers, and whether they can be nursery allocated is
   // controlled by the canNurseryAllocate() method on the proxy handler.
   static_assert(!(Flags & JSCLASS_SKIP_NURSERY_FINALIZE),
                 "Proxies must not use JSCLASS_SKIP_NURSERY_FINALIZE; use "
                 "the canNurseryAllocate() proxy handler method instead.");
   return Flags;
 }
 
-#define PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, classSpec)            \
+#define PROXY_CLASS_DEF(name, flags)                                       \
   {                                                                        \
     name,                                                                  \
         JSClass::NON_NATIVE | JSCLASS_IS_PROXY |                           \
             JSCLASS_DELAY_METADATA_BUILDER | js::CheckProxyFlags<flags>(), \
-        &js::ProxyClassOps, classSpec, &js::ProxyClassExtension,           \
+        &js::ProxyClassOps, JS_NULL_CLASS_SPEC, &js::ProxyClassExtension,  \
         &js::ProxyObjectOps                                                \
   }
 
-#define PROXY_CLASS_DEF(name, flags) \
-  PROXY_CLASS_DEF_WITH_CLASS_SPEC(name, flags, JS_NULL_CLASS_SPEC)
-
 // Converts a proxy into a DeadObjectProxy that will throw exceptions on all
 // access. This will run the proxy's finalizer to perform clean-up before the
 // conversion happens.
 JS_FRIEND_API void NukeNonCCWProxy(JSContext* cx, HandleObject proxy);
 
 // This is a variant of js::NukeNonCCWProxy() for CCWs. It should only be called
 // on CCWs that have been removed from CCW tables.
 JS_FRIEND_API void NukeRemovedCrossCompartmentWrapper(JSContext* cx,
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -67,16 +67,19 @@
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmInstance.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
+const JSClass AtomicsObject::class_ = {
+    "Atomics", JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics)};
+
 static bool ReportBadArrayType(JSContext* cx) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             JSMSG_ATOMICS_BAD_ARRAY);
   return false;
 }
 
 static bool ReportOutOfRange(JSContext* cx) {
   // Use JSMSG_BAD_INDEX here, it is what ToIndex uses for some cases that it
@@ -949,30 +952,47 @@ const JSFunctionSpec AtomicsMethods[] = 
     JS_INLINABLE_FN("or", atomics_or, 3, 0, AtomicsOr),
     JS_INLINABLE_FN("xor", atomics_xor, 3, 0, AtomicsXor),
     JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1, 0, AtomicsIsLockFree),
     JS_FN("wait", atomics_wait, 4, 0),
     JS_FN("notify", atomics_notify, 3, 0),
     JS_FN("wake", atomics_notify, 3, 0),  // Legacy name
     JS_FS_END};
 
-static const JSPropertySpec AtomicsProperties[] = {
-    JS_STRING_SYM_PS(toStringTag, "Atomics", JSPROP_READONLY), JS_PS_END};
+JSObject* AtomicsObject::initClass(JSContext* cx,
+                                   Handle<GlobalObject*> global) {
+  // Create Atomics Object.
+  RootedObject objProto(cx,
+                        GlobalObject::getOrCreateObjectPrototype(cx, global));
+  if (!objProto) {
+    return nullptr;
+  }
+  RootedObject Atomics(cx, NewObjectWithGivenProto(cx, &AtomicsObject::class_,
+                                                   objProto, SingletonObject));
+  if (!Atomics) {
+    return nullptr;
+  }
 
-static JSObject* CreateAtomicsObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
-  RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
-  if (!proto) {
+  if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods)) {
     return nullptr;
   }
-  return NewObjectWithGivenProto(cx, &AtomicsObject::class_, proto,
-                                 SingletonObject);
+  if (!DefineToStringTag(cx, Atomics, cx->names().Atomics)) {
+    return nullptr;
+  }
+
+  RootedValue AtomicsValue(cx, ObjectValue(*Atomics));
+
+  // Everything is set up, install Atomics on the global object.
+  if (!DefineDataProperty(cx, global, cx->names().Atomics, AtomicsValue,
+                          JSPROP_RESOLVING)) {
+    return nullptr;
+  }
+
+  global->setConstructor(JSProto_Atomics, AtomicsValue);
+  return Atomics;
 }
 
-static const ClassSpec AtomicsClassSpec = {CreateAtomicsObject, nullptr,
-                                           AtomicsMethods, AtomicsProperties};
-
-const JSClass AtomicsObject::class_ = {
-    "Atomics", JSCLASS_HAS_CACHED_PROTO(JSProto_Atomics), JS_NULL_CLASS_OPS,
-    &AtomicsClassSpec};
+JSObject* js::InitAtomicsClass(JSContext* cx, Handle<GlobalObject*> global) {
+  return AtomicsObject::initClass(cx, global);
+}
 
 #undef CXX11_ATOMICS
 #undef GNU_ATOMICS
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -13,21 +13,25 @@
 #include "threading/ConditionVariable.h"
 #include "threading/ProtectedData.h"  // js::ThreadData
 #include "vm/JSObject.h"
 #include "vm/MutexIDs.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
+class GlobalObject;
 class SharedArrayRawBuffer;
 
 class AtomicsObject : public NativeObject {
  public:
   static const JSClass class_;
+  static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
+  static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc,
+                                    Value* vp);
 };
 
 MOZ_MUST_USE bool atomics_compareExchange(JSContext* cx, unsigned argc,
                                           Value* vp);
 MOZ_MUST_USE bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
 MOZ_MUST_USE bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
 MOZ_MUST_USE bool atomics_store(JSContext* cx, unsigned argc, Value* vp);
 MOZ_MUST_USE bool atomics_add(JSContext* cx, unsigned argc, Value* vp);
@@ -132,16 +136,18 @@ class FutexThread {
   static mozilla::Atomic<js::Mutex*, mozilla::SequentiallyConsistent,
                          mozilla::recordreplay::Behavior::DontPreserve>
       lock_;
 
   // A flag that controls whether waiting is allowed.
   ThreadData<bool> canWait_;
 };
 
+JSObject* InitAtomicsClass(JSContext* cx, Handle<GlobalObject*> global);
+
 // Go to sleep if the int32_t value at the given address equals `value`.
 MOZ_MUST_USE FutexThread::WaitResult atomics_wait_impl(
     JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
     int32_t value, const mozilla::Maybe<mozilla::TimeDuration>& timeout);
 
 // Go to sleep if the int64_t value at the given address equals `value`.
 MOZ_MUST_USE FutexThread::WaitResult atomics_wait_impl(
     JSContext* cx, SharedArrayRawBuffer* sarb, uint32_t byteOffset,
--- a/js/src/builtin/Boolean.cpp
+++ b/js/src/builtin/Boolean.cpp
@@ -24,18 +24,17 @@
 #include "vm/ProxyObject.h"
 
 #include "vm/BooleanObject-inl.h"
 
 using namespace js;
 
 const JSClass BooleanObject::class_ = {
     "Boolean",
-    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),
-    JS_NULL_CLASS_OPS, &BooleanObject::classSpec_};
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean)};
 
 MOZ_ALWAYS_INLINE bool IsBoolean(HandleValue v) {
   return v.isBoolean() || (v.isObject() && v.toObject().is<BooleanObject>());
 }
 
 MOZ_ALWAYS_INLINE bool bool_toSource_impl(JSContext* cx, const CallArgs& args) {
   HandleValue thisv = args.thisv();
   MOZ_ASSERT(IsBoolean(thisv));
@@ -115,36 +114,49 @@ static bool Boolean(JSContext* cx, unsig
     }
     args.rval().setObject(*obj);
   } else {
     args.rval().setBoolean(b);
   }
   return true;
 }
 
-JSObject* BooleanObject::createPrototype(JSContext* cx, JSProtoKey key) {
-  BooleanObject* booleanProto =
-      GlobalObject::createBlankPrototype<BooleanObject>(cx, cx->global());
+JSObject* js::InitBooleanClass(JSContext* cx, Handle<GlobalObject*> global) {
+  Rooted<BooleanObject*> booleanProto(
+      cx, GlobalObject::createBlankPrototype<BooleanObject>(cx, global));
   if (!booleanProto) {
     return nullptr;
   }
   booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT,
                              BooleanValue(false));
+
+  RootedFunction ctor(cx, GlobalObject::createConstructor(
+                              cx, Boolean, cx->names().Boolean, 1,
+                              gc::AllocKind::FUNCTION, &jit::JitInfo_Boolean));
+  if (!ctor) {
+    return nullptr;
+  }
+
+  if (!LinkConstructorAndPrototype(cx, ctor, booleanProto)) {
+    return nullptr;
+  }
+
+  if (!DefinePropertiesAndFunctions(cx, booleanProto, nullptr,
+                                    boolean_methods)) {
+    return nullptr;
+  }
+
+  if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Boolean, ctor,
+                                            booleanProto)) {
+    return nullptr;
+  }
+
   return booleanProto;
 }
 
-const ClassSpec BooleanObject::classSpec_ = {
-    GenericCreateConstructor<Boolean, 1, gc::AllocKind::FUNCTION,
-                             &jit::JitInfo_Boolean>,
-    BooleanObject::createPrototype,
-    nullptr,
-    nullptr,
-    boolean_methods,
-    nullptr};
-
 JSString* js::BooleanToString(JSContext* cx, bool b) {
   return b ? cx->names().true_ : cx->names().false_;
 }
 
 JS_PUBLIC_API bool js::ToBooleanSlow(HandleValue v) {
   if (v.isString()) {
     return v.toString()->length() != 0;
   }
--- a/js/src/builtin/Boolean.h
+++ b/js/src/builtin/Boolean.h
@@ -6,18 +6,22 @@
 
 #ifndef builtin_Boolean_h
 #define builtin_Boolean_h
 
 /*
  * JS boolean interface.
  */
 
-struct JSContext;
-class JSString;
+#include "NamespaceImports.h"
 
 namespace js {
 
+class GlobalObject;
+
+extern JSObject* InitBooleanClass(JSContext* cx,
+                                  js::Handle<GlobalObject*> global);
+
 extern JSString* BooleanToString(JSContext* cx, bool b);
 
 }  // namespace js
 
 #endif /* builtin_Boolean_h */
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -36,16 +36,19 @@ using namespace js;
 
 using mozilla::CheckedInt;
 using mozilla::IsFinite;
 using mozilla::Maybe;
 using mozilla::RangedPtr;
 
 using JS::AutoStableStringChars;
 
+const JSClass js::JSONClass = {js_JSON_str,
+                               JSCLASS_HAS_CACHED_PROTO(JSProto_JSON)};
+
 /* ES5 15.12.3 Quote.
  * Requires that the destination has enough space allocated for src after
  * escaping (that is, `2 + 6 * (srcEnd - srcBegin)` characters).
  */
 template <typename SrcCharT, typename DstCharT>
 static MOZ_ALWAYS_INLINE RangedPtr<DstCharT> InfallibleQuote(
     RangedPtr<const SrcCharT> srcBegin, RangedPtr<const SrcCharT> srcEnd,
     RangedPtr<DstCharT> dstPtr) {
@@ -1096,26 +1099,35 @@ bool json_stringify(JSContext* cx, unsig
   return true;
 }
 
 static const JSFunctionSpec json_static_methods[] = {
     JS_FN(js_toSource_str, json_toSource, 0, 0),
     JS_FN("parse", json_parse, 2, 0), JS_FN("stringify", json_stringify, 3, 0),
     JS_FS_END};
 
-static const JSPropertySpec json_static_properties[] = {
-    JS_STRING_SYM_PS(toStringTag, "JSON", JSPROP_READONLY), JS_PS_END};
-
-static JSObject* CreateJSONObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
+JSObject* js::InitJSONClass(JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewObjectWithGivenProto(cx, &JSONClass, proto, SingletonObject);
-}
+  RootedObject JSON(
+      cx, NewObjectWithGivenProto(cx, &JSONClass, proto, SingletonObject));
+  if (!JSON) {
+    return nullptr;
+  }
+
+  if (!JS_DefineProperty(cx, global, js_JSON_str, JSON, JSPROP_RESOLVING)) {
+    return nullptr;
+  }
 
-static const ClassSpec JSONClassSpec = {
-    CreateJSONObject, nullptr, json_static_methods, json_static_properties};
+  if (!JS_DefineFunctions(cx, JSON, json_static_methods)) {
+    return nullptr;
+  }
 
-const JSClass js::JSONClass = {js_JSON_str,
-                               JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
-                               JS_NULL_CLASS_OPS, &JSONClassSpec};
+  if (!DefineToStringTag(cx, JSON, cx->names().JSON)) {
+    return nullptr;
+  }
+
+  global->setConstructor(JSProto_JSON, ObjectValue(*JSON));
+
+  return JSON;
+}
--- a/js/src/builtin/JSON.h
+++ b/js/src/builtin/JSON.h
@@ -10,20 +10,23 @@
 #include "mozilla/Range.h"
 
 #include "NamespaceImports.h"
 
 #include "js/RootingAPI.h"
 
 namespace js {
 
+class GlobalObject;
 class StringBuffer;
 
 extern const JSClass JSONClass;
 
+extern JSObject* InitJSONClass(JSContext* cx, Handle<GlobalObject*> obj);
+
 enum class StringifyBehavior { Normal, RestrictedSafe };
 
 /**
  * If maybeSafely is true, Stringify will attempt to assert the API requirements
  * of JS::ToJSONMaybeSafely as it traverses the graph, and will not try to
  * invoke .toJSON on things as it goes.
  */
 extern bool Stringify(JSContext* cx, js::MutableHandleValue vp,
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -189,17 +189,17 @@ static bool Reflect_setPrototypeOf(JSCon
   ObjectOpResult result;
   if (!SetPrototype(cx, obj, proto, result)) {
     return false;
   }
   args.rval().setBoolean(result.reallyOk());
   return true;
 }
 
-static const JSFunctionSpec reflect_methods[] = {
+static const JSFunctionSpec methods[] = {
     JS_SELF_HOSTED_FN("apply", "Reflect_apply", 3, 0),
     JS_SELF_HOSTED_FN("construct", "Reflect_construct", 2, 0),
     JS_SELF_HOSTED_FN("defineProperty", "Reflect_defineProperty", 3, 0),
     JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0),
     JS_SELF_HOSTED_FN("get", "Reflect_get", 2, 0),
     JS_SELF_HOSTED_FN("getOwnPropertyDescriptor",
                       "Reflect_getOwnPropertyDescriptor", 2, 0),
     JS_INLINABLE_FN("getPrototypeOf", Reflect_getPrototypeOf, 1, 0,
@@ -209,22 +209,33 @@ static const JSFunctionSpec reflect_meth
     JS_FN("ownKeys", Reflect_ownKeys, 1, 0),
     JS_FN("preventExtensions", Reflect_preventExtensions, 1, 0),
     JS_FN("set", Reflect_set, 3, 0),
     JS_FN("setPrototypeOf", Reflect_setPrototypeOf, 2, 0),
     JS_FS_END};
 
 /*** Setup ******************************************************************/
 
-static JSObject* CreateReflectObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
+JSObject* js::InitReflect(JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
-}
+
+  RootedObject reflect(
+      cx, NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject));
+  if (!reflect) {
+    return nullptr;
+  }
+  if (!JS_DefineFunctions(cx, reflect, methods)) {
+    return nullptr;
+  }
 
-static const ClassSpec ReflectClassSpec = {CreateReflectObject, nullptr,
-                                           reflect_methods, nullptr};
+  RootedValue value(cx, ObjectValue(*reflect));
+  if (!DefineDataProperty(cx, global, cx->names().Reflect, value,
+                          JSPROP_RESOLVING)) {
+    return nullptr;
+  }
 
-const JSClass js::ReflectClass = {"Reflect", 0, JS_NULL_CLASS_OPS,
-                                  &ReflectClassSpec};
+  global->setConstructor(JSProto_Reflect, value);
+
+  return reflect;
+}
--- a/js/src/builtin/Reflect.h
+++ b/js/src/builtin/Reflect.h
@@ -6,17 +6,19 @@
 
 #ifndef builtin_Reflect_h
 #define builtin_Reflect_h
 
 #include "vm/JSObject.h"
 
 namespace js {
 
-extern const JSClass ReflectClass;
+class GlobalObject;
+
+extern JSObject* InitReflect(JSContext* cx, js::Handle<GlobalObject*> global);
 
 }  // namespace js
 
 namespace js {
 
 extern MOZ_MUST_USE bool Reflect_getPrototypeOf(JSContext* cx, unsigned argc,
                                                 Value* vp);
 
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -448,17 +448,17 @@ static const JSClassOps StringObjectClas
     nullptr,                /* delProperty */
     str_enumerate, nullptr, /* newEnumerate */
     str_resolve,   str_mayResolve};
 
 const JSClass StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
         JSCLASS_HAS_CACHED_PROTO(JSProto_String),
-    &StringObjectClassOps, &StringObject::classSpec_};
+    &StringObjectClassOps};
 
 /*
  * Perform the initial |RequireObjectCoercible(thisv)| and |ToString(thisv)|
  * from nearly all String.prototype.* functions.
  */
 static MOZ_ALWAYS_INLINE JSString* ToStringForStringFunction(
     JSContext* cx, HandleValue thisv) {
   if (!CheckRecursionLimit(cx)) {
@@ -3937,71 +3937,78 @@ static const JSFunctionSpec string_stati
 Shape* StringObject::assignInitialShape(JSContext* cx,
                                         Handle<StringObject*> obj) {
   MOZ_ASSERT(obj->empty());
 
   return NativeObject::addDataProperty(cx, obj, cx->names().length, LENGTH_SLOT,
                                        JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
-JSObject* StringObject::createPrototype(JSContext* cx, JSProtoKey key) {
+JSObject* js::InitStringClass(JSContext* cx, Handle<GlobalObject*> global) {
   Rooted<JSString*> empty(cx, cx->runtime()->emptyString);
   Rooted<StringObject*> proto(
-      cx, GlobalObject::createBlankPrototype<StringObject>(cx, cx->global()));
+      cx, GlobalObject::createBlankPrototype<StringObject>(cx, global));
   if (!proto) {
     return nullptr;
   }
   if (!StringObject::init(cx, proto, empty)) {
     return nullptr;
   }
-  return proto;
-}
-
-static bool StringClassFinish(JSContext* cx, HandleObject ctor,
-                              HandleObject proto) {
-  HandleNativeObject nativeProto = proto.as<NativeObject>();
+
+  /* Now create the String function. */
+  RootedFunction ctor(cx);
+  ctor = GlobalObject::createConstructor(
+      cx, StringConstructor, cx->names().String, 1, gc::AllocKind::FUNCTION,
+      &jit::JitInfo_String);
+  if (!ctor) {
+    return nullptr;
+  }
+
+  if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
+    return nullptr;
+  }
+
+  if (!DefinePropertiesAndFunctions(cx, proto, nullptr, string_methods) ||
+      !DefinePropertiesAndFunctions(cx, ctor, nullptr, string_static_methods)) {
+    return nullptr;
+  }
 
   // Create "trimLeft" as an alias for "trimStart".
   RootedValue trimFn(cx);
   RootedId trimId(cx, NameToId(cx->names().trimStart));
   RootedId trimAliasId(cx, NameToId(cx->names().trimLeft));
-  if (!NativeGetProperty(cx, nativeProto, trimId, &trimFn) ||
-      !NativeDefineDataProperty(cx, nativeProto, trimAliasId, trimFn, 0)) {
-    return false;
+  if (!NativeGetProperty(cx, proto, trimId, &trimFn) ||
+      !NativeDefineDataProperty(cx, proto, trimAliasId, trimFn, 0)) {
+    return nullptr;
   }
 
   // Create "trimRight" as an alias for "trimEnd".
   trimId = NameToId(cx->names().trimEnd);
   trimAliasId = NameToId(cx->names().trimRight);
-  if (!NativeGetProperty(cx, nativeProto, trimId, &trimFn) ||
-      !NativeDefineDataProperty(cx, nativeProto, trimAliasId, trimFn, 0)) {
-    return false;
+  if (!NativeGetProperty(cx, proto, trimId, &trimFn) ||
+      !NativeDefineDataProperty(cx, proto, trimAliasId, trimFn, 0)) {
+    return nullptr;
   }
 
   /*
    * Define escape/unescape, the URI encode/decode functions, and maybe
    * uneval on the global object.
    */
-  if (!JS_DefineFunctions(cx, cx->global(), string_functions)) {
-    return false;
-  }
-
-  return true;
+  if (!JS_DefineFunctions(cx, global, string_functions)) {
+    return nullptr;
+  }
+
+  if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_String, ctor,
+                                            proto)) {
+    return nullptr;
+  }
+
+  return proto;
 }
 
-const ClassSpec StringObject::classSpec_ = {
-    GenericCreateConstructor<StringConstructor, 1, gc::AllocKind::FUNCTION,
-                             &jit::JitInfo_String>,
-    StringObject::createPrototype,
-    string_static_methods,
-    nullptr,
-    string_methods,
-    nullptr,
-    StringClassFinish};
-
 #define ____ false
 
 /*
  * Uri reserved chars + #:
  * - 35: #
  * - 36: $
  * - 38: &
  * - 43: +
--- a/js/src/builtin/Symbol.cpp
+++ b/js/src/builtin/Symbol.cpp
@@ -13,74 +13,88 @@
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 const JSClass SymbolObject::class_ = {
-    "Symbol",
-    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
-        JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
-    JS_NULL_CLASS_OPS, &SymbolObject::classSpec_};
-
-// This uses PlainObject::class_ because: "The Symbol prototype object is an
-// ordinary object. It is not a Symbol instance and does not have a
-// [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
-const JSClass& SymbolObject::protoClass_ = PlainObject::class_;
+    "Symbol", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
+                  JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol)};
 
 SymbolObject* SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol) {
   SymbolObject* obj = NewBuiltinClassInstance<SymbolObject>(cx);
   if (!obj) {
     return nullptr;
   }
   obj->setPrimitiveValue(symbol);
   return obj;
 }
 
 const JSPropertySpec SymbolObject::properties[] = {
-    JS_PSG("description", descriptionGetter, 0),
-    JS_STRING_SYM_PS(toStringTag, "Symbol", JSPROP_READONLY), JS_PS_END};
+    JS_PSG("description", descriptionGetter, 0), JS_PS_END};
 
 const JSFunctionSpec SymbolObject::methods[] = {
     JS_FN(js_toString_str, toString, 0, 0),
     JS_FN(js_valueOf_str, valueOf, 0, 0),
     JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY), JS_FS_END};
 
 const JSFunctionSpec SymbolObject::staticMethods[] = {
     JS_FN("for", for_, 1, 0), JS_FN("keyFor", keyFor, 1, 0), JS_FS_END};
 
-static bool SymbolClassFinish(JSContext* cx, HandleObject ctor,
-                              HandleObject proto) {
-  HandleNativeObject nativeCtor = ctor.as<NativeObject>();
+JSObject* SymbolObject::initClass(JSContext* cx, Handle<GlobalObject*> global,
+                                  bool defineMembers) {
+  // This uses &JSObject::class_ because: "The Symbol prototype object is an
+  // ordinary object. It is not a Symbol instance and does not have a
+  // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3)
+  RootedObject proto(
+      cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
+  if (!proto) {
+    return nullptr;
+  }
 
-  // Define the well-known symbol properties, such as Symbol.iterator.
-  ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
-  RootedValue value(cx);
-  unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
-  WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
-  for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
-    value.setSymbol(wks->get(i));
-    if (!NativeDefineDataProperty(cx, nativeCtor, names[i], value, attrs)) {
-      return false;
+  RootedFunction ctor(cx, GlobalObject::createConstructor(
+                              cx, construct, ClassName(JSProto_Symbol, cx), 0));
+  if (!ctor) {
+    return nullptr;
+  }
+
+  if (defineMembers) {
+    // Define the well-known symbol properties, such as Symbol.iterator.
+    ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames();
+    RootedValue value(cx);
+    unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+    WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
+    for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
+      value.setSymbol(wks->get(i));
+      if (!NativeDefineDataProperty(cx, ctor, names[i], value, attrs)) {
+        return nullptr;
+      }
     }
   }
-  return true;
-}
+
+  if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
+    return nullptr;
+  }
 
-const ClassSpec SymbolObject::classSpec_ = {
-    GenericCreateConstructor<SymbolObject::construct, 0,
-                             gc::AllocKind::FUNCTION>,
-    GenericCreatePrototype<SymbolObject>,
-    staticMethods,
-    nullptr,
-    methods,
-    properties,
-    SymbolClassFinish};
+  if (defineMembers) {
+    if (!DefinePropertiesAndFunctions(cx, proto, properties, methods) ||
+        !DefineToStringTag(cx, proto, cx->names().Symbol) ||
+        !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods)) {
+      return nullptr;
+    }
+  }
+
+  if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor,
+                                            proto)) {
+    return nullptr;
+  }
+  return proto;
+}
 
 // ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2
 bool SymbolObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   // According to a note in the draft standard, "Symbol has ordinary
   // [[Construct]] behaviour but the definition of its @@create method causes
   // `new Symbol` to throw a TypeError exception." We do not support @@create
   // yet, so just throw a TypeError.
   CallArgs args = CallArgsFromVp(argc, vp);
@@ -221,8 +235,16 @@ bool SymbolObject::descriptionGetter_imp
   }
   return true;
 }
 
 bool SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsSymbol, descriptionGetter_impl>(cx, args);
 }
+
+JSObject* js::InitSymbolClass(JSContext* cx, Handle<GlobalObject*> global) {
+  return SymbolObject::initClass(cx, global, true);
+}
+
+JSObject* js::InitBareSymbolCtor(JSContext* cx, Handle<GlobalObject*> global) {
+  return SymbolObject::initClass(cx, global, false);
+}
--- a/js/src/builtin/Symbol.h
+++ b/js/src/builtin/Symbol.h
@@ -7,25 +7,29 @@
 #ifndef builtin_Symbol_h
 #define builtin_Symbol_h
 
 #include "vm/NativeObject.h"
 #include "vm/SymbolType.h"
 
 namespace js {
 
+class GlobalObject;
+
 class SymbolObject : public NativeObject {
   /* Stores this Symbol object's [[PrimitiveValue]]. */
   static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
  public:
   static const unsigned RESERVED_SLOTS = 1;
 
   static const JSClass class_;
-  static const JSClass& protoClass_;
+
+  static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
+                             bool defineMembers);
 
   /*
    * Creates a new Symbol object boxing the given primitive Symbol.  The
    * object's [[Prototype]] is determined from context.
    */
   static SymbolObject* create(JSContext* cx, JS::HandleSymbol symbol);
 
   JS::Symbol* unbox() const {
@@ -54,14 +58,18 @@ class SymbolObject : public NativeObject
   static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx,
                                                   const CallArgs& args);
   static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc,
                                              Value* vp);
 
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec staticMethods[];
-  static const ClassSpec classSpec_;
 };
 
+extern JSObject* InitSymbolClass(JSContext* cx, Handle<GlobalObject*> global);
+
+extern JSObject* InitBareSymbolCtor(JSContext* cx,
+                                    Handle<GlobalObject*> global);
+
 } /* namespace js */
 
 #endif /* builtin_Symbol_h */
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -35,20 +35,18 @@ using mozilla::AssertedCast;
 using mozilla::CheckedInt32;
 using mozilla::IsPowerOfTwo;
 using mozilla::PodCopy;
 using mozilla::PointerRangeSize;
 
 using namespace js;
 
 const JSClass js::TypedObjectModuleObject::class_ = {
-    "TypedObject",
-    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
-        JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject),
-    JS_NULL_CLASS_OPS, &classSpec_};
+    "TypedObject", JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
+                       JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject)};
 
 static const JSFunctionSpec TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0), JS_FS_END};
 
 static void ReportCannotConvertTo(JSContext* cx, HandleValue fromValue,
                                   const char* toType) {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
                             InformalValueTypeName(fromValue), toType);
@@ -1360,38 +1358,41 @@ static JSObject* DefineMetaTypeDescr(JSC
     return nullptr;
   }
 
   module->initReservedSlot(protoSlot, ObjectValue(*proto));
 
   return ctor;
 }
 
-static JSObject* CreateTypedObjectModuleObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
-  RootedObject objProto(cx,
-                        GlobalObject::getOrCreateObjectPrototype(cx, global));
-  if (!objProto) {
-    return nullptr;
-  }
-
-  return NewObjectWithGivenProto<TypedObjectModuleObject>(cx, objProto,
-                                                          SingletonObject);
-}
-
 /*  The initialization strategy for TypedObjects is mildly unusual
  * compared to other classes. Because all of the types are members
  * of a single global, `TypedObject`, we basically make the
  * initializer for the `TypedObject` class populate the
  * `TypedObject` global (which is referred to as "module" herein).
  */
-static bool TypedObjectModuleObjectClassFinish(JSContext* cx, HandleObject ctor,
-                                               HandleObject proto) {
-  Handle<TypedObjectModuleObject*> module = ctor.as<TypedObjectModuleObject>();
-  Handle<GlobalObject*> global = cx->global();
+/* static */
+bool GlobalObject::initTypedObjectModule(JSContext* cx,
+                                         Handle<GlobalObject*> global) {
+  RootedObject objProto(cx,
+                        GlobalObject::getOrCreateObjectPrototype(cx, global));
+  if (!objProto) {
+    return false;
+  }
+
+  Rooted<TypedObjectModuleObject*> module(cx);
+  module = NewObjectWithGivenProto<TypedObjectModuleObject>(cx, objProto,
+                                                            SingletonObject);
+  if (!module) {
+    return false;
+  }
+
+  if (!JS_DefineFunctions(cx, module, TypedObjectMethods)) {
+    return false;
+  }
 
   // uint8, uint16, any, etc
 
 #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_)                    \
   if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_, \
                                               cx->names().name_))            \
     return false;
   JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
@@ -1469,27 +1470,32 @@ static bool TypedObjectModuleObjectClass
   }
 
   RootedValue structTypeValue(cx, ObjectValue(*structType));
   if (!DefineDataProperty(cx, module, cx->names().StructType, structTypeValue,
                           JSPROP_READONLY | JSPROP_PERMANENT)) {
     return false;
   }
 
-  return true;
+  // Everything is setup, install module on the global object:
+  RootedValue moduleValue(cx, ObjectValue(*module));
+  if (!DefineDataProperty(cx, global, cx->names().TypedObject, moduleValue,
+                          JSPROP_RESOLVING)) {
+    return false;
+  }
+
+  global->setConstructor(JSProto_TypedObject, moduleValue);
+
+  return module;
 }
 
-const ClassSpec TypedObjectModuleObject::classSpec_ = {
-    CreateTypedObjectModuleObject,
-    nullptr,
-    TypedObjectMethods,
-    nullptr,
-    nullptr,
-    nullptr,
-    TypedObjectModuleObjectClassFinish};
+JSObject* js::InitTypedObjectModuleObject(JSContext* cx,
+                                          Handle<GlobalObject*> global) {
+  return GlobalObject::getOrCreateTypedObjectModule(cx, global);
+}
 
 /******************************************************************************
  * Typed objects
  */
 
 uint32_t TypedObject::offset() const {
   if (is<InlineTypedObject>()) {
     return 0;
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -89,16 +89,18 @@
  * Typed objects (no matter their class) are non-native objects that
  * fully override the property accessors etc. The overridden accessor
  * methods are the same in each and are defined in methods of
  * TypedObject.
  */
 
 namespace js {
 
+class GlobalObject;
+
 /*
  * Helper method for converting a double into other scalar
  * types in the same way that JavaScript would. In particular,
  * simple C casting from double to int32_t gets things wrong
  * for values like 0xF0000000.
  */
 template <typename T>
 static T ConvertScalar(double d) {
@@ -528,19 +530,16 @@ class TypedObjectModuleObject : public N
     Float32Desc,
     Float64Desc,
     ObjectDesc,
     WasmAnyRefDesc,
     SlotCount
   };
 
   static const JSClass class_;
-
- private:
-  static const ClassSpec classSpec_;
 };
 
 /* Base type for transparent and opaque typed objects. */
 class TypedObject : public JSObject {
   static const bool IsTypedObjectClass = true;
 
   static MOZ_MUST_USE bool obj_getArrayElement(JSContext* cx,
                                                Handle<TypedObject*> typedObj,
@@ -1047,16 +1046,19 @@ inline bool IsComplexTypeDescrClass(cons
 inline bool IsTypeDescrClass(const JSClass* clasp) {
   return IsSimpleTypeDescrClass(clasp) || IsComplexTypeDescrClass(clasp);
 }
 
 inline bool TypedObject::opaque() const {
   return IsOpaqueTypedObjectClass(getClass());
 }
 
+JSObject* InitTypedObjectModuleObject(JSContext* cx,
+                                      JS::Handle<GlobalObject*> global);
+
 }  // namespace js
 
 template <>
 inline bool JSObject::is<js::SimpleTypeDescr>() const {
   return js::IsSimpleTypeDescrClass(getClass());
 }
 
 template <>
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -777,65 +777,77 @@ bool js::intl_supportedLocaleOrFallback(
       return false;
     }
   }
 
   args.rval().setString(candidate);
   return true;
 }
 
+const JSClass js::IntlClass = {js_Object_str,
+                               JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)};
+
 static bool intl_toSource(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   args.rval().setString(cx->names().Intl);
   return true;
 }
 
 static const JSFunctionSpec intl_static_methods[] = {
     JS_FN(js_toSource_str, intl_toSource, 0, 0),
     JS_SELF_HOSTED_FN("getCanonicalLocales", "Intl_getCanonicalLocales", 1, 0),
     JS_FS_END};
 
-static JSObject* CreateIntlObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
+/**
+ * Initializes the Intl Object and its standard built-in properties.
+ * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
+ */
+JSObject* js::InitIntlClass(JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
 
   // The |Intl| object is just a plain object with some "static" function
   // properties and some constructor properties.
-  return NewObjectWithGivenProto(cx, &IntlClass, proto, SingletonObject);
-}
+  RootedObject intl(
+      cx, NewObjectWithGivenProto(cx, &IntlClass, proto, SingletonObject));
+  if (!intl) {
+    return nullptr;
+  }
 
-/**
- * Initializes the Intl Object and its standard built-in properties.
- * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
- */
-static bool IntlClassFinish(JSContext* cx, HandleObject intl,
-                            HandleObject proto) {
+  // Add the static functions.
+  if (!JS_DefineFunctions(cx, intl, intl_static_methods)) {
+    return nullptr;
+  }
+
   // Add the constructor properties.
   RootedId ctorId(cx);
   RootedValue ctorValue(cx);
   for (const auto& protoKey :
        {JSProto_Collator, JSProto_DateTimeFormat, JSProto_NumberFormat,
         JSProto_PluralRules, JSProto_RelativeTimeFormat}) {
     JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, protoKey);
     if (!ctor) {
-      return false;
+      return nullptr;
     }
 
     ctorId = NameToId(ClassName(protoKey, cx));
     ctorValue.setObject(*ctor);
     if (!DefineDataProperty(cx, intl, ctorId, ctorValue, 0)) {
-      return false;
+      return nullptr;
     }
   }
 
-  return true;
-}
+  // The |Intl| object is fully set up now, so define the global property.
+  RootedValue intlValue(cx, ObjectValue(*intl));
+  if (!DefineDataProperty(cx, global, cx->names().Intl, intlValue,
+                          JSPROP_RESOLVING)) {
+    return nullptr;
+  }
 
-static const ClassSpec IntlClassSpec = {
-    CreateIntlObject, nullptr, intl_static_methods, nullptr,
-    nullptr,          nullptr, IntlClassFinish};
+  // Also cache |Intl| to implement spec language that conditions behavior
+  // based on values being equal to "the standard built-in |Intl| object".
+  // Use |setConstructor| to correspond with |JSProto_Intl|.
+  global->setConstructor(JSProto_Intl, ObjectValue(*intl));
 
-const JSClass js::IntlClass = {js_Object_str,
-                               JSCLASS_HAS_CACHED_PROTO(JSProto_Intl),
-                               JS_NULL_CLASS_OPS, &IntlClassSpec};
+  return intl;
+}
--- a/js/src/builtin/intl/IntlObject.h
+++ b/js/src/builtin/intl/IntlObject.h
@@ -9,19 +9,27 @@
 
 #include "mozilla/Attributes.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 
+class GlobalObject;
+
 extern const JSClass IntlClass;
 
 /**
+ * Initializes the Intl Object and its standard built-in properties.
+ * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
+ */
+extern JSObject* InitIntlClass(JSContext* cx, JS::Handle<GlobalObject*> global);
+
+/**
  * Returns a plain object with calendar information for a single valid locale
  * (callers must perform this validation).  The object will have these
  * properties:
  *
  *   firstDayOfWeek
  *     an integer in the range 1=Sunday to 7=Saturday indicating the day
  *     considered the first day of the week in calendars, e.g. 1 for en-US,
  *     2 for en-GB, 1 for bn-IN
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -338,22 +338,22 @@ wasmAssert(`(module
     let mod = wasmEvalText(`(module
                              (import "" "g" (global i64))
                              (func (export "f") (result i32)
                               (i64.eqz (global.get 0))))`,
                            {"":{g: new Global({value: "i64"})}});
     assertEq(mod.exports.f(), 1);
 
     {
-        // "value" is enumerable
+        // "value" is enumerable and is the first enumerated value
         let x = new Global({value: "i32"});
         let s = "";
         for ( let i in x )
             s = s + i + ",";
-        assertEq(s, "valueOf,value,");
+        assertEq(s.substring(0,6), "value,");
     }
 
     // "value" is defined on the prototype, not on the object
     assertEq("value" in Global.prototype, true);
 
     // Can't set the value of an immutable global
     assertErrorMessage(() => (new Global({value: "i32"})).value = 10,
                        TypeError,
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -815,18 +815,18 @@ static const JSStdName* LookupStdName(co
   return nullptr;
 }
 
 /*
  * Table of standard classes, indexed by JSProtoKey. For entries where the
  * JSProtoKey does not correspond to a class with a meaningful constructor, we
  * insert a null entry into the table.
  */
-#define STD_NAME_ENTRY(name, clasp) {NAME_OFFSET(name), JSProto_##name},
-#define STD_DUMMY_ENTRY(name, dummy) {0, JSProto_Null},
+#define STD_NAME_ENTRY(name, init, clasp) {NAME_OFFSET(name), JSProto_##name},
+#define STD_DUMMY_ENTRY(name, init, dummy) {0, JSProto_Null},
 static const JSStdName standard_class_names[] = {
     JS_FOR_PROTOTYPES(STD_NAME_ENTRY, STD_DUMMY_ENTRY){0, JSProto_LIMIT}};
 
 /*
  * Table of top-level function and constant names and the JSProtoKey of the
  * standard class that initializes them.
  */
 static const JSStdName builtin_property_names[] = {
@@ -890,17 +890,19 @@ JS_PUBLIC_API bool JS_ResolveStandardCla
 
   if (stdnm && GlobalObject::skipDeselectedConstructor(cx, stdnm->key)) {
     stdnm = nullptr;
   }
 
   // If this class is anonymous, then it doesn't exist as a global
   // property, so we won't resolve anything.
   JSProtoKey key = stdnm ? stdnm->key : JSProto_Null;
-  if (key != JSProto_Null) {
+  if (key != JSProto_Null && key != JSProto_AsyncFunction &&
+      key != JSProto_GeneratorFunction &&
+      key != JSProto_AsyncGeneratorFunction) {
     const JSClass* clasp = ProtoKeyToClass(key);
     if (!clasp || clasp->specShouldDefineConstructor()) {
       if (!GlobalObject::ensureConstructor(cx, global, key)) {
         return false;
       }
 
       *resolved = true;
       return true;
@@ -966,16 +968,22 @@ static bool EnumerateStandardClassesInTa
     if (!includeResolved && global->isStandardClassResolved(key)) {
       continue;
     }
 
     if (GlobalObject::skipDeselectedConstructor(cx, key)) {
       continue;
     }
 
+    // Async(Function|Generator) and Generator don't yet use ClassSpec.
+    if (key == JSProto_AsyncFunction || key == JSProto_GeneratorFunction ||
+        key == JSProto_AsyncGeneratorFunction) {
+      continue;
+    }
+
     if (const JSClass* clasp = ProtoKeyToClass(key)) {
       if (!clasp->specShouldDefineConstructor()) {
         continue;
       }
     }
 
     jsid id = NameToId(AtomStateOffsetToName(cx->names(), table[i].atomOffset));
     if (!properties.append(id)) {
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -70,17 +70,17 @@ static_assert(
         JSProto_Error + JSEXN_WASMLINKERROR == JSProto_LinkError &&
         JSProto_Error + JSEXN_WASMRUNTIMEERROR == JSProto_RuntimeError &&
         JSEXN_WASMRUNTIMEERROR + 1 == JSEXN_WARN &&
         JSEXN_WARN + 1 == JSEXN_NOTE && JSEXN_NOTE + 1 == JSEXN_LIMIT,
     "GetExceptionProtoKey and ExnTypeFromProtoKey require that "
     "each corresponding JSExnType and JSProtoKey value be separated "
     "by the same constant value");
 
-static inline constexpr JSProtoKey GetExceptionProtoKey(JSExnType exn) {
+static inline JSProtoKey GetExceptionProtoKey(JSExnType exn) {
   MOZ_ASSERT(JSEXN_ERR <= exn);
   MOZ_ASSERT(exn < JSEXN_WARN);
   return JSProtoKey(JSProto_Error + int(exn));
 }
 
 static inline JSExnType ExnTypeFromProtoKey(JSProtoKey key) {
   JSExnType type = static_cast<JSExnType>(key - JSProto_Error);
   MOZ_ASSERT(type >= JSEXN_ERR);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -90,16 +90,19 @@ static bool math_function(JSContext* cx,
   if (args.length() == 0) {
     args.rval().setNaN();
     return true;
   }
 
   return math_function<F>(cx, args[0], args.rval());
 }
 
+const JSClass js::MathClass = {js_Math_str,
+                               JSCLASS_HAS_CACHED_PROTO(JSProto_Math)};
+
 bool js::math_abs_handle(JSContext* cx, js::HandleValue v,
                          js::MutableHandleValue r) {
   double x;
   if (!ToNumber(cx, v, &x)) {
     return false;
   }
 
   double z = Abs(x);
@@ -929,32 +932,36 @@ static const JSFunctionSpec math_static_
     JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
     JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
     JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
     JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
     JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
     JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
     JS_FS_END};
 
-static const JSPropertySpec math_static_properties[] = {
-    JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY), JS_PS_END};
-
-static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
-  Handle<GlobalObject*> global = cx->global();
+JSObject* js::InitMathClass(JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject);
-}
-
-static bool MathClassFinish(JSContext* cx, HandleObject ctor,
-                            HandleObject proto) {
-  return JS_DefineConstDoubles(cx, ctor, math_constants);
-}
+  RootedObject Math(
+      cx, NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject));
+  if (!Math) {
+    return nullptr;
+  }
 
-static const ClassSpec MathClassSpec = {
-    CreateMathObject, nullptr, math_static_methods, math_static_properties,
-    nullptr,          nullptr, MathClassFinish};
+  if (!JS_DefineProperty(cx, global, js_Math_str, Math, JSPROP_RESOLVING)) {
+    return nullptr;
+  }
+  if (!JS_DefineFunctions(cx, Math, math_static_methods)) {
+    return nullptr;
+  }
+  if (!JS_DefineConstDoubles(cx, Math, math_constants)) {
+    return nullptr;
+  }
+  if (!DefineToStringTag(cx, Math, cx->names().Math)) {
+    return nullptr;
+  }
 
-const JSClass js::MathClass = {js_Math_str,
-                               JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
-                               JS_NULL_CLASS_OPS, &MathClassSpec};
+  global->setConstructor(JSProto_Math, ObjectValue(*Math));
+
+  return Math;
+}
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -10,24 +10,28 @@
 #include "mozilla/MemoryReporting.h"
 
 #include <stdint.h>
 
 #include "NamespaceImports.h"
 
 namespace js {
 
+class GlobalObject;
+
 typedef double (*UnaryFunType)(double);
 
 /*
  * JS math functions.
  */
 
 extern const JSClass MathClass;
 
+extern JSObject* InitMathClass(JSContext* cx, Handle<GlobalObject*> global);
+
 extern uint64_t GenerateRandomSeed();
 
 // Fill |seed[0]| and |seed[1]| with random bits, suitable for
 // seeding a XorShift128+ random number generator.
 extern void GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
 
 extern double math_random_impl(JSContext* cx);
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -625,18 +625,17 @@ bool js::num_parseInt(JSContext* cx, uns
 
 static const JSFunctionSpec number_functions[] = {
     JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1, JSPROP_RESOLVING),
     JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1, JSPROP_RESOLVING),
     JS_FS_END};
 
 const JSClass NumberObject::class_ = {
     js_Number_str,
-    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
-    JS_NULL_CLASS_OPS, &NumberObject::classSpec_};
+    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number)};
 
 static bool Number(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (args.length() > 0) {
     // BigInt proposal section 6.2, steps 2a-c.
     if (!ToNumeric(cx, args[0])) {
       return false;
@@ -1335,28 +1334,34 @@ void js::FinishRuntimeNumberState(JSRunt
    * The free also releases the memory for decimalSeparator and numGrouping
    * strings.
    */
   char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
   js_free(storage);
 }
 #endif
 
-JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
-  NumberObject* numberProto =
-      GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
+JSObject* js::InitNumberClass(JSContext* cx, Handle<GlobalObject*> global) {
+  Rooted<NumberObject*> numberProto(cx);
+  numberProto = GlobalObject::createBlankPrototype<NumberObject>(cx, global);
   if (!numberProto) {
     return nullptr;
   }
   numberProto->setPrimitiveValue(0);
-  return numberProto;
-}
 
-static bool NumberClassFinish(JSContext* cx, HandleObject ctor,
-                              HandleObject proto) {
+  RootedFunction ctor(cx);
+  ctor = GlobalObject::createConstructor(cx, Number, cx->names().Number, 1);
+  if (!ctor) {
+    return nullptr;
+  }
+
+  if (!LinkConstructorAndPrototype(cx, ctor, numberProto)) {
+    return nullptr;
+  }
+
   // Our NaN must be one particular canonical value, because we rely on NaN
   // encoding for our value representation.  See Value.h.
   static const JSConstDoubleSpec number_constants[] = {
       // clang-format off
         {"NaN",               GenericNaN()               },
         {"POSITIVE_INFINITY", mozilla::PositiveInfinity<double>() },
         {"NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>() },
         {"MAX_VALUE",         1.7976931348623157E+308    },
@@ -1368,74 +1373,76 @@ static bool NumberClassFinish(JSContext*
         /* ES6 (May 2013 draft) 15.7.3.7 */
         {"EPSILON", 2.2204460492503130808472633361816e-16},
         {0,0}
       // clang-format on
   };
 
   // Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor.
   if (!JS_DefineConstDoubles(cx, ctor, number_constants)) {
-    return false;
+    return nullptr;
+  }
+
+  if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, number_static_methods)) {
+    return nullptr;
   }
 
-  Handle<GlobalObject*> global = cx->global();
+  if (!DefinePropertiesAndFunctions(cx, numberProto, nullptr, number_methods)) {
+    return nullptr;
+  }
 
   if (!JS_DefineFunctions(cx, global, number_functions)) {
-    return false;
+    return nullptr;
   }
 
   // Number.parseInt should be the same function object as global parseInt.
   RootedId parseIntId(cx, NameToId(cx->names().parseInt));
   JSFunction* parseInt =
       DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
   if (!parseInt) {
-    return false;
+    return nullptr;
   }
   RootedValue parseIntValue(cx, ObjectValue(*parseInt));
   if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
-    return false;
+    return nullptr;
   }
 
   // Number.parseFloat should be the same function object as global
   // parseFloat.
   RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
   JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
                                           num_parseFloat, 1, JSPROP_RESOLVING);
   if (!parseFloat) {
-    return false;
+    return nullptr;
   }
   RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
   if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
-    return false;
+    return nullptr;
   }
 
   RootedValue valueNaN(cx, JS::NaNValue());
   RootedValue valueInfinity(cx, JS::InfinityValue());
 
   // ES5 15.1.1.1, 15.1.1.2
   if (!NativeDefineDataProperty(
           cx, global, cx->names().NaN, valueNaN,
           JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
       !NativeDefineDataProperty(
           cx, global, cx->names().Infinity, valueInfinity,
           JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
-    return false;
+    return nullptr;
   }
 
-  return true;
-}
+  if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Number, ctor,
+                                            numberProto)) {
+    return nullptr;
+  }
 
-const ClassSpec NumberObject::classSpec_ = {
-    GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION>,
-    NumberObject::createPrototype,
-    number_static_methods,
-    nullptr,
-    number_methods,
-    nullptr,
-    NumberClassFinish};
+  return numberProto;
+}
 
 static char* FracNumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d,
                                  int base = 10) {
 #ifdef DEBUG
   {
     int32_t _;
     MOZ_ASSERT(!NumberEqualsInt32(d, &_));
   }
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -41,17 +41,17 @@ enum JSType {
   JSTYPE_NULL,      /* null */
   JSTYPE_SYMBOL,    /* symbol */
   JSTYPE_BIGINT,    /* BigInt */
   JSTYPE_LIMIT
 };
 
 /* Dense index into cached prototypes and class atoms for standard objects. */
 enum JSProtoKey {
-#define PROTOKEY_AND_INITIALIZER(name, clasp) JSProto_##name,
+#define PROTOKEY_AND_INITIALIZER(name, init, clasp) JSProto_##name,
   JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
 #undef PROTOKEY_AND_INITIALIZER
       JSProto_LIMIT
 };
 
 /* Struct forward declarations. */
 struct JS_PUBLIC_API JSClass;
 class JSErrorReport;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -748,27 +748,19 @@ const ClassExtension js::ProxyClassExten
 
 const ObjectOps js::ProxyObjectOps = {
     proxy_LookupProperty, Proxy::defineProperty,
     Proxy::has,           Proxy::get,
     Proxy::set,           Proxy::getOwnPropertyDescriptor,
     proxy_DeleteProperty, Proxy::getElements,
     Proxy::fun_toString};
 
-static const JSFunctionSpec proxy_static_methods[] = {
-    JS_FN("revocable", proxy_revocable, 2, 0), JS_FS_END};
-
-static const ClassSpec ProxyClassSpec = {
-    GenericCreateConstructor<js::proxy, 2, gc::AllocKind::FUNCTION>, nullptr,
-    proxy_static_methods, nullptr};
-
-const JSClass js::ProxyClass = PROXY_CLASS_DEF_WITH_CLASS_SPEC(
-    "Proxy",
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) | JSCLASS_HAS_RESERVED_SLOTS(2),
-    &ProxyClassSpec);
+const JSClass js::ProxyClass =
+    PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy) |
+                                 JSCLASS_HAS_RESERVED_SLOTS(2));
 
 JS_FRIEND_API JSObject* js::NewProxyObject(JSContext* cx,
                                            const BaseProxyHandler* handler,
                                            HandleValue priv, JSObject* proto_,
                                            const ProxyOptions& options) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
@@ -797,8 +789,29 @@ void ProxyObject::renew(const BaseProxyH
   MOZ_ASSERT(hasDynamicPrototype());
 
   setHandler(handler);
   setCrossCompartmentPrivate(priv);
   for (size_t i = 0; i < numReservedSlots(); i++) {
     setReservedSlot(i, UndefinedValue());
   }
 }
+
+JSObject* js::InitProxyClass(JSContext* cx, Handle<GlobalObject*> global) {
+  static const JSFunctionSpec static_methods[] = {
+      JS_FN("revocable", proxy_revocable, 2, 0), JS_FS_END};
+
+  RootedFunction ctor(cx);
+  ctor = GlobalObject::createConstructor(cx, proxy, cx->names().Proxy, 2);
+  if (!ctor) {
+    return nullptr;
+  }
+
+  if (!JS_DefineFunctions(cx, ctor, static_methods)) {
+    return nullptr;
+  }
+  if (!JS_DefineProperty(cx, global, "Proxy", ctor, JSPROP_RESOLVING)) {
+    return nullptr;
+  }
+
+  global->setConstructor(JSProto_Proxy, ObjectValue(*ctor));
+  return ctor;
+}
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -8,16 +8,18 @@
 #define proxy_Proxy_h
 
 #include "NamespaceImports.h"
 
 #include "js/Class.h"
 
 namespace js {
 
+class GlobalObject;
+
 /*
  * Dispatch point for handlers that executes the appropriate C++ or scripted
  * traps.
  *
  * Important: All proxy methods need either (a) an AutoEnterPolicy in their
  * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
  * 945826 comment 0.
  */
@@ -104,11 +106,13 @@ bool ProxyGetPropertyByValue(JSContext* 
                              HandleValue idVal, MutableHandleValue vp);
 
 bool ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id,
                       HandleValue val, bool strict);
 
 bool ProxySetPropertyByValue(JSContext* cx, HandleObject proxy,
                              HandleValue idVal, HandleValue val, bool strict);
 
+extern JSObject* InitProxyClass(JSContext* cx, Handle<GlobalObject*> global);
+
 } /* namespace js */
 
 #endif /* proxy_Proxy_h */
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -7,76 +7,60 @@
 #include "vm/AsyncFunction.h"
 
 #include "mozilla/Maybe.h"
 
 #include "builtin/Promise.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
-#include "vm/NativeObject.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Maybe;
 
-static JSObject* CreateAsyncFunction(JSContext* cx, JSProtoKey key) {
+JSObject* js::InitAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) {
+  RootedObject asyncFunctionProto(
+      cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+  if (!asyncFunctionProto) {
+    return nullptr;
+  }
+
+  if (!DefineToStringTag(cx, asyncFunctionProto, cx->names().AsyncFunction)) {
+    return nullptr;
+  }
+
   RootedObject proto(
       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
-
   HandlePropertyName name = cx->names().AsyncFunction;
-  return NewFunctionWithProto(cx, AsyncFunctionConstructor, 1,
-                              FunctionFlags::NATIVE_CTOR, nullptr, name, proto);
-}
-
-static JSObject* CreateAsyncFunctionPrototype(JSContext* cx, JSProtoKey key) {
-  return NewSingletonObjectWithFunctionPrototype(cx, cx->global());
-}
-
-static bool AsyncFunctionClassFinish(JSContext* cx, HandleObject asyncFunction,
-                                     HandleObject asyncFunctionProto) {
-  // Change the "constructor" property to non-writable before adding any other
-  // properties, so it's still the last property and can be modified without a
-  // dictionary-mode transition.
-  MOZ_ASSERT(StringEqualsAscii(
-      JSID_TO_LINEAR_STRING(
-          asyncFunctionProto->as<NativeObject>().lastProperty()->propid()),
-      "constructor"));
-  MOZ_ASSERT(!asyncFunctionProto->as<NativeObject>().inDictionaryMode());
+  RootedObject asyncFunction(
+      cx,
+      NewFunctionWithProto(cx, AsyncFunctionConstructor, 1,
+                           FunctionFlags::NATIVE_CTOR, nullptr, name, proto));
+  if (!asyncFunction) {
+    return nullptr;
+  }
+  if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
+                                   JSPROP_PERMANENT | JSPROP_READONLY,
+                                   JSPROP_READONLY)) {
+    return nullptr;
+  }
 
-  RootedValue asyncFunctionVal(cx, ObjectValue(*asyncFunction));
-  if (!DefineDataProperty(cx, asyncFunctionProto, cx->names().constructor,
-                          asyncFunctionVal, JSPROP_READONLY)) {
-    return false;
-  }
-  MOZ_ASSERT(!asyncFunctionProto->as<NativeObject>().inDictionaryMode());
-
-  return DefineToStringTag(cx, asyncFunctionProto, cx->names().AsyncFunction);
+  global->setConstructor(JSProto_AsyncFunction, ObjectValue(*asyncFunction));
+  global->setPrototype(JSProto_AsyncFunction, ObjectValue(*asyncFunctionProto));
+  return asyncFunction;
 }
 
-static const ClassSpec AsyncFunctionClassSpec = {
-    CreateAsyncFunction,
-    CreateAsyncFunctionPrototype,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    AsyncFunctionClassFinish,
-    ClassSpec::DontDefineConstructor};
-
-const JSClass js::AsyncFunctionClass = {"AsyncFunction", 0, JS_NULL_CLASS_OPS,
-                                        &AsyncFunctionClassSpec};
-
 enum class ResumeKind { Normal, Throw };
 
 // ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
 // 6.2.3.1.1 Await Fulfilled Functions
 // 6.2.3.1.2 Await Rejected Functions
 static bool AsyncFunctionResume(JSContext* cx,
                                 Handle<AsyncFunctionGeneratorObject*> generator,
                                 ResumeKind kind, HandleValue valueOrReason) {
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -11,18 +11,17 @@
 #include "js/Class.h"
 #include "vm/GeneratorObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 namespace js {
 
 class AsyncFunctionGeneratorObject;
-
-extern const JSClass AsyncFunctionClass;
+class GlobalObject;
 
 // Resume the async function when the `await` operand resolves.
 // Split into two functions depending on whether the awaited value was
 // fulfilled or rejected.
 MOZ_MUST_USE bool AsyncFunctionAwaitedFulfilled(
     JSContext* cx, Handle<AsyncFunctionGeneratorObject*> generator,
     HandleValue value);
 
@@ -52,11 +51,14 @@ class AsyncFunctionGeneratorObject : pub
   static AsyncFunctionGeneratorObject* create(JSContext* cx,
                                               HandleFunction asyncGen);
 
   PromiseObject* promise() {
     return &getFixedSlot(PROMISE_SLOT).toObject().as<PromiseObject>();
   }
 };
 
+extern JSObject* InitAsyncFunction(JSContext* cx,
+                                   js::Handle<GlobalObject*> global);
+
 }  // namespace js
 
 #endif /* vm_AsyncFunction_h */
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.cpp
@@ -453,92 +453,69 @@ bool GlobalObject::initAsyncFromSyncIter
     return false;
   }
 
   global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO,
                           ObjectValue(*asyncFromSyncIterProto));
   return true;
 }
 
-static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) {
+JSObject* js::InitAsyncGeneratorFunction(JSContext* cx,
+                                         Handle<GlobalObject*> global) {
+  RootedObject asyncIterProto(
+      cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
+  if (!asyncIterProto) {
+    return nullptr;
+  }
+
+  // 25.5 AsyncGenerator Objects
+  RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting(
+                                     cx, &PlainObject::class_, asyncIterProto));
+  if (!asyncGenProto) {
+    return nullptr;
+  }
+  if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr,
+                                    async_generator_methods) ||
+      !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) {
+    return nullptr;
+  }
+
+  // 25.3.3 Properties of the AsyncGeneratorFunction Prototype Object
+  RootedObject asyncGenerator(
+      cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+  if (!asyncGenerator) {
+    return nullptr;
+  }
+  if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto,
+                                   JSPROP_READONLY, JSPROP_READONLY) ||
+      !DefineToStringTag(cx, asyncGenerator,
+                         cx->names().AsyncGeneratorFunction)) {
+    return nullptr;
+  }
+
   RootedObject proto(
       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
   HandlePropertyName name = cx->names().AsyncGeneratorFunction;
 
   // 25.3.1 The AsyncGeneratorFunction Constructor
-  return NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1,
-                              FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
-                              gc::AllocKind::FUNCTION, SingletonObject);
-}
-
-static JSObject* CreateAsyncGeneratorFunctionPrototype(JSContext* cx,
-                                                       JSProtoKey key) {
-  return NewSingletonObjectWithFunctionPrototype(cx, cx->global());
-}
-
-static bool AsyncGeneratorFunctionClassFinish(JSContext* cx,
-                                              HandleObject asyncGenFunction,
-                                              HandleObject asyncGenerator) {
-  Handle<GlobalObject*> global = cx->global();
-
-  // Change the "constructor" property to non-writable before adding any other
-  // properties, so it's still the last property and can be modified without a
-  // dictionary-mode transition.
-  MOZ_ASSERT(StringEqualsAscii(
-      JSID_TO_LINEAR_STRING(
-          asyncGenerator->as<NativeObject>().lastProperty()->propid()),
-      "constructor"));
-  MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
-
-  RootedValue asyncGenFunctionVal(cx, ObjectValue(*asyncGenFunction));
-  if (!DefineDataProperty(cx, asyncGenerator, cx->names().constructor,
-                          asyncGenFunctionVal, JSPROP_READONLY)) {
-    return false;
+  RootedObject asyncGenFunction(
+      cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1,
+                               FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
+                               gc::AllocKind::FUNCTION, SingletonObject));
+  if (!asyncGenFunction) {
+    return nullptr;
   }
-  MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
-
-  RootedObject asyncIterProto(
-      cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
-  if (!asyncIterProto) {
-    return false;
-  }
-
-  // 25.5 AsyncGenerator Objects
-  RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting(
-                                     cx, &PlainObject::class_, asyncIterProto));
-  if (!asyncGenProto) {
-    return false;
-  }
-  if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr,
-                                    async_generator_methods) ||
-      !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) {
-    return false;
-  }
-
-  // 25.3.3 Properties of the AsyncGeneratorFunction Prototype Object
-  if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto,
-                                   JSPROP_READONLY, JSPROP_READONLY) ||
-      !DefineToStringTag(cx, asyncGenerator,
-                         cx->names().AsyncGeneratorFunction)) {
-    return false;
+  if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator,
+                                   JSPROP_PERMANENT | JSPROP_READONLY,
+                                   JSPROP_READONLY)) {
+    return nullptr;
   }
 
   global->setAsyncGeneratorPrototype(asyncGenProto);
-
-  return true;
+  global->setConstructor(JSProto_AsyncGeneratorFunction,
+                         ObjectValue(*asyncGenFunction));
+  global->setPrototype(JSProto_AsyncGeneratorFunction,
+                       ObjectValue(*asyncGenerator));
+  return asyncGenFunction;
 }
-
-static const ClassSpec AsyncGeneratorFunctionClassSpec = {
-    CreateAsyncGeneratorFunction,
-    CreateAsyncGeneratorFunctionPrototype,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    AsyncGeneratorFunctionClassFinish,
-    ClassSpec::DontDefineConstructor};
-
-const JSClass js::AsyncGeneratorFunctionClass = {
-    "AsyncGeneratorFunction", 0, JS_NULL_CLASS_OPS,
-    &AsyncGeneratorFunctionClassSpec};
--- a/js/src/vm/AsyncIteration.h
+++ b/js/src/vm/AsyncIteration.h
@@ -12,18 +12,17 @@
 #include "vm/GeneratorObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/List.h"
 
 namespace js {
 
 class AsyncGeneratorObject;
-
-extern const JSClass AsyncGeneratorFunctionClass;
+class GlobalObject;
 
 // Resume the async generator when the `await` operand fulfills to `value`.
 MOZ_MUST_USE bool AsyncGeneratorAwaitedFulfilled(
     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
     HandleValue value);
 
 // Resume the async generator when the `await` operand rejects with `reason`.
 MOZ_MUST_USE bool AsyncGeneratorAwaitedRejected(
@@ -291,11 +290,14 @@ class AsyncFromSyncIteratorObject : publ
 
   const Value& nextMethod() const { return getFixedSlot(Slot_NextMethod); }
 };
 
 MOZ_MUST_USE bool AsyncGeneratorResume(
     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
     CompletionKind completionKind, HandleValue argument);
 
+extern JSObject* InitAsyncGeneratorFunction(JSContext* cx,
+                                            js::Handle<GlobalObject*> global);
+
 }  // namespace js
 
 #endif /* vm_AsyncIteration_h */
--- a/js/src/vm/BooleanObject.h
+++ b/js/src/vm/BooleanObject.h
@@ -8,39 +8,41 @@
 #define vm_BooleanObject_h
 
 #include "builtin/Boolean.h"
 
 #include "vm/NativeObject.h"
 
 namespace js {
 
+class GlobalObject;
+
 class BooleanObject : public NativeObject {
   /* Stores this Boolean object's [[PrimitiveValue]]. */
   static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
-  static const ClassSpec classSpec_;
-
  public:
   static const unsigned RESERVED_SLOTS = 1;
 
   static const JSClass class_;
 
   /*
    * Creates a new Boolean object boxing the given primitive bool.
    * If proto is nullptr, the [[Prototype]] will default to Boolean.prototype.
    */
   static inline BooleanObject* create(JSContext* cx, bool b,
                                       HandleObject proto = nullptr);
 
   bool unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toBoolean(); }
 
  private:
-  static JSObject* createPrototype(JSContext* cx, JSProtoKey key);
-
   inline void setPrimitiveValue(bool b) {
     setFixedSlot(PRIMITIVE_VALUE_SLOT, BooleanValue(b));
   }
+
+  /* For access to init, as Boolean.prototype is special. */
+  friend JSObject* js::InitBooleanClass(JSContext* cx,
+                                        js::Handle<GlobalObject*> global);
 };
 
 }  // namespace js
 
 #endif /* vm_BooleanObject_h */
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -4,17 +4,16 @@
  * 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/GeneratorObject.h"
 
 #include "js/PropertySpec.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
-#include "vm/GlobalObject.h"
 #include "vm/JSObject.h"
 
 #include "debugger/DebugAPI-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
@@ -236,96 +235,72 @@ JSObject* js::NewSingletonObjectWithFunc
     return nullptr;
   }
   if (!JSObject::setDelegate(cx, obj)) {
     return nullptr;
   }
   return obj;
 }
 
-static JSObject* CreateGeneratorFunction(JSContext* cx, JSProtoKey key) {
+JSObject* js::InitGeneratorFunction(JSContext* cx,
+                                    Handle<GlobalObject*> global) {
+  RootedObject iteratorProto(
+      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
+  if (!iteratorProto) {
+    return nullptr;
+  }
+
+  RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(
+                                      cx, &PlainObject::class_, iteratorProto));
+  if (!genObjectProto) {
+    return nullptr;
+  }
+  if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr,
+                                    generator_methods) ||
+      !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) {
+    return nullptr;
+  }
+
+  RootedObject genFunctionProto(
+      cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+  if (!genFunctionProto) {
+    return nullptr;
+  }
+  if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto,
+                                   JSPROP_READONLY, JSPROP_READONLY) ||
+      !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) {
+    return nullptr;
+  }
+
   RootedObject proto(
       cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
   if (!proto) {
     return nullptr;
   }
-
   HandlePropertyName name = cx->names().GeneratorFunction;
-  return NewFunctionWithProto(cx, Generator, 1, FunctionFlags::NATIVE_CTOR,
-                              nullptr, name, proto, gc::AllocKind::FUNCTION,
-                              SingletonObject);
-}
-
-static JSObject* CreateGeneratorFunctionPrototype(JSContext* cx,
-                                                  JSProtoKey key) {
-  return NewSingletonObjectWithFunctionPrototype(cx, cx->global());
-}
-
-static bool GeneratorFunctionClassFinish(JSContext* cx,
-                                         HandleObject genFunction,
-                                         HandleObject genFunctionProto) {
-  Handle<GlobalObject*> global = cx->global();
-
-  // Change the "constructor" property to non-writable before adding any other
-  // properties, so it's still the last property and can be modified without a
-  // dictionary-mode transition.
-  MOZ_ASSERT(StringEqualsAscii(
-      JSID_TO_LINEAR_STRING(
-          genFunctionProto->as<NativeObject>().lastProperty()->propid()),
-      "constructor"));
-  MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());
-
-  RootedValue genFunctionVal(cx, ObjectValue(*genFunction));
-  if (!DefineDataProperty(cx, genFunctionProto, cx->names().constructor,
-                          genFunctionVal, JSPROP_READONLY)) {
-    return false;
+  RootedObject genFunction(
+      cx, NewFunctionWithProto(cx, Generator, 1, FunctionFlags::NATIVE_CTOR,
+                               nullptr, name, proto, gc::AllocKind::FUNCTION,
+                               SingletonObject));
+  if (!genFunction) {
+    return nullptr;
   }
-  MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());
-
-  RootedObject iteratorProto(
-      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
-  if (!iteratorProto) {
-    return false;
-  }
-
-  RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(
-                                      cx, &PlainObject::class_, iteratorProto));
-  if (!genObjectProto) {
-    return false;
-  }
-  if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr,
-                                    generator_methods) ||
-      !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) {
-    return false;
-  }
-
-  if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto,
-                                   JSPROP_READONLY, JSPROP_READONLY) ||
-      !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) {
-    return false;
+  if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto,
+                                   JSPROP_PERMANENT | JSPROP_READONLY,
+                                   JSPROP_READONLY)) {
+    return nullptr;
   }
 
   global->setGeneratorObjectPrototype(genObjectProto);
-
-  return true;
+  global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
+  global->setPrototype(JSProto_GeneratorFunction,
+                       ObjectValue(*genFunctionProto));
+  return genFunction;
 }
 
-static const ClassSpec GeneratorFunctionClassSpec = {
-    CreateGeneratorFunction,
-    CreateGeneratorFunctionPrototype,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    GeneratorFunctionClassFinish,
-    ClassSpec::DontDefineConstructor};
-
-const JSClass js::GeneratorFunctionClass = {
-    "GeneratorFunction", 0, JS_NULL_CLASS_OPS, &GeneratorFunctionClassSpec};
-
 bool AbstractGeneratorObject::isAfterYield() {
   return isAfterYieldOrAwait(JSOP_YIELD);
 }
 
 bool AbstractGeneratorObject::isAfterAwait() {
   return isAfterYieldOrAwait(JSOP_AWAIT);
 }
 
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -11,17 +11,17 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Stack.h"
 
 namespace js {
 
-extern const JSClass GeneratorFunctionClass;
+class GlobalObject;
 
 enum class GeneratorResumeKind { Next, Throw, Return };
 
 class AbstractGeneratorObject : public NativeObject {
  public:
   // Magic value stored in the resumeIndex slot when the generator is
   // running or closing. See the resumeIndex comment below.
   static const int32_t RESUME_INDEX_RUNNING = INT32_MAX;
@@ -220,14 +220,17 @@ bool GeneratorThrowOrReturn(JSContext* c
  *   only on the stack, and not the `.generator` pseudo-variable this function
  *   consults.
  */
 AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
                                                     AbstractFramePtr frame);
 
 void SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame);
 
+extern JSObject* InitGeneratorFunction(JSContext* cx,
+                                       js::Handle<GlobalObject*> global);
+
 }  // namespace js
 
 template <>
 bool JSObject::is<js::AbstractGeneratorObject>() const;
 
 #endif /* vm_GeneratorObject_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -1,9 +1,8 @@
-
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/GlobalObject.h"
 
@@ -60,37 +59,50 @@
 #include "gc/FreeOp-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Realm-inl.h"
 
 using namespace js;
 
+struct ProtoTableEntry {
+  const JSClass* clasp;
+  ClassInitializerOp init;
+};
+
 namespace js {
 
 extern const JSClass IntlClass;
 extern const JSClass JSONClass;
 extern const JSClass MathClass;
-extern const JSClass ReflectClass;
 extern const JSClass WebAssemblyClass;
 
+#define DECLARE_PROTOTYPE_CLASS_INIT(name, init, clasp) \
+  extern JSObject* init(JSContext* cx, Handle<GlobalObject*> global);
+JS_FOR_EACH_PROTOTYPE(DECLARE_PROTOTYPE_CLASS_INIT)
+#undef DECLARE_PROTOTYPE_CLASS_INIT
+
 }  // namespace js
 
-static const JSClass* const protoTable[JSProto_LIMIT] = {
-#define INIT_FUNC(name, clasp) clasp,
-#define INIT_FUNC_DUMMY(name, clasp) nullptr,
+JSObject* js::InitViaClassSpec(JSContext* cx, Handle<GlobalObject*> global) {
+  MOZ_CRASH("InitViaClassSpec() should not be called.");
+}
+
+static const ProtoTableEntry protoTable[JSProto_LIMIT] = {
+#define INIT_FUNC(name, init, clasp) {clasp, init},
+#define INIT_FUNC_DUMMY(name, init, clasp) {nullptr, nullptr},
     JS_FOR_PROTOTYPES(INIT_FUNC, INIT_FUNC_DUMMY)
 #undef INIT_FUNC_DUMMY
 #undef INIT_FUNC
 };
 
 JS_FRIEND_API const JSClass* js::ProtoKeyToClass(JSProtoKey key) {
   MOZ_ASSERT(key < JSProto_LIMIT);
-  return protoTable[key];
+  return protoTable[key].clasp;
 }
 
 // This method is not in the header file to avoid having to include
 // TypedObject.h from GlobalObject.h. It is not generally perf
 // sensitive.
 TypedObjectModuleObject& js::GlobalObject::getTypedObjectModule() const {
   Value v = getConstructor(JSProto_TypedObject);
   // only gets called from contexts where TypedObject must be initialized
@@ -157,34 +169,59 @@ bool GlobalObject::resolveConstructor(JS
   // an allocation that re-entrantly tries to create the same prototype.
   AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
 
   // Constructor resolution may execute self-hosted scripts. These
   // self-hosted scripts do not call out to user code by construction. Allow
   // all scripts to execute, even in debuggee compartments that are paused.
   AutoSuppressDebuggeeNoExecuteChecks suppressNX(cx);
 
+  // There are two different kinds of initialization hooks. One of them is
+  // the class js::InitFoo hook, defined in a JSProtoKey-keyed table at the
+  // top of this file. The other lives in the ClassSpec for classes that
+  // define it. Classes may use one or the other, but not both.
+  ClassInitializerOp init = protoTable[key].init;
+  if (init == InitViaClassSpec) {
+    init = nullptr;
+  }
+
   // Some classes can be disabled at compile time, others at run time;
-  // if a feature is compile-time disabled, clasp is null.
+  // if a feature is compile-time disabled, init and clasp are both null.
   const JSClass* clasp = ProtoKeyToClass(key);
-  if (!clasp || skipDeselectedConstructor(cx, key)) {
+  if ((!init && !clasp) || skipDeselectedConstructor(cx, key)) {
     if (mode == IfClassIsDisabled::Throw) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_CONSTRUCTOR_DISABLED,
                                 clasp ? clasp->name : "constructor");
       return false;
     }
     return true;
   }
 
-  // Class spec must have a constructor defined.
-  if (!clasp->specDefined()) {
+  // Some classes have no init routine, which means that they're disabled at
+  // compile-time. We could try to enforce that callers never pass such keys
+  // to resolveConstructor, but that would cramp the style of consumers like
+  // GlobalObject::initStandardClasses that want to just carpet-bomb-call
+  // resolveConstructor with every JSProtoKey. So it's easier to just handle
+  // it here.
+  bool haveSpec = clasp && clasp->specDefined();
+  if (!init && !haveSpec) {
     return true;
   }
 
+  // See if there's an old-style initialization hook.
+  if (init) {
+    MOZ_ASSERT(!haveSpec);
+    return init(cx, global);
+  }
+
+  //
+  // Ok, we're doing it with a class spec.
+  //
+
   bool isObjectOrFunction = key == JSProto_Function || key == JSProto_Object;
 
   // We need to create the prototype first, and immediately stash it in the
   // slot. This is so the following bootstrap ordering is possible:
   // * Object.prototype
   // * Function.prototype
   // * Function
   // * Object
@@ -715,17 +752,17 @@ bool GlobalObject::initSelfHostingBuilti
       return false;
     }
   }
 
   return InitBareBuiltinCtor(cx, global, JSProto_Array) &&
          InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
          InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
          InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
-         InitBareBuiltinCtor(cx, global, JSProto_Symbol) &&
+         InitBareSymbolCtor(cx, global) &&
          DefineFunctions(cx, global, builtins, AsIntrinsic);
 }
 
 /* static */
 bool GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, HandleValue code,
                                            Handle<GlobalObject*> global) {
   HeapSlot& v = global->getSlotRef(RUNTIME_CODEGEN_ENABLED);
   if (v.isUndefined()) {
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -450,17 +450,19 @@ class GlobalObject : public NativeObject
     if (!ensureConstructor(cx, global, JSProto_WeakSet)) {
       return nullptr;
     }
     return &global->getPrototype(JSProto_WeakSet).toObject().as<NativeObject>();
   }
 
   static JSObject* getOrCreateTypedObjectModule(JSContext* cx,
                                                 Handle<GlobalObject*> global) {
-    return getOrCreateConstructor(cx, JSProto_TypedObject);
+    return getOrCreateObject(cx, global,
+                             APPLICATION_SLOTS + JSProto_TypedObject,
+                             initTypedObjectModule);
   }
 
   static TypeDescr* getOrCreateScalarTypeDescr(JSContext* cx,
                                                Handle<GlobalObject*> global,
                                                Scalar::Type scalarType);
 
   static TypeDescr* getOrCreateReferenceTypeDescr(JSContext* cx,
                                                   Handle<GlobalObject*> global,
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -187,17 +187,18 @@ inline JSAtom* js::AtomStateEntry::asPtr
   }
   return atom;
 }
 
 UniqueChars js::AtomToPrintableString(JSContext* cx, JSAtom* atom) {
   return QuoteString(cx, atom);
 }
 
-#define DEFINE_PROTO_STRING(name, clasp) const char js_##name##_str[] = #name;
+#define DEFINE_PROTO_STRING(name, init, clasp) \
+  const char js_##name##_str[] = #name;
 JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
 #undef DEFINE_PROTO_STRING
 
 #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
 #undef CONST_CHAR_STR
 
 // Use a low initial capacity for the permanent atoms table to avoid penalizing
@@ -245,17 +246,18 @@ bool JSRuntime::initializeAtoms(JSContex
     return false;
   }
 
   static const CommonNameInfo cachedNames[] = {
 #define COMMON_NAME_INFO(idpart, id, text) \
   {js_##idpart##_str, sizeof(text) - 1},
       FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
-#define COMMON_NAME_INFO(name, clasp) {js_##name##_str, sizeof(#name) - 1},
+#define COMMON_NAME_INFO(name, init, clasp) \
+  {js_##name##_str, sizeof(#name) - 1},
           JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
 #define COMMON_NAME_INFO(name) {#name, sizeof(#name) - 1},
               JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
 #define COMMON_NAME_INFO(name) {"Symbol." #name, sizeof("Symbol." #name) - 1},
                   JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
 #undef COMMON_NAME_INFO
--- a/js/src/vm/JSAtom.h
+++ b/js/src/vm/JSAtom.h
@@ -22,17 +22,18 @@ namespace js {
  */
 extern UniqueChars AtomToPrintableString(JSContext* cx, JSAtom* atom);
 
 class PropertyName;
 
 } /* namespace js */
 
 /* Well-known predefined C strings. */
-#define DECLARE_PROTO_STR(name, clasp) extern const char js_##name##_str[];
+#define DECLARE_PROTO_STR(name, init, clasp) \
+  extern const char js_##name##_str[];
 JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
 #undef DECLARE_PROTO_STR
 
 #define DECLARE_CONST_CHAR_STR(idpart, id, text) \
   extern const char js_##idpart##_str[];
 FOR_EACH_COMMON_PROPERTYNAME(DECLARE_CONST_CHAR_STR)
 #undef DECLARE_CONST_CHAR_STR
 
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -3410,19 +3410,19 @@ void GetObjectSlotNameFunctor::operator(
     do {
       const char* slotname = nullptr;
       const char* pattern = nullptr;
       if (obj->is<GlobalObject>()) {
         pattern = "CLASS_OBJECT(%s)";
         if (false) {
           ;
         }
-#define TEST_SLOT_MATCHES_PROTOTYPE(name, clasp) \
-  else if ((JSProto_##name) == slot) {           \
-    slotname = js_##name##_str;                  \
+#define TEST_SLOT_MATCHES_PROTOTYPE(name, init, clasp) \
+  else if ((JSProto_##name) == slot) {                 \
+    slotname = js_##name##_str;                        \
   }
         JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
 #undef TEST_SLOT_MATCHES_PROTOTYPE
       } else {
         pattern = "%s";
         if (obj->is<EnvironmentObject>()) {
           if (slot == EnvironmentObject::enclosingEnvironmentSlot()) {
             slotname = "enclosing_environment";
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -805,16 +805,21 @@ MOZ_ALWAYS_INLINE const char* GetObjectC
  * types of environment.
  */
 Value GetThisValue(JSObject* obj);
 
 Value GetThisValueOfLexical(JSObject* env);
 
 Value GetThisValueOfWith(JSObject* env);
 
+/* * */
+
+using ClassInitializerOp = JSObject* (*)(JSContext* cx,
+                                         Handle<GlobalObject*> global);
+
 } /* namespace js */
 
 namespace js {
 
 bool NewObjectWithTaggedProtoIsCachable(JSContext* cx,
                                         Handle<TaggedProto> proto,
                                         NewObjectKind newKind,
                                         const JSClass* clasp);
--- a/js/src/vm/NumberObject.h
+++ b/js/src/vm/NumberObject.h
@@ -2,43 +2,45 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/. */
 
 #ifndef vm_NumberObject_h
 #define vm_NumberObject_h
 
-#include "vm/NativeObject.h"
+#include "jsnum.h"
 
 namespace js {
 
+class GlobalObject;
+
 class NumberObject : public NativeObject {
   /* Stores this Number object's [[PrimitiveValue]]. */
   static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
-  static const ClassSpec classSpec_;
-
  public:
   static const unsigned RESERVED_SLOTS = 1;
 
   static const JSClass class_;
 
   /*
    * Creates a new Number object boxing the given number.
    * If proto is nullptr, then Number.prototype will be used instead.
    */
   static inline NumberObject* create(JSContext* cx, double d,
                                      HandleObject proto = nullptr);
 
   double unbox() const { return getFixedSlot(PRIMITIVE_VALUE_SLOT).toNumber(); }
 
  private:
-  static JSObject* createPrototype(JSContext* cx, JSProtoKey key);
-
   inline void setPrimitiveValue(double d) {
     setFixedSlot(PRIMITIVE_VALUE_SLOT, NumberValue(d));
   }
+
+  /* For access to init, as Number.prototype is special. */
+  friend JSObject* js::InitNumberClass(JSContext* cx,
+                                       Handle<GlobalObject*> global);
 };
 
 }  // namespace js
 
 #endif /* vm_NumberObject_h */
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -129,17 +129,17 @@ namespace JS {
 struct RuntimeSizes;
 }  // namespace JS
 
 /* Various built-in or commonly-used names pinned on first context. */
 struct JSAtomState {
 #define PROPERTYNAME_FIELD(idpart, id, text) js::ImmutablePropertyNamePtr id;
   FOR_EACH_COMMON_PROPERTYNAME(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
-#define PROPERTYNAME_FIELD(name, clasp) js::ImmutablePropertyNamePtr name;
+#define PROPERTYNAME_FIELD(name, init, clasp) js::ImmutablePropertyNamePtr name;
   JS_FOR_EACH_PROTOTYPE(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
 #define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr name;
   JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
 #define PROPERTYNAME_FIELD(name) js::ImmutablePropertyNamePtr Symbol_##name;
   JS_FOR_EACH_WELL_KNOWN_SYMBOL(PROPERTYNAME_FIELD)
 #undef PROPERTYNAME_FIELD
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -7,22 +7,24 @@
 #ifndef vm_StringObject_h
 #define vm_StringObject_h
 
 #include "vm/JSObject.h"
 #include "vm/Shape.h"
 
 namespace js {
 
+class GlobalObject;
+
+JSObject* InitStringClass(JSContext* cx, Handle<GlobalObject*> global);
+
 class StringObject : public NativeObject {
   static const unsigned PRIMITIVE_VALUE_SLOT = 0;
   static const unsigned LENGTH_SLOT = 1;
 
-  static const ClassSpec classSpec_;
-
  public:
   static const unsigned RESERVED_SLOTS = 2;
 
   static const JSClass class_;
 
   /*
    * Creates a new String object boxing the given string.  The object's
    * [[Prototype]] is determined from context.
@@ -50,20 +52,22 @@ class StringObject : public NativeObject
     return getFixedSlotOffset(PRIMITIVE_VALUE_SLOT);
   }
   static size_t offsetOfLength() { return getFixedSlotOffset(LENGTH_SLOT); }
 
  private:
   static inline bool init(JSContext* cx, Handle<StringObject*> obj,
                           HandleString str);
 
-  static JSObject* createPrototype(JSContext* cx, JSProtoKey key);
-
   void setStringThis(JSString* str) {
     MOZ_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined());
     setFixedSlot(PRIMITIVE_VALUE_SLOT, StringValue(str));
     setFixedSlot(LENGTH_SLOT, Int32Value(int32_t(str->length())));
   }
+
+  /* For access to init, as String.prototype is special. */
+  friend JSObject* js::InitStringClass(JSContext* cx,
+                                       Handle<GlobalObject*> global);
 };
 
 }  // namespace js
 
 #endif /* vm_StringObject_h */
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -678,26 +678,16 @@ static bool GetLimits(JSContext* cx, Han
         }
       }
     }
   }
 
   return true;
 }
 
-template <class Class, const char* name>
-static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
-  RootedAtom className(cx, Atomize(cx, name, strlen(name)));
-  if (!className) {
-    return nullptr;
-  }
-
-  return NewNativeConstructor(cx, Class::construct, 1, className);
-}
-
 // ============================================================================
 // WebAssembly.Module class and methods
 
 const JSClassOps WasmModuleObject::classOps_ = {nullptr, /* addProperty */
                                                 nullptr, /* delProperty */
                                                 nullptr, /* enumerate */
                                                 nullptr, /* newEnumerate */
                                                 nullptr, /* resolve */
@@ -705,36 +695,19 @@ const JSClassOps WasmModuleObject::class
                                                 WasmModuleObject::finalize};
 
 const JSClass WasmModuleObject::class_ = {
     "WebAssembly.Module",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmModuleObject::classOps_,
-    &WasmModuleObject::classSpec_,
 };
 
-const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
-
-static constexpr char WasmModuleName[] = "Module";
-
-const ClassSpec WasmModuleObject::classSpec_ = {
-    CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
-    GenericCreatePrototype<WasmModuleObject>,
-    WasmModuleObject::static_methods,
-    nullptr,
-    WasmModuleObject::methods,
-    WasmModuleObject::properties,
-    nullptr,
-    ClassSpec::DontDefineConstructor};
-
-const JSPropertySpec WasmModuleObject::properties[] = {
-    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
-    JS_PS_END};
+const JSPropertySpec WasmModuleObject::properties[] = {JS_PS_END};
 
 const JSFunctionSpec WasmModuleObject::methods[] = {JS_FS_END};
 
 const JSFunctionSpec WasmModuleObject::static_methods[] = {
     JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
     JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
     JS_FN("customSections", WasmModuleObject::customSections, 2,
           JSPROP_ENUMERATE),
@@ -1253,33 +1226,18 @@ const JSClassOps WasmInstanceObject::cla
                                                   WasmInstanceObject::trace};
 
 const JSClass WasmInstanceObject::class_ = {
     "WebAssembly.Instance",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmInstanceObject::classOps_,
-    &WasmInstanceObject::classSpec_,
 };
 
-const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
-
-static constexpr char WasmInstanceName[] = "Instance";
-
-const ClassSpec WasmInstanceObject::classSpec_ = {
-    CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
-    GenericCreatePrototype<WasmInstanceObject>,
-    WasmInstanceObject::static_methods,
-    nullptr,
-    WasmInstanceObject::methods,
-    WasmInstanceObject::properties,
-    nullptr,
-    ClassSpec::DontDefineConstructor};
-
 static bool IsInstance(HandleValue v) {
   return v.isObject() && v.toObject().is<WasmInstanceObject>();
 }
 
 /* static */
 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
                                            const CallArgs& args) {
   args.rval().setObject(
@@ -1291,17 +1249,16 @@ bool WasmInstanceObject::exportsGetterIm
 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
                                        Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
 }
 
 const JSPropertySpec WasmInstanceObject::properties[] = {
     JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
-    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
     JS_PS_END};
 
 const JSFunctionSpec WasmInstanceObject::methods[] = {JS_FS_END};
 
 const JSFunctionSpec WasmInstanceObject::static_methods[] = {JS_FS_END};
 
 bool WasmInstanceObject::isNewborn() const {
   MOZ_ASSERT(is<WasmInstanceObject>());
@@ -1694,31 +1651,17 @@ const JSClassOps WasmMemoryObject::class
                                                 nullptr, /* mayResolve */
                                                 WasmMemoryObject::finalize};
 
 const JSClass WasmMemoryObject::class_ = {
     "WebAssembly.Memory",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
-    &WasmMemoryObject::classOps_, &WasmMemoryObject::classSpec_};
-
-const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_;
-
-static constexpr char WasmMemoryName[] = "Memory";
-
-const ClassSpec WasmMemoryObject::classSpec_ = {
-    CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>,
-    GenericCreatePrototype<WasmMemoryObject>,
-    WasmMemoryObject::static_methods,
-    nullptr,
-    WasmMemoryObject::methods,
-    WasmMemoryObject::properties,
-    nullptr,
-    ClassSpec::DontDefineConstructor};
+    &WasmMemoryObject::classOps_};
 
 /* static */
 void WasmMemoryObject::finalize(JSFreeOp* fop, JSObject* obj) {
   WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
   if (memory.hasObservers()) {
     fop->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers);
   }
 }
@@ -1822,17 +1765,16 @@ bool WasmMemoryObject::bufferGetterImpl(
 /* static */
 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
 }
 
 const JSPropertySpec WasmMemoryObject::properties[] = {
     JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE),
-    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY),
     JS_PS_END};
 
 /* static */
 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) {
   RootedWasmMemoryObject memory(
       cx, &args.thisv().toObject().as<WasmMemoryObject>());
 
   if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) {
@@ -2061,31 +2003,17 @@ const JSClassOps WasmTableObject::classO
                                                nullptr, /* construct */
                                                WasmTableObject::trace};
 
 const JSClass WasmTableObject::class_ = {
     "WebAssembly.Table",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
-    &WasmTableObject::classOps_, &WasmTableObject::classSpec_};
-
-const JSClass& WasmTableObject::protoClass_ = PlainObject::class_;
-
-static constexpr char WasmTableName[] = "Table";
-
-const ClassSpec WasmTableObject::classSpec_ = {
-    CreateWasmConstructor<WasmTableObject, WasmTableName>,
-    GenericCreatePrototype<WasmTableObject>,
-    WasmTableObject::static_methods,
-    nullptr,
-    WasmTableObject::methods,
-    WasmTableObject::properties,
-    nullptr,
-    ClassSpec::DontDefineConstructor};
+    &WasmTableObject::classOps_};
 
 bool WasmTableObject::isNewborn() const {
   MOZ_ASSERT(is<WasmTableObject>());
   return getReservedSlot(TABLE_SLOT).isUndefined();
 }
 
 /* static */
 void WasmTableObject::finalize(JSFreeOp* fop, JSObject* obj) {
@@ -2229,17 +2157,16 @@ bool WasmTableObject::lengthGetterImpl(J
 /* static */
 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
 }
 
 const JSPropertySpec WasmTableObject::properties[] = {
     JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE),
-    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY),
     JS_PS_END};
 
 static bool ToTableIndex(JSContext* cx, HandleValue v, const Table& table,
                          const char* noun, uint32_t* index) {
   if (!EnforceRangeU32(cx, v, "Table", noun, index)) {
     return false;
   }
 
@@ -2450,31 +2377,17 @@ const JSClassOps WasmGlobalObject::class
                                                 nullptr, /* hasInstance */
                                                 nullptr, /* construct */
                                                 WasmGlobalObject::trace};
 
 const JSClass WasmGlobalObject::class_ = {
     "WebAssembly.Global",
     JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
         JSCLASS_BACKGROUND_FINALIZE,
-    &WasmGlobalObject::classOps_, &WasmGlobalObject::classSpec_};
-
-const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_;
-
-static constexpr char WasmGlobalName[] = "Global";
-
-const ClassSpec WasmGlobalObject::classSpec_ = {
-    CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>,
-    GenericCreatePrototype<WasmGlobalObject>,
-    WasmGlobalObject::static_methods,
-    nullptr,
-    WasmGlobalObject::methods,
-    WasmGlobalObject::properties,
-    nullptr,
-    ClassSpec::DontDefineConstructor};
+    &WasmGlobalObject::classOps_};
 
 /* static */
 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
   WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
   if (global->isNewborn()) {
     // This can happen while we're allocating the object, in which case
     // every single slot of the object is not defined yet. In particular,
     // there's nothing to trace yet.
@@ -2793,17 +2706,16 @@ bool WasmGlobalObject::valueSetterImpl(J
 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
 }
 
 const JSPropertySpec WasmGlobalObject::properties[] = {
     JS_PSGS("value", WasmGlobalObject::valueGetter,
             WasmGlobalObject::valueSetter, JSPROP_ENUMERATE),
-    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY),
     JS_PS_END};
 
 const JSFunctionSpec WasmGlobalObject::methods[] = {
     JS_FN(js_valueOf_str, WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE),
     JS_FS_END};
 
 const JSFunctionSpec WasmGlobalObject::static_methods[] = {JS_FS_END};
 
@@ -3788,74 +3700,151 @@ static const JSFunctionSpec WebAssembly_
     JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE),
     JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE),
     JS_FN("compileStreaming", WebAssembly_compileStreaming, 1,
           JSPROP_ENUMERATE),
     JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1,
           JSPROP_ENUMERATE),
     JS_FS_END};
 
-static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
+const JSClass js::WebAssemblyClass = {
+    js_WebAssembly_str, JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly)};
+
+template <class Class>
+static bool InitConstructor(JSContext* cx, HandleObject wasm, const char* name,
+                            MutableHandleObject proto) {
+  proto.set(NewBuiltinClassInstance<PlainObject>(cx, SingletonObject));
+  if (!proto) {
+    return false;
+  }
+
+  if (!DefinePropertiesAndFunctions(cx, proto, Class::properties,
+                                    Class::methods)) {
+    return false;
+  }
+
+  RootedAtom className(cx, Atomize(cx, name, strlen(name)));
+  if (!className) {
+    return false;
+  }
+
+  RootedFunction ctor(cx,
+                      NewNativeConstructor(cx, Class::construct, 1, className));
+  if (!ctor) {
+    return false;
+  }
+
+  if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, Class::static_methods)) {
+    return false;
+  }
+
+  if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
+    return false;
+  }
+
+  UniqueChars tagStr(JS_smprintf("WebAssembly.%s", name));
+  if (!tagStr) {
+    ReportOutOfMemory(cx);
+    return false;
+  }
+
+  RootedAtom tag(cx, Atomize(cx, tagStr.get(), strlen(tagStr.get())));
+  if (!tag) {
+    return false;
+  }
+  if (!DefineToStringTag(cx, proto, tag)) {
+    return false;
+  }
+
+  RootedId id(cx, AtomToId(className));
+  RootedValue ctorValue(cx, ObjectValue(*ctor));
+  return DefineDataProperty(cx, wasm, id, ctorValue, 0);
+}
+
+static bool InitErrorClass(JSContext* cx, HandleObject wasm, const char* name,
+                           JSExnType exn) {
+  Handle<GlobalObject*> global = cx->global();
+  RootedObject proto(
+      cx, GlobalObject::getOrCreateCustomErrorPrototype(cx, global, exn));
+  if (!proto) {
+    return false;
+  }
+
+  RootedAtom className(cx, Atomize(cx, name, strlen(name)));
+  if (!className) {
+    return false;
+  }
+
+  RootedId id(cx, AtomToId(className));
+  RootedValue ctorValue(cx, global->getConstructor(GetExceptionProtoKey(exn)));
+  return DefineDataProperty(cx, wasm, id, ctorValue, 0);
+}
+
+JSObject* js::InitWebAssemblyClass(JSContext* cx,
+                                   Handle<GlobalObject*> global) {
   MOZ_RELEASE_ASSERT(HasSupport(cx));
 
-  Handle<GlobalObject*> global = cx->global();
+  MOZ_ASSERT(!global->isStandardClassResolved(JSProto_WebAssembly));
+
   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
-  return NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject);
+
+  RootedObject wasm(cx, NewObjectWithGivenProto(cx, &WebAssemblyClass, proto,
+                                                SingletonObject));
+  if (!wasm) {
+    return nullptr;
+  }
+
+  if (!JS_DefineFunctions(cx, wasm, WebAssembly_static_methods)) {
+    return nullptr;
+  }
+
+  RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx),
+      tableProto(cx);
+  RootedObject globalProto(cx);
+  if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto)) {
+    return nullptr;
+  }
+  if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance",
+                                           &instanceProto)) {
+    return nullptr;
+  }
+  if (!InitConstructor<WasmMemoryObject>(cx, wasm, "Memory", &memoryProto)) {
+    return nullptr;
+  }
+  if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto)) {
+    return nullptr;
+  }
+  if (!InitConstructor<WasmGlobalObject>(cx, wasm, "Global", &globalProto)) {
+    return nullptr;
+  }
+  if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR)) {
+    return nullptr;
+  }
+  if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR)) {
+    return nullptr;
+  }
+  if (!InitErrorClass(cx, wasm, "RuntimeError", JSEXN_WASMRUNTIMEERROR)) {
+    return nullptr;
+  }
+
+  // Perform the final fallible write of the WebAssembly object to a global
+  // object property at the end. Only after that succeeds write all the
+  // constructor and prototypes to the JSProto slots. This ensures that
+  // initialization is atomic since a failed initialization can be retried.
+
+  if (!JS_DefineProperty(cx, global, js_WebAssembly_str, wasm,
+                         JSPROP_RESOLVING)) {
+    return nullptr;
+  }
+
+  global->setPrototype(JSProto_WasmModule, ObjectValue(*moduleProto));
+  global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
+  global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
+  global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
+  global->setPrototype(JSProto_WasmGlobal, ObjectValue(*globalProto));
+  global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
+
+  MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));
+  return wasm;
 }
-
-static bool WebAssemblyClassFinish(JSContext* cx, HandleObject wasm,
-                                   HandleObject proto) {
-  struct NameAndProtoKey {
-    const char* const name;
-    JSProtoKey key;
-  };
-
-  constexpr NameAndProtoKey entries[] = {
-      {"Module", JSProto_WasmModule},
-      {"Instance", JSProto_WasmInstance},
-      {"Memory", JSProto_WasmMemory},
-      {"Table", JSProto_WasmTable},
-      {"Global", JSProto_WasmGlobal},
-      {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
-      {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
-      {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
-  };
-
-  RootedValue ctorValue(cx);
-  RootedId id(cx);
-  for (const auto& entry : entries) {
-    const char* name = entry.name;
-    JSProtoKey key = entry.key;
-
-    JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, key);
-    if (!ctor) {
-      return false;
-    }
-    ctorValue.setObject(*ctor);
-
-    JSAtom* className = Atomize(cx, name, strlen(name));
-    if (!className) {
-      return false;
-    }
-    id.set(AtomToId(className));
-
-    if (!DefineDataProperty(cx, wasm, id, ctorValue, 0)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-static const ClassSpec WebAssemblyClassSpec = {CreateWebAssemblyObject,
-                                               nullptr,
-                                               WebAssembly_static_methods,
-                                               nullptr,
-                                               nullptr,
-                                               nullptr,
-                                               WebAssemblyClassFinish};
-
-const JSClass js::WebAssemblyClass = {
-    js_WebAssembly_str, JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly),
-    JS_NULL_CLASS_OPS, &WebAssemblyClassSpec};
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -22,16 +22,17 @@
 #include "gc/Policy.h"
 #include "gc/ZoneAllocator.h"
 #include "vm/NativeObject.h"
 #include "wasm/WasmTypes.h"
 
 namespace js {
 
 class ArrayBufferObjectMaybeShared;
+class GlobalObject;
 class StructTypeDescr;
 class TypedArrayObject;
 class WasmFunctionScope;
 class WasmInstanceScope;
 class SharedArrayRawBuffer;
 
 namespace wasm {
 
@@ -117,33 +118,33 @@ uint32_t ExportedFunctionToFuncIndex(JSF
 bool IsSharedWasmMemoryObject(JSObject* obj);
 
 }  // namespace wasm
 
 // The class of the WebAssembly global namespace object.
 
 extern const JSClass WebAssemblyClass;
 
+JSObject* InitWebAssemblyClass(JSContext* cx, Handle<GlobalObject*> global);
+
 // The class of WebAssembly.Module. Each WasmModuleObject owns a
 // wasm::Module. These objects are used both as content-facing JS objects and as
 // internal implementation details of asm.js.
 
 class WasmModuleObject : public NativeObject {
   static const unsigned MODULE_SLOT = 0;
   static const JSClassOps classOps_;
-  static const ClassSpec classSpec_;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static bool imports(JSContext* cx, unsigned argc, Value* vp);
   static bool exports(JSContext* cx, unsigned argc, Value* vp);
   static bool customSections(JSContext* cx, unsigned argc, Value* vp);
 
  public:
   static const unsigned RESERVED_SLOTS = 1;
   static const JSClass class_;
-  static const JSClass& protoClass_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
   static bool construct(JSContext*, unsigned, Value*);
 
   static WasmModuleObject* create(JSContext* cx, const wasm::Module& module,
                                   HandleObject proto = nullptr);
   const wasm::Module& module() const;
@@ -161,17 +162,16 @@ class WasmModuleObject : public NativeOb
 STATIC_ASSERT_ANYREF_IS_JSOBJECT;
 
 class WasmGlobalObject : public NativeObject {
   static const unsigned TYPE_SLOT = 0;
   static const unsigned MUTABLE_SLOT = 1;
   static const unsigned CELL_SLOT = 2;
 
   static const JSClassOps classOps_;
-  static const ClassSpec classSpec_;
   static void finalize(JSFreeOp*, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
 
   static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
   static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
   static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
 
@@ -185,17 +185,16 @@ class WasmGlobalObject : public NativeOb
     double f64;
     wasm::AnyRef ref;
     Cell() : i64(0) {}
     ~Cell() {}
   };
 
   static const unsigned RESERVED_SLOTS = 3;
   static const JSClass class_;
-  static const JSClass& protoClass_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
   static bool construct(JSContext*, unsigned, Value*);
 
   static WasmGlobalObject* create(JSContext* cx, wasm::HandleVal value,
                                   bool isMutable);
   bool isNewborn() { return getReservedSlot(CELL_SLOT).isUndefined(); }
@@ -216,17 +215,16 @@ class WasmInstanceObject : public Native
   static const unsigned INSTANCE_SLOT = 0;
   static const unsigned EXPORTS_OBJ_SLOT = 1;
   static const unsigned EXPORTS_SLOT = 2;
   static const unsigned SCOPES_SLOT = 3;
   static const unsigned INSTANCE_SCOPE_SLOT = 4;
   static const unsigned GLOBALS_SLOT = 5;
 
   static const JSClassOps classOps_;
-  static const ClassSpec classSpec_;
   static bool exportsGetterImpl(JSContext* cx, const CallArgs& args);
   static bool exportsGetter(JSContext* cx, unsigned argc, Value* vp);
   bool isNewborn() const;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
 
   // ExportMap maps from function index to exported function object.
   // This allows the instance to lazily create exported function
@@ -242,17 +240,16 @@ class WasmInstanceObject : public Native
   using ScopeMap =
       JS::WeakCache<GCHashMap<uint32_t, WeakHeapPtr<WasmFunctionScope*>,
                               DefaultHasher<uint32_t>, ZoneAllocPolicy>>;
   ScopeMap& scopes() const;
 
  public:
   static const unsigned RESERVED_SLOTS = 6;
   static const JSClass class_;
-  static const JSClass& protoClass_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
   static bool construct(JSContext*, unsigned, Value*);
 
   static WasmInstanceObject* create(
       JSContext* cx, RefPtr<const wasm::Code> code,
       const wasm::DataSegmentVector& dataSegments,
@@ -291,17 +288,16 @@ class WasmInstanceObject : public Native
 
 // The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
 // or SharedArrayBuffer object which owns the actual memory.
 
 class WasmMemoryObject : public NativeObject {
   static const unsigned BUFFER_SLOT = 0;
   static const unsigned OBSERVERS_SLOT = 1;
   static const JSClassOps classOps_;
-  static const ClassSpec classSpec_;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
   static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool growImpl(JSContext* cx, const CallArgs& args);
   static bool grow(JSContext* cx, unsigned argc, Value* vp);
   static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta);
 
   using InstanceSet =
@@ -310,17 +306,16 @@ class WasmMemoryObject : public NativeOb
                               ZoneAllocPolicy>>;
   bool hasObservers() const;
   InstanceSet& observers() const;
   InstanceSet* getOrCreateObservers(JSContext* cx);
 
  public:
   static const unsigned RESERVED_SLOTS = 2;
   static const JSClass class_;
-  static const JSClass& protoClass_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
   static bool construct(JSContext*, unsigned, Value*);
 
   static WasmMemoryObject* create(JSContext* cx,
                                   Handle<ArrayBufferObjectMaybeShared*> buffer,
                                   HandleObject proto);
@@ -354,33 +349,31 @@ class WasmMemoryObject : public NativeOb
 
 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
 // wasm::Table, allowing a Table to be shared between multiple Instances
 // (eventually between multiple threads).
 
 class WasmTableObject : public NativeObject {
   static const unsigned TABLE_SLOT = 0;
   static const JSClassOps classOps_;
-  static const ClassSpec classSpec_;
   bool isNewborn() const;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
   static bool lengthGetterImpl(JSContext* cx, const CallArgs& args);
   static bool lengthGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool getImpl(JSContext* cx, const CallArgs& args);
   static bool get(JSContext* cx, unsigned argc, Value* vp);
   static bool setImpl(JSContext* cx, const CallArgs& args);
   static bool set(JSContext* cx, unsigned argc, Value* vp);
   static bool growImpl(JSContext* cx, const CallArgs& args);
   static bool grow(JSContext* cx, unsigned argc, Value* vp);
 
  public:
   static const unsigned RESERVED_SLOTS = 1;
   static const JSClass class_;
-  static const JSClass& protoClass_;
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSFunctionSpec static_methods[];
   static bool construct(JSContext*, unsigned, Value*);
 
   // Note that, after creation, a WasmTableObject's table() is not initialized
   // and must be initialized before use.