Bug 1328386 - Part 8: Store internals object for Intl objects in internal slot instead of using a WeakMap. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 23 Jan 2017 08:33:51 -0800
changeset 331145 e15e0f265264c7ec5bdcb643f75cacbbc29f16ba
parent 331144 60adc4589abc3de850e379496a143dc6dc208cac
child 331146 f35fe2367a4d3a2d912e217ca5259fb595b3f145
push id31261
push usercbook@mozilla.com
push dateThu, 26 Jan 2017 11:32:02 +0000
treeherdermozilla-central@a338e596b1d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1328386
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1328386 - Part 8: Store internals object for Intl objects in internal slot instead of using a WeakMap. r=Waldo
js/src/builtin/Intl.cpp
js/src/builtin/Intl.h
js/src/builtin/Intl.js
js/src/builtin/SelfHostingDefines.h
js/src/js.msg
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1016,16 +1016,17 @@ Collator(JSContext* cx, const CallArgs& 
         if (!proto)
             return false;
     }
 
     Rooted<CollatorObject*> collator(cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
     if (!collator)
         return false;
 
+    collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
     collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
 
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 6.
     if (!IntlInitialize(cx, collator, cx->names().InitializeCollator, locales, options))
         return false;
@@ -1074,16 +1075,17 @@ CreateCollatorPrototype(JSContext* cx, H
                                                             0));
     if (!ctor)
         return nullptr;
 
     Rooted<CollatorObject*> proto(cx);
     proto = GlobalObject::createBlankPrototype<CollatorObject>(cx, global);
     if (!proto)
         return nullptr;
+    proto->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
     proto->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(nullptr));
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     // 10.2.2
     if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
         return nullptr;
@@ -1464,16 +1466,17 @@ NumberFormat(JSContext* cx, const CallAr
             return false;
     }
 
     Rooted<NumberFormatObject*> numberFormat(cx);
     numberFormat = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
     if (!numberFormat)
         return false;
 
+    numberFormat->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
     numberFormat->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
 
     RootedValue thisValue(cx, construct ? ObjectValue(*numberFormat) : args.thisv());
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 3.
     return LegacyIntlInitialize(cx, numberFormat, cx->names().InitializeNumberFormat, thisValue,
@@ -1524,16 +1527,17 @@ CreateNumberFormatPrototype(JSContext* c
     ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
     if (!ctor)
         return nullptr;
 
     RootedNativeObject proto(cx);
     proto = GlobalObject::createBlankPrototype<NumberFormatObject>(cx, global);
     if (!proto)
         return nullptr;
+    proto->setReservedSlot(NumberFormatObject::INTERNALS_SLOT, NullValue());
     proto->setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     // 11.3.2
     if (!JS_DefineFunctions(cx, ctor, numberFormat_static_methods))
         return nullptr;
@@ -2422,16 +2426,17 @@ DateTimeFormat(JSContext* cx, const Call
             return false;
     }
 
     Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
     dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
     if (!dateTimeFormat)
         return false;
 
+    dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
     dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
                                     PrivateValue(nullptr));
 
     RootedValue thisValue(cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 3.
@@ -2483,16 +2488,17 @@ CreateDateTimeFormatPrototype(JSContext*
     ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
     if (!ctor)
         return nullptr;
 
     Rooted<DateTimeFormatObject*> proto(cx);
     proto = GlobalObject::createBlankPrototype<DateTimeFormatObject>(cx, global);
     if (!proto)
         return nullptr;
+    proto->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT, NullValue());
     proto->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(nullptr));
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     // 12.3.2
     if (!JS_DefineFunctions(cx, ctor, dateTimeFormat_static_methods))
         return nullptr;
@@ -3499,16 +3505,17 @@ PluralRules(JSContext* cx, unsigned argc
             return false;
     }
 
     Rooted<PluralRulesObject*> pluralRules(cx);
     pluralRules = NewObjectWithGivenProto<PluralRulesObject>(cx, proto);
     if (!pluralRules)
         return false;
 
+    pluralRules->setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
     pluralRules->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
 
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 3.
     if (!IntlInitialize(cx, pluralRules, cx->names().InitializePluralRules, locales, options))
         return false;
@@ -3541,16 +3548,17 @@ CreatePluralRulesPrototype(JSContext* cx
     ctor = global->createConstructor(cx, &PluralRules, cx->names().PluralRules, 0);
     if (!ctor)
         return nullptr;
 
     Rooted<PluralRulesObject*> proto(cx);
     proto = GlobalObject::createBlankPrototype<PluralRulesObject>(cx, global);
     if (!proto)
         return nullptr;
+    proto->setReservedSlot(PluralRulesObject::INTERNALS_SLOT, NullValue());
     proto->setReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT, PrivateValue(nullptr));
 
     if (!LinkConstructorAndPrototype(cx, ctor, proto))
         return nullptr;
 
     if (!JS_DefineFunctions(cx, ctor, pluralRules_static_methods))
         return nullptr;
 
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -8,16 +8,17 @@
 #define builtin_Intl_h
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jsalloc.h"
 #include "NamespaceImports.h"
 
+#include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
 #include "js/GCAPI.h"
 #include "js/GCHashTable.h"
 
 #if ENABLE_INTL_API
 #include "unicode/utypes.h"
 #endif
 
@@ -182,18 +183,22 @@ class SharedIntlData
 
 /******************** Collator ********************/
 
 class CollatorObject : public NativeObject
 {
   public:
     static const Class class_;
 
-    static constexpr uint32_t UCOLLATOR_SLOT = 0;
-    static constexpr uint32_t SLOT_COUNT = 1;
+    static constexpr uint32_t INTERNALS_SLOT = 0;
+    static constexpr uint32_t UCOLLATOR_SLOT = 1;
+    static constexpr uint32_t SLOT_COUNT = 2;
+
+    static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
+                  "INTERNALS_SLOT must match self-hosting define for internals object slot");
 
   private:
     static const ClassOps classOps_;
 
     static void finalize(FreeOp* fop, JSObject* obj);
 };
 
 /**
@@ -244,18 +249,22 @@ intl_CompareStrings(JSContext* cx, unsig
 
 /******************** NumberFormat ********************/
 
 class NumberFormatObject : public NativeObject
 {
   public:
     static const Class class_;
 
-    static constexpr uint32_t UNUMBER_FORMAT_SLOT = 0;
-    static constexpr uint32_t SLOT_COUNT = 1;
+    static constexpr uint32_t INTERNALS_SLOT = 0;
+    static constexpr uint32_t UNUMBER_FORMAT_SLOT = 1;
+    static constexpr uint32_t SLOT_COUNT = 2;
+
+    static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
+                  "INTERNALS_SLOT must match self-hosting define for internals object slot");
 
   private:
     static const ClassOps classOps_;
 
     static void finalize(FreeOp* fop, JSObject* obj);
 };
 
 /**
@@ -303,18 +312,22 @@ intl_FormatNumber(JSContext* cx, unsigne
 
 /******************** DateTimeFormat ********************/
 
 class DateTimeFormatObject : public NativeObject
 {
   public:
     static const Class class_;
 
-    static constexpr uint32_t UDATE_FORMAT_SLOT = 0;
-    static constexpr uint32_t SLOT_COUNT = 1;
+    static constexpr uint32_t INTERNALS_SLOT = 0;
+    static constexpr uint32_t UDATE_FORMAT_SLOT = 1;
+    static constexpr uint32_t SLOT_COUNT = 2;
+
+    static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
+                  "INTERNALS_SLOT must match self-hosting define for internals object slot");
 
   private:
     static const ClassOps classOps_;
 
     static void finalize(FreeOp* fop, JSObject* obj);
 };
 
 /**
@@ -413,18 +426,22 @@ intl_FormatDateTime(JSContext* cx, unsig
 
 /******************** PluralRules ********************/
 
 class PluralRulesObject : public NativeObject
 {
   public:
     static const Class class_;
 
-    static constexpr uint32_t UPLURAL_RULES_SLOT = 0;
-    static constexpr uint32_t SLOT_COUNT = 1;
+    static constexpr uint32_t INTERNALS_SLOT = 0;
+    static constexpr uint32_t UPLURAL_RULES_SLOT = 1;
+    static constexpr uint32_t SLOT_COUNT = 2;
+
+    static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
+                  "INTERNALS_SLOT must match self-hosting define for internals object slot");
 
   private:
     static const ClassOps classOps_;
 
     static void finalize(FreeOp* fop, JSObject* obj);
 };
 
 /**
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -2,17 +2,17 @@
  * 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/. */
 
 /* Portions Copyright Norbert Lindenberg 2011-2012. */
 
 /*global JSMSG_INTL_OBJECT_NOT_INITED: false, JSMSG_INVALID_LOCALES_ELEMENT: false,
          JSMSG_INVALID_LANGUAGE_TAG: false, JSMSG_INVALID_LOCALE_MATCHER: false,
          JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false,
-         JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false,
+         JSMSG_INVALID_CURRENCY_CODE: false,
          JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false,
          JSMSG_DATE_NOT_FINITE: false, JSMSG_INVALID_KEYS_TYPE: false,
          JSMSG_INVALID_KEY: false,
          intl_Collator_availableLocales: false,
          intl_availableCollations: false,
          intl_CompareStrings: false,
          intl_NumberFormat_availableLocales: false,
          intl_numberingSystem: false,
@@ -1212,177 +1212,117 @@ function intlFallbackSymbol() {
     var fallbackSymbol = intlFallbackSymbolHolder.value;
     if (!fallbackSymbol)
         intlFallbackSymbolHolder.value = fallbackSymbol = std_Symbol();
     return fallbackSymbol;
 }
 
 
 /**
- * Weak map used to track the initialize-as-Intl status (and, if an object has
- * been so initialized, the Intl-specific internal properties) of all objects.
- * Presence of an object as a key within this map indicates that the object has
- * its [[initializedIntlObject]] internal property set to true.  The associated
- * value is an object whose structure is documented in |initializeIntlObject|
- * below.
- *
- * Ideally we'd be using private symbols for internal properties, but
- * SpiderMonkey doesn't have those yet.
+ * Initializes the INTL_INTERNALS_OBJECT_SLOT of the given object.
  */
-var internalsMap = new WeakMap();
-
-
-/**
- * Set the [[initializedIntlObject]] internal property of |obj| to true.
- */
-function initializeIntlObject(obj) {
+function initializeIntlObject(obj, type, lazyData) {
     assert(IsObject(obj), "Non-object passed to initializeIntlObject");
-
-    // Intl-initialized objects are weird.  They have [[initializedIntlObject]]
-    // set on them, but they don't *necessarily* have any other properties.
-
-    var internals = std_Object_create(null);
+    assert((type === "Collator" && IsCollator(obj)) ||
+           (type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+           (type === "NumberFormat" && IsNumberFormat(obj)) ||
+           (type === "PluralRules" && IsPluralRules(obj)),
+           "type must match the object's class");
+    assert(IsObject(lazyData), "non-object lazy data");
 
     // The meaning of an internals object for an object |obj| is as follows.
     //
-    // If the .type is "partial", |obj| has [[initializedIntlObject]] set but
-    // nothing else.  No other property of |internals| can be used.  (This
-    // occurs when InitializeCollator or similar marks an object as
-    // [[initializedIntlObject]] but fails before marking it as the appropriate
-    // more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
+    // The .type property indicates the type of Intl object that |obj| is:
+    // "Collator", "DateTimeFormat", "NumberFormat", or "PluralRules" (likely
+    // with more coming in future Intl specs).
     //
-    // Otherwise, the .type indicates the type of Intl object that |obj| is:
-    // "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
-    // in future Intl specs).  In these cases |obj| *conceptually* also has
-    // [[initializedCollator]] or similar set, and all the other properties
-    // implied by that.
-    //
-    // If |internals| doesn't have a "partial" .type, two additional properties
-    // have meaning.  The .lazyData property stores information needed to
-    // compute -- without observable side effects -- the actual internal Intl
-    // properties of |obj|.  If it is non-null, then the actual internal
-    // properties haven't been computed, and .lazyData must be processed by
+    // The .lazyData property stores information needed to compute -- without
+    // observable side effects -- the actual internal Intl properties of
+    // |obj|.  If it is non-null, then the actual internal properties haven't
+    // been computed, and .lazyData must be processed by
     // |setInternalProperties| before internal Intl property values are
     // available.  If it is null, then the .internalProps property contains an
     // object whose properties are the internal Intl properties of |obj|.
 
-    internals.type = "partial";
-    internals.lazyData = null;
+    var internals = std_Object_create(null);
+    internals.type = type;
+    internals.lazyData = lazyData;
     internals.internalProps = null;
 
-    callFunction(std_WeakMap_set, internalsMap, obj, internals);
-    return internals;
-}
-
-
-/**
- * Mark |internals| as having the given type and lazy data.
- */
-function setLazyData(internals, type, lazyData)
-{
-    assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
-    assert(type === "Collator" || type === "DateTimeFormat" ||
-           type == "NumberFormat" || type === "PluralRules",
-           "bad type");
-    assert(IsObject(lazyData), "non-object lazy data");
-
-    // Set in reverse order so that the .type change is a barrier.
-    internals.lazyData = lazyData;
-    internals.type = type;
+    assert(UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT) === null,
+           "Internal slot already initialized?");
+    UnsafeSetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT, internals);
 }
 
 
 /**
  * Set the internal properties object for an |internals| object previously
  * associated with lazy data.
  */
-function setInternalProperties(internals, internalProps)
-{
-    assert(internals.type !== "partial", "newborn internals can't have computed internals");
+function setInternalProperties(internals, internalProps) {
     assert(IsObject(internals.lazyData), "lazy data must exist already");
     assert(IsObject(internalProps), "internalProps argument should be an object");
 
     // Set in reverse order so that the .lazyData nulling is a barrier.
     internals.internalProps = internalProps;
     internals.lazyData = null;
 }
 
 
 /**
  * Get the existing internal properties out of a non-newborn |internals|, or
  * null if none have been computed.
  */
-function maybeInternalProperties(internals)
-{
+function maybeInternalProperties(internals) {
     assert(IsObject(internals), "non-object passed to maybeInternalProperties");
-    assert(internals.type !== "partial", "maybeInternalProperties must only be used on completely-initialized internals objects");
     var lazyData = internals.lazyData;
     if (lazyData)
         return null;
     assert(IsObject(internals.internalProps), "missing lazy data and computed internals");
     return internals.internalProps;
 }
 
 
 /**
- * Return whether |obj| has an[[initializedIntlObject]] property set to true.
- */
-function isInitializedIntlObject(obj) {
-#ifdef DEBUG
-    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
-    if (IsObject(internals)) {
-        assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
-        var type = internals.type;
-        assert(type === "partial" || type === "Collator" ||
-               type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
-               "unexpected type");
-        assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
-        assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
-    } else {
-        assert(internals === undefined, "bad mapping for |obj|");
-    }
-#endif
-    return callFunction(std_WeakMap_has, internalsMap, obj);
-}
-
-
-/**
- * Check that |obj| meets the requirements for "this Collator object", "this
- * NumberFormat object", or "this DateTimeFormat object" as used in the method
- * with the given name.  Throw a TypeError if |obj| doesn't meet these
- * requirements.  But if it does, return |obj|'s internals object (*not* the
- * object holding its internal properties!), associated with it by
- * |internalsMap|, with structure specified above.
+ * Return |obj|'s internals object (*not* the object holding its internal
+ * properties!), with structure specified above.
  *
  * Spec: ECMAScript Internationalization API Specification, 10.3.
  * Spec: ECMAScript Internationalization API Specification, 11.3.
  * Spec: ECMAScript Internationalization API Specification, 12.3.
  */
-function getIntlObjectInternals(obj, className, methodName) {
-    assert(typeof className === "string", "bad className for getIntlObjectInternals");
-
-    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
-    assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
-
-    if (internals === undefined || internals.type !== className)
-        ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
+function getIntlObjectInternals(obj) {
+    assert(IsObject(obj), "getIntlObjectInternals called with non-Object");
+    assert(IsCollator(obj) || IsDateTimeFormat(obj) || IsNumberFormat(obj) || IsPluralRules(obj),
+           "getIntlObjectInternals called with non-Intl object");
+
+    var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
+
+    assert(IsObject(internals), "internals not an object");
+    assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
+    assert((internals.type === "Collator" && IsCollator(obj)) ||
+           (internals.type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+           (internals.type === "NumberFormat" && IsNumberFormat(obj)) ||
+           (internals.type === "PluralRules" && IsPluralRules(obj)),
+           "type must match the object's class");
+    assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"),
+           "missing lazyData");
+    assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"),
+           "missing internalProps");
 
     return internals;
 }
 
 
 /**
  * Get the internal properties of known-Intl object |obj|.  For use only by
  * C++ code that knows what it's doing!
  */
-function getInternals(obj)
-{
-    assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
-
-    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
+function getInternals(obj) {
+    var internals = getIntlObjectInternals(obj);
 
     // If internal properties have already been computed, use them.
     var internalProps = maybeInternalProperties(internals);
     if (internalProps)
         return internalProps;
 
     // Otherwise it's time to fully create them.
     var type = internals.type;
@@ -1500,21 +1440,21 @@ function resolveCollatorInternals(lazyCo
     // object using |setInternalProperties|.
     return internalProps;
 }
 
 
 /**
  * Returns an object containing the Collator internal properties of |obj|.
  */
-function getCollatorInternals(obj, methodName) {
+function getCollatorInternals(obj) {
     assert(IsObject(obj), "getCollatorInternals called with non-object");
     assert(IsCollator(obj), "getCollatorInternals called with non-Collator");
 
-    var internals = getIntlObjectInternals(obj, "Collator", methodName);
+    var internals = getIntlObjectInternals(obj);
     assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
 
     // If internal properties have already been computed, use them.
     var internalProps = maybeInternalProperties(internals);
     if (internalProps)
         return internalProps;
 
     // Otherwise it's time to fully create them.
@@ -1536,20 +1476,16 @@ function getCollatorInternals(obj, metho
  * Spec: ECMAScript Internationalization API Specification, 10.1.1.
  */
 function InitializeCollator(collator, locales, options) {
     assert(IsObject(collator), "InitializeCollator called with non-object");
     assert(IsCollator(collator), "InitializeCollator called with non-Collator");
 
     // Steps 1-2 (These steps are no longer required and should be removed
     // from the spec; https://github.com/tc39/ecma402/issues/115).
-    assert(!isInitializedIntlObject(collator), "collator mustn't be initialized");
-
-    // Step 2.
-    var internals = initializeIntlObject(collator);
 
     // Lazy Collator data has the following structure:
     //
     //   {
     //     requestedLocales: List of locales,
     //     usage: "sort" / "search",
     //     opt: // opt object computed in InitializeCollator
     //       {
@@ -1613,17 +1549,17 @@ function InitializeCollator(collator, lo
     // Step 23.
     var ip = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
     lazyCollatorData.ignorePunctuation = ip;
 
     // Step 26.
     //
     // We've done everything that must be done now: mark the lazy data as fully
     // computed and install it.
-    setLazyData(internals, "Collator", lazyCollatorData);
+    initializeIntlObject(collator, "Collator", lazyCollatorData);
 }
 
 
 /**
  * Returns the subset of the given locale list for which this locale list has a
  * matching (possibly fallback) locale. Locales appear in the same order in the
  * returned list as in the input list.
  *
@@ -1708,17 +1644,17 @@ function collatorCompareToBind(x, y) {
  *
  * Spec: ECMAScript Internationalization API Specification, 10.3.2.
  */
 function Intl_Collator_compare_get() {
     // Check "this Collator object" per introduction of section 10.3.
     if (!IsObject(this) || !IsCollator(this))
         ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "compare", "Collator");
 
-    var internals = getCollatorInternals(this, "compare");
+    var internals = getCollatorInternals(this);
 
     // Step 1.
     if (internals.boundCompare === undefined) {
         // Step 1.a.
         var F = collatorCompareToBind;
 
         // Steps 1.b-d.
         var bc = callFunction(FunctionBind, F, this);
@@ -1736,17 +1672,17 @@ function Intl_Collator_compare_get() {
  *
  * Spec: ECMAScript Internationalization API Specification, 10.3.3 and 10.4.
  */
 function Intl_Collator_resolvedOptions() {
     // Check "this Collator object" per introduction of section 10.3.
     if (!IsObject(this) || !IsCollator(this))
         ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "Collator", "resolvedOptions", "Collator");
 
-    var internals = getCollatorInternals(this, "resolvedOptions");
+    var internals = getCollatorInternals(this);
 
     var result = {
         locale: internals.locale,
         usage: internals.usage,
         sensitivity: internals.sensitivity,
         ignorePunctuation: internals.ignorePunctuation
     };
 
@@ -1849,21 +1785,21 @@ function resolveNumberFormatInternals(la
     // object using |setInternalProperties|.
     return internalProps;
 }
 
 
 /**
  * Returns an object containing the NumberFormat internal properties of |obj|.
  */
-function getNumberFormatInternals(obj, methodName) {
+function getNumberFormatInternals(obj) {
     assert(IsObject(obj), "getNumberFormatInternals called with non-object");
     assert(IsNumberFormat(obj), "getNumberFormatInternals called with non-NumberFormat");
 
-    var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
+    var internals = getIntlObjectInternals(obj);
     assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
 
     // If internal properties have already been computed, use them.
     var internalProps = maybeInternalProperties(internals);
     if (internalProps)
         return internalProps;
 
     // Otherwise it's time to fully create them.
@@ -1942,20 +1878,16 @@ function SetNumberFormatDigitOptions(laz
  * Spec: ECMAScript Internationalization API Specification, 11.1.1.
  */
 function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
     assert(IsObject(numberFormat), "InitializeNumberFormat called with non-object");
     assert(IsNumberFormat(numberFormat), "InitializeNumberFormat called with non-NumberFormat");
 
     // Steps 1-2 (These steps are no longer required and should be removed
     // from the spec; https://github.com/tc39/ecma402/issues/115).
-    assert(!isInitializedIntlObject(numberFormat), "numberFormat mustn't be initialized");
-
-    // Step 2.
-    var internals = initializeIntlObject(numberFormat);
 
     // Lazy NumberFormat data has the following structure:
     //
     //   {
     //     requestedLocales: List of locales,
     //     style: "decimal" / "percent" / "currency",
     //
     //     // fields present only if style === "currency":
@@ -2047,17 +1979,17 @@ function InitializeNumberFormat(numberFo
     // Steps 26.
     var g = GetOption(options, "useGrouping", "boolean", undefined, true);
     lazyNumberFormatData.useGrouping = g;
 
     // Steps 35-36.
     //
     // We've done everything that must be done now: mark the lazy data as fully
     // computed and install it.
-    setLazyData(internals, "NumberFormat", lazyNumberFormatData);
+    initializeIntlObject(numberFormat, "NumberFormat", lazyNumberFormatData);
 
     if (numberFormat !== thisValue && thisValue instanceof GetNumberFormatConstructor()) {
         if (!IsObject(thisValue))
             ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof thisValue);
 
         _DefineDataProperty(thisValue, intlFallbackSymbol(), numberFormat,
                             ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
 
@@ -2159,17 +2091,17 @@ function numberFormatFormatToBind(value)
  * effective locale and the formatting options of this NumberFormat.
  *
  * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  */
 function Intl_NumberFormat_format_get() {
     // Steps 1-3.
     var nf = UnwrapNumberFormat(this, "format");
 
-    var internals = getNumberFormatInternals(nf, "format");
+    var internals = getNumberFormatInternals(nf);
 
     // Step 4.
     if (internals.boundFormat === undefined) {
         // Step 4.a.
         var F = numberFormatFormatToBind;
 
         // Steps 4.b-d.
         var bf = callFunction(FunctionBind, F, nf);
@@ -2182,17 +2114,17 @@ function Intl_NumberFormat_format_get() 
 _SetCanonicalName(Intl_NumberFormat_format_get, "get format");
 
 
 function Intl_NumberFormat_formatToParts(value) {
     // Steps 1-3.
     var nf = UnwrapNumberFormat(this, "formatToParts");
 
     // Ensure the NumberFormat internals are resolved.
-    getNumberFormatInternals(nf, "formatToParts");
+    getNumberFormatInternals(nf);
 
     // Step 4.
     var x = ToNumber(value);
 
     // Step 5.
     return intl_FormatNumber(nf, x, /* formatToParts = */ true);
 }
 
@@ -2201,17 +2133,17 @@ function Intl_NumberFormat_formatToParts
  * Returns the resolved options for a NumberFormat object.
  *
  * Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
  */
 function Intl_NumberFormat_resolvedOptions() {
     // Invoke |UnwrapNumberFormat| per introduction of section 11.3.
     var nf = UnwrapNumberFormat(this, "resolvedOptions");
 
-    var internals = getNumberFormatInternals(nf, "resolvedOptions");
+    var internals = getNumberFormatInternals(nf);
 
     var result = {
         locale: internals.locale,
         numberingSystem: internals.numberingSystem,
         style: internals.style,
         minimumIntegerDigits: internals.minimumIntegerDigits,
         minimumFractionDigits: internals.minimumFractionDigits,
         maximumFractionDigits: internals.maximumFractionDigits,
@@ -2312,21 +2244,21 @@ function resolveDateTimeFormatInternals(
     // object using |setInternalProperties|.
     return internalProps;
 }
 
 
 /**
  * Returns an object containing the DateTimeFormat internal properties of |obj|.
  */
-function getDateTimeFormatInternals(obj, methodName) {
+function getDateTimeFormatInternals(obj) {
     assert(IsObject(obj), "getDateTimeFormatInternals called with non-object");
     assert(IsDateTimeFormat(obj), "getDateTimeFormatInternals called with non-DateTimeFormat");
 
-    var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
+    var internals = getIntlObjectInternals(obj);
     assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
 
     // If internal properties have already been computed, use them.
     var internalProps = maybeInternalProperties(internals);
     if (internalProps)
         return internalProps;
 
     // Otherwise it's time to fully create them.
@@ -2371,20 +2303,16 @@ function UnwrapDateTimeFormat(dtf, metho
  */
 function InitializeDateTimeFormat(dateTimeFormat, thisValue, locales, options) {
     assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat called with non-Object");
     assert(IsDateTimeFormat(dateTimeFormat),
            "InitializeDateTimeFormat called with non-DateTimeFormat");
 
     // Steps 1-2 (These steps are no longer required and should be removed
     // from the spec; https://github.com/tc39/ecma402/issues/115).
-    assert(!isInitializedIntlObject(dateTimeFormat), "dateTimeFormat mustn't be initialized");
-
-    // Step 2.
-    var internals = initializeIntlObject(dateTimeFormat);
 
     // Lazy DateTimeFormat data has the following structure:
     //
     //   {
     //     requestedLocales: List of locales,
     //
     //     localeOpt: // *first* opt computed in InitializeDateTimeFormat
     //       {
@@ -2485,17 +2413,17 @@ function InitializeDateTimeFormat(dateTi
     // Pass hr12 on to ICU.
     if (hr12 !== undefined)
         formatOpt.hour12 = hr12;
 
     // Step 31.
     //
     // We've done everything that must be done now: mark the lazy data as fully
     // computed and install it.
-    setLazyData(internals, "DateTimeFormat", lazyDateTimeFormatData);
+    initializeIntlObject(dateTimeFormat, "DateTimeFormat", lazyDateTimeFormatData);
 
     if (dateTimeFormat !== thisValue && thisValue instanceof GetDateTimeFormatConstructor()) {
         if (!IsObject(thisValue))
             ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, typeof thisValue);
 
         _DefineDataProperty(thisValue, intlFallbackSymbol(), dateTimeFormat,
                             ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
 
@@ -2910,17 +2838,17 @@ function dateTimeFormatFormatToBind() {
  * effective locale and the formatting options of this DateTimeFormat.
  *
  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  */
 function Intl_DateTimeFormat_format_get() {
     // Steps 1-3.
     var dtf = UnwrapDateTimeFormat(this, "format");
 
-    var internals = getDateTimeFormatInternals(dtf, "format");
+    var internals = getDateTimeFormatInternals(dtf);
 
     // Step 4.
     if (internals.boundFormat === undefined) {
         // Step 4.a.
         var F = dateTimeFormatFormatToBind;
 
         // Steps 4.b-d.
         var bf = callFunction(FunctionBind, F, dtf);
@@ -2933,17 +2861,17 @@ function Intl_DateTimeFormat_format_get(
 _SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
 
 
 function Intl_DateTimeFormat_formatToParts() {
     // Steps 1-3.
     var dtf = UnwrapDateTimeFormat(this, "formatToParts");
 
     // Ensure the DateTimeFormat internals are resolved.
-    getDateTimeFormatInternals(dtf, "formatToParts");
+    getDateTimeFormatInternals(dtf);
 
     // Steps 4-5.
     var date = arguments.length > 0 ? arguments[0] : undefined;
     var x = (date === undefined) ? std_Date_now() : ToNumber(date);
 
     // Step 6.
     return intl_FormatDateTime(dtf, x, /* formatToParts = */ true);
 }
@@ -2953,17 +2881,17 @@ function Intl_DateTimeFormat_formatToPar
  * Returns the resolved options for a DateTimeFormat object.
  *
  * Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
  */
 function Intl_DateTimeFormat_resolvedOptions() {
     // Invoke |UnwrapDateTimeFormat| per introduction of section 12.3.
     var dtf = UnwrapDateTimeFormat(this, "resolvedOptions");
 
-    var internals = getDateTimeFormatInternals(dtf, "resolvedOptions");
+    var internals = getDateTimeFormatInternals(dtf);
 
     var result = {
         locale: internals.locale,
         calendar: internals.calendar,
         numberingSystem: internals.numberingSystem,
         timeZone: internals.timeZone
     };
     resolveICUPattern(internals.pattern, result);
@@ -3131,21 +3059,21 @@ function resolvePluralRulesInternals(laz
     }
 
     return internalProps;
 }
 
 /**
  * Returns an object containing the PluralRules internal properties of |obj|.
  */
-function getPluralRulesInternals(obj, methodName) {
+function getPluralRulesInternals(obj) {
     assert(IsObject(obj), "getPluralRulesInternals called with non-object");
     assert(IsPluralRules(obj), "getPluralRulesInternals called with non-PluralRules");
 
-    var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
+    var internals = getIntlObjectInternals(obj);
     assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
 
     var internalProps = maybeInternalProperties(internals);
     if (internalProps)
         return internalProps;
 
     internalProps = resolvePluralRulesInternals(internals.lazyData);
     setInternalProperties(internals, internalProps);
@@ -3164,19 +3092,16 @@ function getPluralRulesInternals(obj, me
  * Spec: ECMAScript 402 API, PluralRules, 1.1.1.
  */
 function InitializePluralRules(pluralRules, locales, options) {
     assert(IsObject(pluralRules), "InitializePluralRules called with non-object");
     assert(IsPluralRules(pluralRules), "InitializePluralRules called with non-PluralRules");
 
     // Steps 1-2 (These steps are no longer required and should be removed
     // from the spec; https://github.com/tc39/ecma402/issues/115).
-    assert(!isInitializedIntlObject(pluralRules), "pluralRules mustn't be initialized");
-
-    let internals = initializeIntlObject(pluralRules);
 
     // Lazy PluralRules data has the following structure:
     //
     //   {
     //     requestedLocales: List of locales,
     //     type: "cardinal" / "ordinal",
     //
     //     opt: // opt object computer in InitializePluralRules
@@ -3218,17 +3143,17 @@ function InitializePluralRules(pluralRul
 
     // Steps 9-10.
     let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
     opt.localeMatcher = matcher;
 
     // Steps 11-12.
     SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3);
 
-    setLazyData(internals, "PluralRules", lazyPluralRulesData)
+    initializeIntlObject(pluralRules, "PluralRules", lazyPluralRulesData);
 }
 
 /**
  * Returns the subset of the given locale list for which this locale list has a
  * matching (possibly fallback) locale. Locales appear in the same order in the
  * returned list as in the input list.
  *
  * Spec: ECMAScript 402 API, PluralRules, 1.3.2.
@@ -3257,17 +3182,17 @@ function Intl_PluralRules_select(value) 
     // Step 1.
     let pluralRules = this;
 
     // Step 2.
     if (!IsObject(pluralRules) || !IsPluralRules(pluralRules))
         ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "select", "PluralRules");
 
     // Ensure the PluralRules internals are resolved.
-    getPluralRulesInternals(pluralRules, "select");
+    getPluralRulesInternals(pluralRules);
 
     // Steps 3-4.
     let n = ToNumber(value);
 
     // Step 5.
     return intl_SelectPluralRule(pluralRules, n);
 }
 
@@ -3278,17 +3203,17 @@ function Intl_PluralRules_select(value) 
  */
 function Intl_PluralRules_resolvedOptions() {
     // Check "this PluralRules object" per introduction of section 1.4.
     if (!IsObject(this) || !IsPluralRules(this)) {
         ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, "PluralRules", "resolvedOptions",
                        "PluralRules");
     }
 
-    var internals = getPluralRulesInternals(this, "resolvedOptions");
+    var internals = getPluralRulesInternals(this);
 
     var result = {
         locale: internals.locale,
         type: internals.type,
         pluralCategories: callFunction(std_Array_slice, internals.pluralCategories, 0),
         minimumIntegerDigits: internals.minimumIntegerDigits,
         minimumFractionDigits: internals.minimumFractionDigits,
         maximumFractionDigits: internals.maximumFractionDigits,
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -119,9 +119,11 @@
 #define STRING_GENERICS_TO_LOCALE_LOWER_CASE  18
 #define STRING_GENERICS_TO_LOCALE_UPPER_CASE  19
 #define STRING_GENERICS_TO_UPPER_CASE         20
 #define STRING_GENERICS_TRIM                  21
 #define STRING_GENERICS_TRIM_LEFT             22
 #define STRING_GENERICS_TRIM_RIGHT            23
 #define STRING_GENERICS_METHODS_LIMIT         24
 
+#define INTL_INTERNALS_OBJECT_SLOT 0
+
 #endif
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -469,17 +469,16 @@ MSG_DEF(JSMSG_DEBUG_PROMISE_NOT_REJECTED
 
 // Tracelogger
 MSG_DEF(JSMSG_TRACELOGGER_ENABLE_FAIL, 1, JSEXN_ERR, "enabling tracelogger failed: {0}")
 
 // Intl
 MSG_DEF(JSMSG_DATE_NOT_FINITE,         0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_INTERNAL_INTL_ERROR,     0, JSEXN_ERR, "internal error while computing Intl data")
 MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED,  3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1} called on value that's not an object initialized as a {2}")
-MSG_DEF(JSMSG_INTL_OBJECT_REINITED,    0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
 MSG_DEF(JSMSG_INVALID_CURRENCY_CODE,   1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
 MSG_DEF(JSMSG_INVALID_DIGITS_VALUE,    1, JSEXN_RANGEERR, "invalid digits value: {0}")
 MSG_DEF(JSMSG_INVALID_KEYS_TYPE,       0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
 MSG_DEF(JSMSG_INVALID_KEY,             1, JSEXN_RANGEERR, "invalid key: {0}")
 MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG,    1, JSEXN_RANGEERR, "invalid language tag: {0}")
 MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
 MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER,  1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")
 MSG_DEF(JSMSG_INVALID_OPTION_VALUE,    2, JSEXN_RANGEERR, "invalid value {1} for option {0}")