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 466651 e15e0f265264c7ec5bdcb643f75cacbbc29f16ba
parent 466650 60adc4589abc3de850e379496a143dc6dc208cac
child 466652 f35fe2367a4d3a2d912e217ca5259fb595b3f145
push id42948
push userbmo:gasolin@mozilla.com
push dateThu, 26 Jan 2017 07:49:21 +0000
reviewersWaldo
bugs1328386
milestone54.0a1
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}")