Bug 1328386 - Part 2: Add a NativeObject subclass for each Intl object. r=Waldo
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 23 Jan 2017 08:33:30 -0800
changeset 331044 36ad679ec7dbdb6923ff0db88d0e9b720296527c
parent 331043 862bc2b2553e0a65ac080e49d4279f4faba4bc76
child 331045 16d0d9e90e254865c501d1fcb924a103305dcd53
push id86156
push userryanvm@gmail.com
push dateWed, 25 Jan 2017 22:49:02 +0000
treeherdermozilla-inbound@f35fe2367a4d [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 2: Add a NativeObject subclass for each Intl object. r=Waldo
js/src/builtin/Intl.cpp
js/src/builtin/Intl.h
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -919,37 +919,32 @@ class ScopedICUObject
 };
 
 // The inline capacity we use for the char16_t Vectors.
 static const size_t INITIAL_CHAR_BUFFER_SIZE = 32;
 
 
 /******************** Collator ********************/
 
-static void collator_finalize(FreeOp* fop, JSObject* obj);
-
-static const uint32_t UCOLLATOR_SLOT = 0;
-static const uint32_t COLLATOR_SLOTS_COUNT = 1;
-
-static const ClassOps CollatorClassOps = {
+const ClassOps CollatorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    collator_finalize
+    CollatorObject::finalize
 };
 
-static const Class CollatorClass = {
+const Class CollatorObject::class_ = {
     js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(COLLATOR_SLOTS_COUNT) |
+    JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
     JSCLASS_FOREGROUND_FINALIZE,
-    &CollatorClassOps
+    &CollatorObject::classOps_
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 collator_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().Collator);
@@ -1020,21 +1015,22 @@ Collator(JSContext* cx, const CallArgs& 
             return false;
 
         if (!proto) {
             proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
             if (!proto)
                 return false;
         }
 
-        obj = NewObjectWithGivenProto(cx, &CollatorClass, proto);
+        obj = NewObjectWithGivenProto<CollatorObject>(cx, proto);
         if (!obj)
             return false;
 
-        obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
+        obj->as<CollatorObject>().setReservedSlot(CollatorObject::UCOLLATOR_SLOT,
+                                                  PrivateValue(nullptr));
     }
 
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 6.
     if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
         return false;
@@ -1056,44 +1052,45 @@ js::intl_Collator(JSContext* cx, unsigne
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(!args.isConstructing());
     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
     // be used with "new", but it still has to be treated as a constructor.
     return Collator(cx, args, true);
 }
 
-static void
-collator_finalize(FreeOp* fop, JSObject* obj)
+void
+CollatorObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
 
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
-    const Value& slot = obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT);
+    const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
     if (!slot.isUndefined()) {
         if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
             ucol_close(coll);
     }
 }
 
 static JSObject*
 CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
                                                             0));
     if (!ctor)
         return nullptr;
 
-    RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &CollatorClass));
+    Rooted<CollatorObject*> proto(cx);
+    proto = GlobalObject::createBlankPrototype<CollatorObject>(cx, global);
     if (!proto)
         return nullptr;
-    proto->setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
+    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;
 
@@ -1382,26 +1379,29 @@ js::intl_CompareStrings(JSContext* cx, u
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isString());
 
     RootedObject collator(cx, &args[0].toObject());
 
     // Obtain a UCollator object, cached if possible.
     // XXX Does this handle Collator instances from other globals correctly?
-    bool isCollatorInstance = collator->getClass() == &CollatorClass;
+    bool isCollatorInstance = collator->is<CollatorObject>();
     UCollator* coll;
     if (isCollatorInstance) {
-        void* priv = collator->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate();
+        void* priv =
+            collator->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT)
+                                          .toPrivate();
         coll = static_cast<UCollator*>(priv);
         if (!coll) {
             coll = NewUCollator(cx, collator);
             if (!coll)
                 return false;
-            collator->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(coll));
+            collator->as<CollatorObject>().setReservedSlot(CollatorObject::UCOLLATOR_SLOT,
+                                                           PrivateValue(coll));
         }
     } else {
         // There's no good place to cache the ICU collator for an object
         // that has been initialized as a Collator but is not a Collator
         // instance. One possibility might be to add a Collator instance as an
         // internal property to each such object.
         coll = NewUCollator(cx, collator);
         if (!coll)
@@ -1420,37 +1420,32 @@ js::intl_CompareStrings(JSContext* cx, u
         return false;
     args.rval().set(result);
     return true;
 }
 
 
 /******************** NumberFormat ********************/
 
-static void numberFormat_finalize(FreeOp* fop, JSObject* obj);
-
-static const uint32_t UNUMBER_FORMAT_SLOT = 0;
-static const uint32_t NUMBER_FORMAT_SLOTS_COUNT = 1;
-
-static const ClassOps NumberFormatClassOps = {
+const ClassOps NumberFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    numberFormat_finalize
+    NumberFormatObject::finalize
 };
 
-static const Class NumberFormatClass = {
+const Class NumberFormatObject::class_ = {
     js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(NUMBER_FORMAT_SLOTS_COUNT) |
+    JSCLASS_HAS_RESERVED_SLOTS(NumberFormatObject::SLOT_COUNT) |
     JSCLASS_FOREGROUND_FINALIZE,
-    &NumberFormatClassOps
+    &NumberFormatObject::classOps_
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().NumberFormat);
@@ -1521,21 +1516,22 @@ NumberFormat(JSContext* cx, const CallAr
             return false;
 
         if (!proto) {
             proto = GlobalObject::getOrCreateNumberFormatPrototype(cx, cx->global());
             if (!proto)
                 return false;
         }
 
-        obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto);
+        obj = NewObjectWithGivenProto<NumberFormatObject>(cx, proto);
         if (!obj)
             return false;
 
-        obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
+        obj->as<NumberFormatObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT,
+                                                      PrivateValue(nullptr));
     }
 
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 3.
     if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
         return false;
@@ -1558,45 +1554,46 @@ js::intl_NumberFormat(JSContext* cx, uns
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(!args.isConstructing());
     // intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
-static void
-numberFormat_finalize(FreeOp* fop, JSObject* obj)
+void
+NumberFormatObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
 
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
-    const Value& slot = obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT);
+    const Value& slot =
+        obj->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT);
     if (!slot.isUndefined()) {
         if (UNumberFormat* nf = static_cast<UNumberFormat*>(slot.toPrivate()))
             unum_close(nf);
     }
 }
 
 static JSObject*
 CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx);
     ctor = GlobalObject::createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0);
     if (!ctor)
         return nullptr;
 
-    RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
-                                                                    &NumberFormatClass));
+    RootedNativeObject proto(cx);
+    proto = GlobalObject::createBlankPrototype<NumberFormatObject>(cx, global);
     if (!proto)
         return nullptr;
-    proto->setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
+    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;
 
@@ -2381,27 +2378,29 @@ js::intl_FormatNumber(JSContext* cx, uns
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[1].isNumber());
     MOZ_ASSERT(args[2].isBoolean());
 
     RootedObject numberFormat(cx, &args[0].toObject());
 
     // Obtain a UNumberFormat object, cached if possible.
-    bool isNumberFormatInstance = numberFormat->getClass() == &NumberFormatClass;
+    bool isNumberFormatInstance = numberFormat->is<NumberFormatObject>();
     UNumberFormat* nf;
     if (isNumberFormatInstance) {
         void* priv =
-            numberFormat->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate();
+            numberFormat->as<NumberFormatObject>().getReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT)
+                                                  .toPrivate();
         nf = static_cast<UNumberFormat*>(priv);
         if (!nf) {
             nf = NewUNumberFormat(cx, numberFormat);
             if (!nf)
                 return false;
-            numberFormat->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nf));
+            numberFormat->as<NumberFormatObject>().setReservedSlot(NumberFormatObject::UNUMBER_FORMAT_SLOT,
+                                                                   PrivateValue(nf));
         }
     } else {
         // There's no good place to cache the ICU number format for an object
         // that has been initialized as a NumberFormat but is not a
         // NumberFormat instance. One possibility might be to add a
         // NumberFormat instance as an internal property to each such object.
         nf = NewUNumberFormat(cx, numberFormat);
         if (!nf)
@@ -2431,37 +2430,32 @@ js::intl_FormatNumber(JSContext* cx, uns
         return false;
     args.rval().set(result);
     return true;
 }
 
 
 /******************** DateTimeFormat ********************/
 
-static void dateTimeFormat_finalize(FreeOp* fop, JSObject* obj);
-
-static const uint32_t UDATE_FORMAT_SLOT = 0;
-static const uint32_t DATE_TIME_FORMAT_SLOTS_COUNT = 1;
-
-static const ClassOps DateTimeFormatClassOps = {
+const ClassOps DateTimeFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    dateTimeFormat_finalize
+    DateTimeFormatObject::finalize
 };
 
-static const Class DateTimeFormatClass = {
+const Class DateTimeFormatObject::class_ = {
     js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(DATE_TIME_FORMAT_SLOTS_COUNT) |
+    JSCLASS_HAS_RESERVED_SLOTS(DateTimeFormatObject::SLOT_COUNT) |
     JSCLASS_FOREGROUND_FINALIZE,
-    &DateTimeFormatClassOps
+    &DateTimeFormatObject::classOps_
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 dateTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().DateTimeFormat);
@@ -2533,21 +2527,22 @@ DateTimeFormat(JSContext* cx, const Call
             return false;
 
         if (!proto) {
             proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
             if (!proto)
                 return false;
         }
 
-        obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto);
+        obj = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
         if (!obj)
             return false;
 
-        obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
+        obj->as<DateTimeFormatObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
+                                                        PrivateValue(nullptr));
     }
 
     RootedValue locales(cx, args.get(0));
     RootedValue options(cx, args.get(1));
 
     // Step 3.
     if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
         return false;
@@ -2570,45 +2565,46 @@ js::intl_DateTimeFormat(JSContext* cx, u
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(!args.isConstructing());
     // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
-static void
-dateTimeFormat_finalize(FreeOp* fop, JSObject* obj)
+void
+DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
 
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
-    const Value& slot = obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT);
+    const Value& slot =
+        obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
     if (!slot.isUndefined()) {
         if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
             udat_close(df);
     }
 }
 
 static JSObject*
 CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx);
     ctor = GlobalObject::createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0);
     if (!ctor)
         return nullptr;
 
-    RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
-                                                                    &DateTimeFormatClass));
+    Rooted<DateTimeFormatObject*> proto(cx);
+    proto = GlobalObject::createBlankPrototype<DateTimeFormatObject>(cx, global);
     if (!proto)
         return nullptr;
-    proto->setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
+    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;
 
@@ -3522,27 +3518,29 @@ js::intl_FormatDateTime(JSContext* cx, u
     MOZ_ASSERT(args.length() == 3);
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[1].isNumber());
     MOZ_ASSERT(args[2].isBoolean());
 
     RootedObject dateTimeFormat(cx, &args[0].toObject());
 
     // Obtain a UDateFormat object, cached if possible.
-    bool isDateTimeFormatInstance = dateTimeFormat->getClass() == &DateTimeFormatClass;
+    bool isDateTimeFormatInstance = dateTimeFormat->is<DateTimeFormatObject>();
     UDateFormat* df;
     if (isDateTimeFormatInstance) {
         void* priv =
-            dateTimeFormat->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate();
+            dateTimeFormat->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT)
+                                                      .toPrivate();
         df = static_cast<UDateFormat*>(priv);
         if (!df) {
             df = NewUDateFormat(cx, dateTimeFormat);
             if (!df)
                 return false;
-            dateTimeFormat->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(df));
+            dateTimeFormat->as<DateTimeFormatObject>().setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
+                                                                       PrivateValue(df));
         }
     } else {
         // There's no good place to cache the ICU date-time format for an object
         // that has been initialized as a DateTimeFormat but is not a
         // DateTimeFormat instance. One possibility might be to add a
         // DateTimeFormat instance as an internal property to each such object.
         df = NewUDateFormat(cx, dateTimeFormat);
         if (!df)
@@ -3561,37 +3559,32 @@ js::intl_FormatDateTime(JSContext* cx, u
         return false;
     args.rval().set(result);
     return true;
 }
 
 
 /**************** PluralRules *****************/
 
-static void pluralRules_finalize(FreeOp* fop, JSObject* obj);
-
-static const uint32_t UPLURAL_RULES_SLOT = 0;
-static const uint32_t PLURAL_RULES_SLOTS_COUNT = 1;
-
-static const ClassOps PluralRulesClassOps = {
+const ClassOps PluralRulesObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
-    pluralRules_finalize
+    PluralRulesObject::finalize
 };
 
-static const Class PluralRulesClass = {
+const Class PluralRulesObject::class_ = {
     js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(PLURAL_RULES_SLOTS_COUNT) |
+    JSCLASS_HAS_RESERVED_SLOTS(PluralRulesObject::SLOT_COUNT) |
     JSCLASS_FOREGROUND_FINALIZE,
-    &PluralRulesClassOps
+    &PluralRulesObject::classOps_
 };
 
 #if JS_HAS_TOSOURCE
 static bool
 pluralRules_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().PluralRules);
@@ -3630,62 +3623,64 @@ PluralRules(JSContext* cx, unsigned argc
         return false;
 
     if (!proto) {
         proto = GlobalObject::getOrCreatePluralRulesPrototype(cx, cx->global());
         if (!proto)
             return false;
     }
 
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &PluralRulesClass, proto));
-    if (!obj)
+    Rooted<PluralRulesObject*> pluralRules(cx);
+    pluralRules = NewObjectWithGivenProto<PluralRulesObject>(cx, proto);
+    if (!pluralRules)
         return false;
 
-    obj->as<NativeObject>().setReservedSlot(UPLURAL_RULES_SLOT, PrivateValue(nullptr));
+    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, obj, cx->names().InitializePluralRules, locales, options))
+    if (!IntlInitialize(cx, pluralRules, cx->names().InitializePluralRules, locales, options))
         return false;
 
-    args.rval().setObject(*obj);
+    args.rval().setObject(*pluralRules);
     return true;
 }
 
-static void
-pluralRules_finalize(FreeOp* fop, JSObject* obj)
+void
+PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
 
     // This is-undefined check shouldn't be necessary, but for internal
     // brokenness in object allocation code.  For the moment, hack around it by
     // explicitly guarding against the possibility of the reserved slot not
     // containing a private.  See bug 949220.
-    const Value& slot = obj->as<NativeObject>().getReservedSlot(UPLURAL_RULES_SLOT);
+    const Value& slot =
+        obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
     if (!slot.isUndefined()) {
         if (UPluralRules* pr = static_cast<UPluralRules*>(slot.toPrivate()))
             uplrules_close(pr);
     }
 }
 
 static JSObject*
 CreatePluralRulesPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx);
     ctor = global->createConstructor(cx, &PluralRules, cx->names().PluralRules, 0);
     if (!ctor)
         return nullptr;
 
-    RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
-                                                                    &PluralRulesClass));
+    Rooted<PluralRulesObject*> proto(cx);
+    proto = GlobalObject::createBlankPrototype<PluralRulesObject>(cx, global);
     if (!proto)
         return nullptr;
-    proto->setReservedSlot(UPLURAL_RULES_SLOT, PrivateValue(nullptr));
+    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;
 
     if (!JS_DefineFunctions(cx, proto, pluralRules_methods))
@@ -3712,17 +3707,17 @@ CreatePluralRulesPrototype(JSContext* cx
 js::GlobalObject::addPluralRulesConstructor(JSContext* cx, HandleObject intl)
 {
     Handle<GlobalObject*> global = cx->global();
 
     {
         const HeapSlot& slot = global->getReservedSlotRef(PLURAL_RULES_PROTO);
         if (!slot.isUndefined()) {
             MOZ_ASSERT(slot.isObject());
-            MOZ_ASSERT(slot.toObject().hasClass(&PluralRulesClass));
+            MOZ_ASSERT(slot.toObject().is<PluralRulesObject>());
             JS_ReportErrorASCII(cx,
                                 "the PluralRules constructor can't be added "
                                 "multiple times in the same global");
             return false;
         }
     }
 
     JSObject* pluralRulesProto = CreatePluralRulesPrototype(cx, intl, global);
@@ -3825,16 +3820,17 @@ js::intl_SelectPluralRule(JSContext* cx,
     UPluralType category;
     if (StringEqualsAscii(type, "cardinal")) {
         category = UPLURAL_TYPE_CARDINAL;
     } else {
         MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
         category = UPLURAL_TYPE_ORDINAL;
     }
 
+    // TODO: Cache UPluralRules in PluralRulesObject::UPluralRulesSlot.
     UPluralRules* pr = uplrules_openForType(icuLocale(locale.ptr()), category, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
 
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -8,32 +8,37 @@
 #define builtin_Intl_h
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jsalloc.h"
 #include "NamespaceImports.h"
 
+#include "js/Class.h"
 #include "js/GCAPI.h"
 #include "js/GCHashTable.h"
 
 #if ENABLE_INTL_API
 #include "unicode/utypes.h"
 #endif
 
+#include "vm/NativeObject.h"
+
 class JSLinearString;
 
 /*
  * The Intl module specified by standard ECMA-402,
  * ECMAScript Internationalization API Specification.
  */
 
 namespace js {
 
+class FreeOp;
+
 /**
  * Initializes the Intl Object and its standard built-in properties.
  * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  */
 extern JSObject*
 InitIntlClass(JSContext* cx, HandleObject obj);
 
 /**
@@ -172,16 +177,30 @@ class SharedIntlData
 
 /*
  * The following functions are for use by self-hosted code.
  */
 
 
 /******************** Collator ********************/
 
+class CollatorObject : public NativeObject
+{
+  public:
+    static const Class class_;
+
+    static constexpr uint32_t UCOLLATOR_SLOT = 0;
+    static constexpr uint32_t SLOT_COUNT = 1;
+
+  private:
+    static const ClassOps classOps_;
+
+    static void finalize(FreeOp* fop, JSObject* obj);
+};
+
 /**
  * Returns a new instance of the standard built-in Collator constructor.
  * Self-hosted code cannot cache this constructor (as it does for others in
  * Utilities.js) because it is initialized after self-hosted code is compiled.
  *
  * Usage: collator = intl_Collator(locales, options)
  */
 extern MOZ_MUST_USE bool
@@ -220,16 +239,30 @@ intl_availableCollations(JSContext* cx, 
  * Usage: result = intl_CompareStrings(collator, x, y)
  */
 extern MOZ_MUST_USE bool
 intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp);
 
 
 /******************** 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;
+
+  private:
+    static const ClassOps classOps_;
+
+    static void finalize(FreeOp* fop, JSObject* obj);
+};
+
 /**
  * Returns a new instance of the standard built-in NumberFormat constructor.
  * Self-hosted code cannot cache this constructor (as it does for others in
  * Utilities.js) because it is initialized after self-hosted code is compiled.
  *
  * Usage: numberFormat = intl_NumberFormat(locales, options)
  */
 extern MOZ_MUST_USE bool
@@ -265,16 +298,30 @@ intl_numberingSystem(JSContext* cx, unsi
  * Usage: formatted = intl_FormatNumber(numberFormat, x, formatToParts)
  */
 extern MOZ_MUST_USE bool
 intl_FormatNumber(JSContext* cx, unsigned argc, Value* vp);
 
 
 /******************** 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;
+
+  private:
+    static const ClassOps classOps_;
+
+    static void finalize(FreeOp* fop, JSObject* obj);
+};
+
 /**
  * Returns a new instance of the standard built-in DateTimeFormat constructor.
  * Self-hosted code cannot cache this constructor (as it does for others in
  * Utilities.js) because it is initialized after self-hosted code is compiled.
  *
  * Usage: dateTimeFormat = intl_DateTimeFormat(locales, options)
  */
 extern MOZ_MUST_USE bool
@@ -361,16 +408,30 @@ intl_patternForSkeleton(JSContext* cx, u
  * Usage: formatted = intl_FormatDateTime(dateTimeFormat, x, formatToParts)
  */
 extern MOZ_MUST_USE bool
 intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
 
 
 /******************** 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;
+
+  private:
+    static const ClassOps classOps_;
+
+    static void finalize(FreeOp* fop, JSObject* obj);
+};
+
 /**
  * Returns an object indicating the supported locales for plural rules
  * by having a true-valued property for each such locale with the
  * canonicalized language tag as the property name. The object has no
  * prototype.
  *
  * Usage: availableLocales = intl_PluralRules_availableLocales()
  */