Bug 1431957 - Move Intl.Collator functionality into builtin/intl/Collator.*. r=anba
authorJeff Walden <jwalden@mit.edu>
Fri, 19 Jan 2018 16:28:11 -0800
changeset 401028 cf6b00396d19491834dd557bc8a7e985f572478d
parent 401027 2aabca30def81d6bcc5bd2d5b9b7941afe67706b
child 401029 a671868ffa9caad5ff7b80ff8bb5199387378172
push id99283
push userjwalden@mit.edu
push dateSat, 27 Jan 2018 07:54:29 +0000
treeherdermozilla-inbound@c50cbf6e8176 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanba
bugs1431957
milestone60.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 1431957 - Move Intl.Collator functionality into builtin/intl/Collator.*. r=anba
js/src/builtin/Intl.cpp
js/src/builtin/Intl.h
js/src/builtin/intl/Collator.cpp
js/src/builtin/intl/Collator.h
js/src/jit/MCallOptimize.cpp
js/src/moz.build
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -23,16 +23,17 @@
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jsutil.h"
 
+#include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/IntlTimeZoneData.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "js/Date.h"
@@ -62,588 +63,16 @@ using JS::TimeClip;
 
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::INITIAL_CHAR_BUFFER_SIZE;
 using js::intl::StringsAreEqual;
 
-/******************** Collator ********************/
-
-const ClassOps CollatorObject::classOps_ = {
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* enumerate */
-    nullptr, /* newEnumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    CollatorObject::finalize
-};
-
-const Class CollatorObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
-    &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);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec collator_static_methods[] = {
-    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_Collator_supportedLocalesOf", 1, 0),
-    JS_FS_END
-};
-
-static const JSFunctionSpec collator_methods[] = {
-    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_Collator_resolvedOptions", 0, 0),
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str, collator_toSource, 0, 0),
-#endif
-    JS_FS_END
-};
-
-static const JSPropertySpec collator_properties[] = {
-    JS_SELF_HOSTED_GET("compare", "Intl_Collator_compare_get", 0),
-    JS_STRING_SYM_PS(toStringTag, "Object", JSPROP_READONLY),
-    JS_PS_END
-};
-
-/**
- * 10.1.2 Intl.Collator([ locales [, options]])
- *
- * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
- */
-static bool
-Collator(JSContext* cx, const CallArgs& args)
-{
-    // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
-
-    // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
-    RootedObject proto(cx);
-    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
-        return false;
-
-    if (!proto) {
-        proto = GlobalObject::getOrCreateCollatorPrototype(cx, cx->global());
-        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));
-
-    HandleValue locales = args.get(0);
-    HandleValue options = args.get(1);
-
-    // Step 6.
-    if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator, locales, options))
-        return false;
-
-    args.rval().setObject(*collator);
-    return true;
-}
-
-static bool
-Collator(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return Collator(cx, args);
-}
-
-bool
-js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(!args.isConstructing());
-
-    return Collator(cx, args);
-}
-
-void
-CollatorObject::finalize(FreeOp* fop, JSObject* obj)
-{
-    MOZ_ASSERT(fop->onActiveCooperatingThread());
-
-    const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
-    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;
-
-    RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
-    if (!proto)
-        return nullptr;
-
-    if (!LinkConstructorAndPrototype(cx, ctor, proto))
-        return nullptr;
-
-    // 10.2.2
-    if (!JS_DefineFunctions(cx, ctor, collator_static_methods))
-        return nullptr;
-
-    // 10.3.5
-    if (!JS_DefineFunctions(cx, proto, collator_methods))
-        return nullptr;
-
-    // 10.3.2 and 10.3.3
-    if (!JS_DefineProperties(cx, proto, collator_properties))
-        return nullptr;
-
-    // 8.1
-    RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineDataProperty(cx, Intl, cx->names().Collator, ctorValue, 0))
-        return nullptr;
-
-    return proto;
-}
-
-bool
-js::intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    RootedValue result(cx);
-    if (!GetAvailableLocales(cx, ucol_countAvailable, ucol_getAvailable, &result))
-        return false;
-    args.rval().set(result);
-    return true;
-}
-
-bool
-js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-    UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UEnumeration, uenum_close> toClose(values);
-
-    uint32_t count = uenum_count(values, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    RootedObject collations(cx, NewDenseEmptyArray(cx));
-    if (!collations)
-        return false;
-
-    uint32_t index = 0;
-
-    // The first element of the collations array must be |null| per
-    // ES2017 Intl, 10.2.3 Internal Slots.
-    if (!DefineDataElement(cx, collations, index++, NullHandleValue))
-        return false;
-
-    RootedValue element(cx);
-    for (uint32_t i = 0; i < count; i++) {
-        const char* collation = uenum_next(values, nullptr, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        // Per ECMA-402, 10.2.3, we don't include standard and search:
-        // "The values 'standard' and 'search' must not be used as elements in
-        // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
-        // array."
-        if (StringsAreEqual(collation, "standard") || StringsAreEqual(collation, "search"))
-            continue;
-
-        // ICU returns old-style keyword values; map them to BCP 47 equivalents.
-        JSString* jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation));
-        if (!jscollation)
-            return false;
-        element = StringValue(jscollation);
-        if (!DefineDataElement(cx, collations, index++, element))
-            return false;
-    }
-
-    args.rval().setObject(*collations);
-    return true;
-}
-
-/**
- * Returns a new UCollator with the locale and collation options
- * of the given Collator.
- */
-static UCollator*
-NewUCollator(JSContext* cx, Handle<CollatorObject*> collator)
-{
-    RootedValue value(cx);
-
-    RootedObject internals(cx, intl::GetInternalsObject(cx, collator));
-    if (!internals)
-        return nullptr;
-
-    if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
-        return nullptr;
-    JSAutoByteString locale(cx, value.toString());
-    if (!locale)
-        return nullptr;
-
-    // UCollator options with default values.
-    UColAttributeValue uStrength = UCOL_DEFAULT;
-    UColAttributeValue uCaseLevel = UCOL_OFF;
-    UColAttributeValue uAlternate = UCOL_DEFAULT;
-    UColAttributeValue uNumeric = UCOL_OFF;
-    // Normalization is always on to meet the canonical equivalence requirement.
-    UColAttributeValue uNormalization = UCOL_ON;
-    UColAttributeValue uCaseFirst = UCOL_DEFAULT;
-
-    if (!GetProperty(cx, internals, internals, cx->names().usage, &value))
-        return nullptr;
-
-    {
-        JSLinearString* usage = value.toString()->ensureLinear(cx);
-        if (!usage)
-            return nullptr;
-        if (StringEqualsAscii(usage, "search")) {
-            // ICU expects search as a Unicode locale extension on locale.
-            // Unicode locale extensions must occur before private use extensions.
-            const char* oldLocale = locale.ptr();
-            const char* p;
-            size_t index;
-            size_t localeLen = strlen(oldLocale);
-            if ((p = strstr(oldLocale, "-x-")))
-                index = p - oldLocale;
-            else
-                index = localeLen;
-
-            const char* insert;
-            if ((p = strstr(oldLocale, "-u-")) && static_cast<size_t>(p - oldLocale) < index) {
-                index = p - oldLocale + 2;
-                insert = "-co-search";
-            } else {
-                insert = "-u-co-search";
-            }
-            size_t insertLen = strlen(insert);
-            char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
-            if (!newLocale)
-                return nullptr;
-            memcpy(newLocale, oldLocale, index);
-            memcpy(newLocale + index, insert, insertLen);
-            memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
-            locale.clear();
-            locale.initBytes(JS::UniqueChars(newLocale));
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(usage, "sort"));
-        }
-    }
-
-    // We don't need to look at the collation property - it can only be set
-    // via the Unicode locale extension and is therefore already set on
-    // locale.
-
-    if (!GetProperty(cx, internals, internals, cx->names().sensitivity, &value))
-        return nullptr;
-
-    {
-        JSLinearString* sensitivity = value.toString()->ensureLinear(cx);
-        if (!sensitivity)
-            return nullptr;
-        if (StringEqualsAscii(sensitivity, "base")) {
-            uStrength = UCOL_PRIMARY;
-        } else if (StringEqualsAscii(sensitivity, "accent")) {
-            uStrength = UCOL_SECONDARY;
-        } else if (StringEqualsAscii(sensitivity, "case")) {
-            uStrength = UCOL_PRIMARY;
-            uCaseLevel = UCOL_ON;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(sensitivity, "variant"));
-            uStrength = UCOL_TERTIARY;
-        }
-    }
-
-    if (!GetProperty(cx, internals, internals, cx->names().ignorePunctuation, &value))
-        return nullptr;
-    // According to the ICU team, UCOL_SHIFTED causes punctuation to be
-    // ignored. Looking at Unicode Technical Report 35, Unicode Locale Data
-    // Markup Language, "shifted" causes whitespace and punctuation to be
-    // ignored - that's a bit more than asked for, but there's no way to get
-    // less.
-    if (value.toBoolean())
-        uAlternate = UCOL_SHIFTED;
-
-    if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
-        return nullptr;
-    if (!value.isUndefined() && value.toBoolean())
-        uNumeric = UCOL_ON;
-
-    if (!GetProperty(cx, internals, internals, cx->names().caseFirst, &value))
-        return nullptr;
-    if (!value.isUndefined()) {
-        JSLinearString* caseFirst = value.toString()->ensureLinear(cx);
-        if (!caseFirst)
-            return nullptr;
-        if (StringEqualsAscii(caseFirst, "upper")) {
-            uCaseFirst = UCOL_UPPER_FIRST;
-        } else if (StringEqualsAscii(caseFirst, "lower")) {
-            uCaseFirst = UCOL_LOWER_FIRST;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(caseFirst, "false"));
-            uCaseFirst = UCOL_OFF;
-        }
-    }
-
-    UErrorCode status = U_ZERO_ERROR;
-    UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return nullptr;
-    }
-
-    ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
-    ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
-    ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
-    ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
-    ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
-    ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
-    if (U_FAILURE(status)) {
-        ucol_close(coll);
-        intl::ReportInternalError(cx);
-        return nullptr;
-    }
-
-    return coll;
-}
-
-static bool
-intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
-                    MutableHandleValue result)
-{
-    MOZ_ASSERT(str1);
-    MOZ_ASSERT(str2);
-
-    if (str1 == str2) {
-        result.setInt32(0);
-        return true;
-    }
-
-    AutoStableStringChars stableChars1(cx);
-    if (!stableChars1.initTwoByte(cx, str1))
-        return false;
-
-    AutoStableStringChars stableChars2(cx);
-    if (!stableChars2.initTwoByte(cx, str2))
-        return false;
-
-    mozilla::Range<const char16_t> chars1 = stableChars1.twoByteRange();
-    mozilla::Range<const char16_t> chars2 = stableChars2.twoByteRange();
-
-    UCollationResult uresult = ucol_strcoll(coll,
-                                            chars1.begin().get(), chars1.length(),
-                                            chars2.begin().get(), chars2.length());
-    int32_t res;
-    switch (uresult) {
-        case UCOL_LESS: res = -1; break;
-        case UCOL_EQUAL: res = 0; break;
-        case UCOL_GREATER: res = 1; break;
-        default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
-    }
-    result.setInt32(res);
-    return true;
-}
-
-bool
-js::intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-    MOZ_ASSERT(args[0].isObject());
-    MOZ_ASSERT(args[1].isString());
-    MOZ_ASSERT(args[2].isString());
-
-    Rooted<CollatorObject*> collator(cx, &args[0].toObject().as<CollatorObject>());
-
-    // Obtain a cached UCollator object.
-    // XXX Does this handle Collator instances from other globals correctly?
-    void* priv = collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
-    UCollator* coll = static_cast<UCollator*>(priv);
-    if (!coll) {
-        coll = NewUCollator(cx, collator);
-        if (!coll)
-            return false;
-        collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT, PrivateValue(coll));
-    }
-
-    // Use the UCollator to actually compare the strings.
-    RootedString str1(cx, args[1].toString());
-    RootedString str2(cx, args[2].toString());
-    return intl_CompareStrings(cx, coll, str1, str2, args.rval());
-}
-
-js::SharedIntlData::LocaleHasher::Lookup::Lookup(JSLinearString* locale)
-  : js::SharedIntlData::LinearStringLookup(locale)
-{
-    if (isLatin1)
-        hash = mozilla::HashString(latin1Chars, length);
-    else
-        hash = mozilla::HashString(twoByteChars, length);
-}
-
-bool
-js::SharedIntlData::LocaleHasher::match(Locale key, const Lookup& lookup)
-{
-    if (key->length() != lookup.length)
-        return false;
-
-    if (key->hasLatin1Chars()) {
-        const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
-        if (lookup.isLatin1)
-            return EqualChars(keyChars, lookup.latin1Chars, lookup.length);
-        return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
-    }
-
-    const char16_t* keyChars = key->twoByteChars(lookup.nogc);
-    if (lookup.isLatin1)
-        return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
-    return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
-}
-
-bool
-js::SharedIntlData::ensureUpperCaseFirstLocales(JSContext* cx)
-{
-    if (upperCaseFirstInitialized)
-        return true;
-
-    // If ensureUpperCaseFirstLocales() was called previously, but didn't
-    // complete due to OOM, clear all data and start from scratch.
-    if (upperCaseFirstLocales.initialized())
-        upperCaseFirstLocales.finish();
-    if (!upperCaseFirstLocales.init()) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* available = ucol_openAvailableLocales(&status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UEnumeration, uenum_close> toClose(available);
-
-    RootedAtom locale(cx);
-    while (true) {
-        int32_t size;
-        const char* rawLocale = uenum_next(available, &size, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        if (rawLocale == nullptr)
-            break;
-
-        UCollator* collator = ucol_open(rawLocale, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-        ScopedICUObject<UCollator, ucol_close> toCloseCollator(collator);
-
-        UColAttributeValue caseFirst = ucol_getAttribute(collator, UCOL_CASE_FIRST, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        if (caseFirst != UCOL_UPPER_FIRST)
-            continue;
-
-        MOZ_ASSERT(size >= 0);
-        locale = Atomize(cx, rawLocale, size_t(size));
-        if (!locale)
-            return false;
-
-        LocaleHasher::Lookup lookup(locale);
-        LocaleSet::AddPtr p = upperCaseFirstLocales.lookupForAdd(lookup);
-
-        // ICU shouldn't report any duplicate locales, but if it does, just
-        // ignore the duplicated locale.
-        if (!p && !upperCaseFirstLocales.add(p, locale)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    MOZ_ASSERT(!upperCaseFirstInitialized,
-               "ensureUpperCaseFirstLocales is neither reentrant nor thread-safe");
-    upperCaseFirstInitialized = true;
-
-    return true;
-}
-
-bool
-js::SharedIntlData::isUpperCaseFirst(JSContext* cx, HandleString locale, bool* isUpperFirst)
-{
-    if (!ensureUpperCaseFirstLocales(cx))
-        return false;
-
-    RootedLinearString localeLinear(cx, locale->ensureLinear(cx));
-    if (!localeLinear)
-        return false;
-
-    LocaleHasher::Lookup lookup(localeLinear);
-    *isUpperFirst = upperCaseFirstLocales.has(lookup);
-
-    return true;
-}
-
-bool
-js::intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
-
-    RootedString locale(cx, args[0].toString());
-    bool isUpperFirst;
-    if (!sharedIntlData.isUpperCaseFirst(cx, locale, &isUpperFirst))
-        return false;
-
-    args.rval().setBoolean(isUpperFirst);
-    return true;
-}
-
-
 /******************** DateTimeFormat ********************/
 
 const ClassOps DateTimeFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -234,91 +234,16 @@ class SharedIntlData
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 };
 
 /*
  * The following functions are for use by self-hosted code.
  */
 
 
-/******************** Collator ********************/
-
-class CollatorObject : public NativeObject
-{
-  public:
-    static const Class class_;
-
-    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);
-};
-
-/**
- * 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
-intl_Collator(JSContext* cx, unsigned argc, Value* vp);
-
-/**
- * Returns an object indicating the supported locales for collation
- * 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_Collator_availableLocales()
- */
-extern MOZ_MUST_USE bool
-intl_Collator_availableLocales(JSContext* cx, unsigned argc, Value* vp);
-
-/**
- * Returns an array with the collation type identifiers per Unicode
- * Technical Standard 35, Unicode Locale Data Markup Language, for the
- * collations supported for the given locale. "standard" and "search" are
- * excluded.
- *
- * Usage: collations = intl_availableCollations(locale)
- */
-extern MOZ_MUST_USE bool
-intl_availableCollations(JSContext* cx, unsigned argc, Value* vp);
-
-/**
- * Compares x and y (which must be String values), and returns a number less
- * than 0 if x < y, 0 if x = y, or a number greater than 0 if x > y according
- * to the sort order for the locale and collation options of the given
- * Collator.
- *
- * Spec: ECMAScript Internationalization API Specification, 10.3.2.
- *
- * Usage: result = intl_CompareStrings(collator, x, y)
- */
-extern MOZ_MUST_USE bool
-intl_CompareStrings(JSContext* cx, unsigned argc, Value* vp);
-
-/**
- * Returns true if the given locale sorts upper-case before lower-case
- * characters.
- *
- * Usage: result = intl_isUpperCaseFirst(locale)
- */
-extern MOZ_MUST_USE bool
-intl_isUpperCaseFirst(JSContext* cx, unsigned argc, Value* vp);
-
-
 /******************** DateTimeFormat ********************/
 
 class DateTimeFormatObject : public NativeObject
 {
   public:
     static const Class class_;
 
     static constexpr uint32_t INTERNALS_SLOT = 0;
copy from js/src/builtin/Intl.cpp
copy to js/src/builtin/intl/Collator.cpp
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -1,79 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/*
- * The Intl module specified by standard ECMA-402,
- * ECMAScript Internationalization API Specification.
- */
-
-#include "builtin/Intl.h"
+/* Intl.Collator implementation. */
 
-#include "mozilla/Casting.h"
-#include "mozilla/FloatingPoint.h"
-#include "mozilla/HashFunctions.h"
-#include "mozilla/PodOperations.h"
-#include "mozilla/Range.h"
-#include "mozilla/TypeTraits.h"
+#include "builtin/intl/Collator.h"
 
-#include <string.h>
+#include "mozilla/Assertions.h"
 
 #include "jsapi.h"
-#include "jsatom.h"
 #include "jscntxt.h"
-#include "jsfriendapi.h"
-#include "jsobj.h"
-#include "jsstr.h"
-#include "jsutil.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
-#include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
-#include "builtin/IntlTimeZoneData.h"
-#include "ds/Sort.h"
 #include "gc/FreeOp.h"
-#include "js/Date.h"
-#include "vm/DateTime.h"
+#include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
-#include "vm/Interpreter.h"
-#include "vm/SelfHosting.h"
-#include "vm/Stack.h"
+#include "vm/Runtime.h"
 #include "vm/String.h"
-#include "vm/StringBuffer.h"
-#include "vm/Unicode.h"
 
 #include "jsobjinlines.h"
 
-#include "vm/NativeObject-inl.h"
-
 using namespace js;
 
-using mozilla::AssertedCast;
-using mozilla::IsNegativeZero;
-using mozilla::PodCopy;
-using mozilla::Range;
-using mozilla::RangedPtr;
-
-using JS::ClippedTime;
-using JS::TimeClip;
-
-using js::intl::CallICU;
-using js::intl::DateTimeFormatOptions;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
-using js::intl::INITIAL_CHAR_BUFFER_SIZE;
+using js::intl::ReportInternalError;
 using js::intl::StringsAreEqual;
 
-/******************** Collator ********************/
-
 const ClassOps CollatorObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     CollatorObject::finalize
@@ -167,27 +129,27 @@ js::intl_Collator(JSContext* cx, unsigne
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(!args.isConstructing());
 
     return Collator(cx, args);
 }
 
 void
-CollatorObject::finalize(FreeOp* fop, JSObject* obj)
+js::CollatorObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onActiveCooperatingThread());
 
     const Value& slot = obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
     if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate()))
         ucol_close(coll);
 }
 
-static JSObject*
-CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
+JSObject*
+js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, GlobalObject::createConstructor(cx, &Collator, cx->names().Collator,
                                                             0));
     if (!ctor)
         return nullptr;
 
     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
     if (!proto)
@@ -237,24 +199,24 @@ js::intl_availableCollations(JSContext* 
     MOZ_ASSERT(args[0].isString());
 
     JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
     UErrorCode status = U_ZERO_ERROR;
     UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
     if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
+        ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(values);
 
     uint32_t count = uenum_count(values, &status);
     if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
+        ReportInternalError(cx);
         return false;
     }
 
     RootedObject collations(cx, NewDenseEmptyArray(cx));
     if (!collations)
         return false;
 
     uint32_t index = 0;
@@ -263,17 +225,17 @@ js::intl_availableCollations(JSContext* 
     // ES2017 Intl, 10.2.3 Internal Slots.
     if (!DefineDataElement(cx, collations, index++, NullHandleValue))
         return false;
 
     RootedValue element(cx);
     for (uint32_t i = 0; i < count; i++) {
         const char* collation = uenum_next(values, nullptr, &status);
         if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
+            ReportInternalError(cx);
             return false;
         }
 
         // Per ECMA-402, 10.2.3, we don't include standard and search:
         // "The values 'standard' and 'search' must not be used as elements in
         // any [[sortLocaleData]][locale].co and [[searchLocaleData]][locale].co
         // array."
         if (StringsAreEqual(collation, "standard") || StringsAreEqual(collation, "search"))
@@ -413,29 +375,29 @@ NewUCollator(JSContext* cx, Handle<Colla
             MOZ_ASSERT(StringEqualsAscii(caseFirst, "false"));
             uCaseFirst = UCOL_OFF;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
     UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
+        ReportInternalError(cx);
         return nullptr;
     }
 
     ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
     ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
     ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
     ucol_setAttribute(coll, UCOL_NUMERIC_COLLATION, uNumeric, &status);
     ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, uNormalization, &status);
     ucol_setAttribute(coll, UCOL_CASE_FIRST, uCaseFirst, &status);
     if (U_FAILURE(status)) {
         ucol_close(coll);
-        intl::ReportInternalError(cx);
+        ReportInternalError(cx);
         return nullptr;
     }
 
     return coll;
 }
 
 static bool
 intl_CompareStrings(JSContext* cx, UCollator* coll, HandleString str1, HandleString str2,
@@ -543,43 +505,43 @@ js::SharedIntlData::ensureUpperCaseFirst
     if (!upperCaseFirstLocales.init()) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     UErrorCode status = U_ZERO_ERROR;
     UEnumeration* available = ucol_openAvailableLocales(&status);
     if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
+        ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(available);
 
     RootedAtom locale(cx);
     while (true) {
         int32_t size;
         const char* rawLocale = uenum_next(available, &size, &status);
         if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
+            ReportInternalError(cx);
             return false;
         }
 
         if (rawLocale == nullptr)
             break;
 
         UCollator* collator = ucol_open(rawLocale, &status);
         if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
+            ReportInternalError(cx);
             return false;
         }
         ScopedICUObject<UCollator, ucol_close> toCloseCollator(collator);
 
         UColAttributeValue caseFirst = ucol_getAttribute(collator, UCOL_CASE_FIRST, &status);
         if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
+            ReportInternalError(cx);
             return false;
         }
 
         if (caseFirst != UCOL_UPPER_FIRST)
             continue;
 
         MOZ_ASSERT(size >= 0);
         locale = Atomize(cx, rawLocale, size_t(size));
@@ -632,2479 +594,8 @@ js::intl_isUpperCaseFirst(JSContext* cx,
     RootedString locale(cx, args[0].toString());
     bool isUpperFirst;
     if (!sharedIntlData.isUpperCaseFirst(cx, locale, &isUpperFirst))
         return false;
 
     args.rval().setBoolean(isUpperFirst);
     return true;
 }
-
-
-/******************** DateTimeFormat ********************/
-
-const ClassOps DateTimeFormatObject::classOps_ = {
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* enumerate */
-    nullptr, /* newEnumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    DateTimeFormatObject::finalize
-};
-
-const Class DateTimeFormatObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(DateTimeFormatObject::SLOT_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
-    &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);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec dateTimeFormat_static_methods[] = {
-    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
-    JS_FS_END
-};
-
-static const JSFunctionSpec dateTimeFormat_methods[] = {
-    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
-    JS_SELF_HOSTED_FN("formatToParts", "Intl_DateTimeFormat_formatToParts", 1, 0),
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
-#endif
-    JS_FS_END
-};
-
-static const JSPropertySpec dateTimeFormat_properties[] = {
-    JS_SELF_HOSTED_GET("format", "Intl_DateTimeFormat_format_get", 0),
-    JS_STRING_SYM_PS(toStringTag, "Object", JSPROP_READONLY),
-    JS_PS_END
-};
-
-/**
- * 12.2.1 Intl.DateTimeFormat([ locales [, options]])
- *
- * ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
- */
-static bool
-DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeFormatOptions dtfOptions)
-{
-    // Step 1 (Handled by OrdinaryCreateFromConstructor fallback code).
-
-    // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
-    RootedObject proto(cx);
-    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
-        return false;
-
-    if (!proto) {
-        proto = GlobalObject::getOrCreateDateTimeFormatPrototype(cx, cx->global());
-        if (!proto)
-            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());
-    HandleValue locales = args.get(0);
-    HandleValue options = args.get(1);
-
-    // Step 3.
-    return intl::LegacyInitializeObject(cx, dateTimeFormat, cx->names().InitializeDateTimeFormat,
-                                        thisValue, locales, options, dtfOptions, args.rval());
-}
-
-static bool
-DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard);
-}
-
-static bool
-MozDateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Don't allow to call mozIntl.DateTimeFormat as a function. That way we
-    // don't need to worry how to handle the legacy initialization semantics
-    // when applied on mozIntl.DateTimeFormat.
-    if (!ThrowIfNotConstructing(cx, args, "mozIntl.DateTimeFormat"))
-        return false;
-
-    return DateTimeFormat(cx, args, true, DateTimeFormatOptions::EnableMozExtensions);
-}
-
-bool
-js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    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, DateTimeFormatOptions::Standard);
-}
-
-void
-DateTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
-{
-    MOZ_ASSERT(fop->onActiveCooperatingThread());
-
-    const Value& slot =
-        obj->as<DateTimeFormatObject>().getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT);
-    if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate()))
-        udat_close(df);
-}
-
-static JSObject*
-CreateDateTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global,
-                              MutableHandleObject constructor, DateTimeFormatOptions dtfOptions)
-{
-    RootedFunction ctor(cx);
-    ctor = dtfOptions == DateTimeFormatOptions::EnableMozExtensions
-           ? GlobalObject::createConstructor(cx, MozDateTimeFormat, cx->names().DateTimeFormat, 0)
-           : GlobalObject::createConstructor(cx, DateTimeFormat, cx->names().DateTimeFormat, 0);
-    if (!ctor)
-        return nullptr;
-
-    RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
-    if (!proto)
-        return nullptr;
-
-    if (!LinkConstructorAndPrototype(cx, ctor, proto))
-        return nullptr;
-
-    // 12.3.2
-    if (!JS_DefineFunctions(cx, ctor, dateTimeFormat_static_methods))
-        return nullptr;
-
-    // 12.4.4 and 12.4.5
-    if (!JS_DefineFunctions(cx, proto, dateTimeFormat_methods))
-        return nullptr;
-
-    // 12.4.2 and 12.4.3
-    if (!JS_DefineProperties(cx, proto, dateTimeFormat_properties))
-        return nullptr;
-
-    // 8.1
-    RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineDataProperty(cx, Intl, cx->names().DateTimeFormat, ctorValue, 0))
-        return nullptr;
-
-    constructor.set(ctor);
-    return proto;
-}
-
-bool
-js::AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
-{
-    Handle<GlobalObject*> global = cx->global();
-
-    RootedObject mozDateTimeFormat(cx);
-    JSObject* mozDateTimeFormatProto =
-        CreateDateTimeFormatPrototype(cx, intl, global, &mozDateTimeFormat, DateTimeFormatOptions::EnableMozExtensions);
-    return mozDateTimeFormatProto != nullptr;
-}
-
-bool
-js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    RootedValue result(cx);
-    if (!GetAvailableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
-        return false;
-    args.rval().set(result);
-    return true;
-}
-
-static bool
-DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
-{
-    UErrorCode status = U_ZERO_ERROR;
-    UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
-
-    // This correctly handles nullptr |cal| when opening failed.
-    ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
-
-    const char* calendar = ucal_getType(cal, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    // ICU returns old-style keyword values; map them to BCP 47 equivalents
-    JSString* str = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar));
-    if (!str)
-        return false;
-
-    rval.setString(str);
-    return true;
-}
-
-struct CalendarAlias
-{
-    const char* const calendar;
-    const char* const alias;
-};
-
-const CalendarAlias calendarAliases[] = {
-    { "islamic-civil", "islamicc" },
-    { "ethioaa", "ethiopic-amete-alem" }
-};
-
-bool
-js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    RootedObject calendars(cx, NewDenseEmptyArray(cx));
-    if (!calendars)
-        return false;
-    uint32_t index = 0;
-
-    // We need the default calendar for the locale as the first result.
-    RootedValue element(cx);
-    if (!DefaultCalendar(cx, locale, &element))
-        return false;
-
-    if (!DefineDataElement(cx, calendars, index++, element))
-        return false;
-
-    // Now get the calendars that "would make a difference", i.e., not the default.
-    UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UEnumeration, uenum_close> toClose(values);
-
-    uint32_t count = uenum_count(values, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    for (; count > 0; count--) {
-        const char* calendar = uenum_next(values, nullptr, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        // ICU returns old-style keyword values; map them to BCP 47 equivalents
-        calendar = uloc_toUnicodeLocaleType("ca", calendar);
-
-        JSString* jscalendar = JS_NewStringCopyZ(cx, calendar);
-        if (!jscalendar)
-            return false;
-        element = StringValue(jscalendar);
-        if (!DefineDataElement(cx, calendars, index++, element))
-            return false;
-
-        // ICU doesn't return calendar aliases, append them here.
-        for (const auto& calendarAlias : calendarAliases) {
-            if (StringsAreEqual(calendar, calendarAlias.calendar)) {
-                JSString* jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias);
-                if (!jscalendar)
-                    return false;
-                element = StringValue(jscalendar);
-                if (!DefineDataElement(cx, calendars, index++, element))
-                    return false;
-            }
-        }
-    }
-
-    args.rval().setObject(*calendars);
-    return true;
-}
-
-bool
-js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    return DefaultCalendar(cx, locale, args.rval());
-}
-
-template<typename Char>
-static constexpr Char
-ToUpperASCII(Char c)
-{
-    return ('a' <= c && c <= 'z')
-           ? (c & ~0x20)
-           : c;
-}
-
-static_assert(ToUpperASCII('a') == 'A', "verifying 'a' uppercases correctly");
-static_assert(ToUpperASCII('m') == 'M', "verifying 'm' uppercases correctly");
-static_assert(ToUpperASCII('z') == 'Z', "verifying 'z' uppercases correctly");
-static_assert(ToUpperASCII(u'a') == u'A', "verifying u'a' uppercases correctly");
-static_assert(ToUpperASCII(u'k') == u'K', "verifying u'k' uppercases correctly");
-static_assert(ToUpperASCII(u'z') == u'Z', "verifying u'z' uppercases correctly");
-
-template<typename Char1, typename Char2>
-static bool
-EqualCharsIgnoreCaseASCII(const Char1* s1, const Char2* s2, size_t len)
-{
-    for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (ToUpperASCII(*s1) != ToUpperASCII(*s2))
-            return false;
-    }
-    return true;
-}
-
-template<typename Char>
-static js::HashNumber
-HashStringIgnoreCaseASCII(const Char* s, size_t length)
-{
-    uint32_t hash = 0;
-    for (size_t i = 0; i < length; i++)
-        hash = mozilla::AddToHash(hash, ToUpperASCII(s[i]));
-    return hash;
-}
-
-js::SharedIntlData::TimeZoneHasher::Lookup::Lookup(JSLinearString* timeZone)
-  : js::SharedIntlData::LinearStringLookup(timeZone)
-{
-    if (isLatin1)
-        hash = HashStringIgnoreCaseASCII(latin1Chars, length);
-    else
-        hash = HashStringIgnoreCaseASCII(twoByteChars, length);
-}
-
-bool
-js::SharedIntlData::TimeZoneHasher::match(TimeZoneName key, const Lookup& lookup)
-{
-    if (key->length() != lookup.length)
-        return false;
-
-    // Compare time zone names ignoring ASCII case differences.
-    if (key->hasLatin1Chars()) {
-        const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
-        if (lookup.isLatin1)
-            return EqualCharsIgnoreCaseASCII(keyChars, lookup.latin1Chars, lookup.length);
-        return EqualCharsIgnoreCaseASCII(keyChars, lookup.twoByteChars, lookup.length);
-    }
-
-    const char16_t* keyChars = key->twoByteChars(lookup.nogc);
-    if (lookup.isLatin1)
-        return EqualCharsIgnoreCaseASCII(lookup.latin1Chars, keyChars, lookup.length);
-    return EqualCharsIgnoreCaseASCII(keyChars, lookup.twoByteChars, lookup.length);
-}
-
-static bool
-IsLegacyICUTimeZone(const char* timeZone)
-{
-    for (const auto& legacyTimeZone : js::timezone::legacyICUTimeZones) {
-        if (StringsAreEqual(timeZone, legacyTimeZone))
-            return true;
-    }
-    return false;
-}
-
-bool
-js::SharedIntlData::ensureTimeZones(JSContext* cx)
-{
-    if (timeZoneDataInitialized)
-        return true;
-
-    // If ensureTimeZones() was called previously, but didn't complete due to
-    // OOM, clear all sets/maps and start from scratch.
-    if (availableTimeZones.initialized())
-        availableTimeZones.finish();
-    if (!availableTimeZones.init()) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucal_openTimeZones(&status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UEnumeration, uenum_close> toClose(values);
-
-    RootedAtom timeZone(cx);
-    while (true) {
-        int32_t size;
-        const char* rawTimeZone = uenum_next(values, &size, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        if (rawTimeZone == nullptr)
-            break;
-
-        // Skip legacy ICU time zone names.
-        if (IsLegacyICUTimeZone(rawTimeZone))
-            continue;
-
-        MOZ_ASSERT(size >= 0);
-        timeZone = Atomize(cx, rawTimeZone, size_t(size));
-        if (!timeZone)
-            return false;
-
-        TimeZoneHasher::Lookup lookup(timeZone);
-        TimeZoneSet::AddPtr p = availableTimeZones.lookupForAdd(lookup);
-
-        // ICU shouldn't report any duplicate time zone names, but if it does,
-        // just ignore the duplicate name.
-        if (!p && !availableTimeZones.add(p, timeZone)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    if (ianaZonesTreatedAsLinksByICU.initialized())
-        ianaZonesTreatedAsLinksByICU.finish();
-    if (!ianaZonesTreatedAsLinksByICU.init()) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    for (const char* rawTimeZone : timezone::ianaZonesTreatedAsLinksByICU) {
-        MOZ_ASSERT(rawTimeZone != nullptr);
-        timeZone = Atomize(cx, rawTimeZone, strlen(rawTimeZone));
-        if (!timeZone)
-            return false;
-
-        TimeZoneHasher::Lookup lookup(timeZone);
-        TimeZoneSet::AddPtr p = ianaZonesTreatedAsLinksByICU.lookupForAdd(lookup);
-        MOZ_ASSERT(!p, "Duplicate entry in timezone::ianaZonesTreatedAsLinksByICU");
-
-        if (!ianaZonesTreatedAsLinksByICU.add(p, timeZone)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    if (ianaLinksCanonicalizedDifferentlyByICU.initialized())
-        ianaLinksCanonicalizedDifferentlyByICU.finish();
-    if (!ianaLinksCanonicalizedDifferentlyByICU.init()) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    RootedAtom linkName(cx);
-    RootedAtom& target = timeZone;
-    for (const auto& linkAndTarget : timezone::ianaLinksCanonicalizedDifferentlyByICU) {
-        const char* rawLinkName = linkAndTarget.link;
-        const char* rawTarget = linkAndTarget.target;
-
-        MOZ_ASSERT(rawLinkName != nullptr);
-        linkName = Atomize(cx, rawLinkName, strlen(rawLinkName));
-        if (!linkName)
-            return false;
-
-        MOZ_ASSERT(rawTarget != nullptr);
-        target = Atomize(cx, rawTarget, strlen(rawTarget));
-        if (!target)
-            return false;
-
-        TimeZoneHasher::Lookup lookup(linkName);
-        TimeZoneMap::AddPtr p = ianaLinksCanonicalizedDifferentlyByICU.lookupForAdd(lookup);
-        MOZ_ASSERT(!p, "Duplicate entry in timezone::ianaLinksCanonicalizedDifferentlyByICU");
-
-        if (!ianaLinksCanonicalizedDifferentlyByICU.add(p, linkName, target)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    MOZ_ASSERT(!timeZoneDataInitialized, "ensureTimeZones is neither reentrant nor thread-safe");
-    timeZoneDataInitialized = true;
-
-    return true;
-}
-
-bool
-js::SharedIntlData::validateTimeZoneName(JSContext* cx, HandleString timeZone,
-                                         MutableHandleAtom result)
-{
-    if (!ensureTimeZones(cx))
-        return false;
-
-    RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
-    if (!timeZoneLinear)
-        return false;
-
-    TimeZoneHasher::Lookup lookup(timeZoneLinear);
-    if (TimeZoneSet::Ptr p = availableTimeZones.lookup(lookup))
-        result.set(*p);
-
-    return true;
-}
-
-bool
-js::SharedIntlData::tryCanonicalizeTimeZoneConsistentWithIANA(JSContext* cx, HandleString timeZone,
-                                                              MutableHandleAtom result)
-{
-    if (!ensureTimeZones(cx))
-        return false;
-
-    RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
-    if (!timeZoneLinear)
-        return false;
-
-    TimeZoneHasher::Lookup lookup(timeZoneLinear);
-    MOZ_ASSERT(availableTimeZones.has(lookup), "Invalid time zone name");
-
-    if (TimeZoneMap::Ptr p = ianaLinksCanonicalizedDifferentlyByICU.lookup(lookup)) {
-        // The effectively supported time zones aren't known at compile time,
-        // when
-        // 1. SpiderMonkey was compiled with "--with-system-icu".
-        // 2. ICU's dynamic time zone data loading feature was used.
-        //    (ICU supports loading time zone files at runtime through the
-        //    ICU_TIMEZONE_FILES_DIR environment variable.)
-        // Ensure ICU supports the new target zone before applying the update.
-        TimeZoneName targetTimeZone = p->value();
-        TimeZoneHasher::Lookup targetLookup(targetTimeZone);
-        if (availableTimeZones.has(targetLookup))
-            result.set(targetTimeZone);
-    } else if (TimeZoneSet::Ptr p = ianaZonesTreatedAsLinksByICU.lookup(lookup)) {
-        result.set(*p);
-    }
-
-    return true;
-}
-
-void
-js::SharedIntlData::destroyInstance()
-{
-    availableTimeZones.finish();
-    ianaZonesTreatedAsLinksByICU.finish();
-    ianaLinksCanonicalizedDifferentlyByICU.finish();
-    upperCaseFirstLocales.finish();
-}
-
-void
-js::SharedIntlData::trace(JSTracer* trc)
-{
-    // Atoms are always tenured.
-    if (!JS::CurrentThreadIsHeapMinorCollecting()) {
-        availableTimeZones.trace(trc);
-        ianaZonesTreatedAsLinksByICU.trace(trc);
-        ianaLinksCanonicalizedDifferentlyByICU.trace(trc);
-        upperCaseFirstLocales.trace(trc);
-    }
-}
-
-size_t
-js::SharedIntlData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
-{
-    return availableTimeZones.sizeOfExcludingThis(mallocSizeOf) +
-           ianaZonesTreatedAsLinksByICU.sizeOfExcludingThis(mallocSizeOf) +
-           ianaLinksCanonicalizedDifferentlyByICU.sizeOfExcludingThis(mallocSizeOf) +
-           upperCaseFirstLocales.sizeOfExcludingThis(mallocSizeOf);
-}
-
-bool
-js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
-
-    RootedString timeZone(cx, args[0].toString());
-    RootedAtom validatedTimeZone(cx);
-    if (!sharedIntlData.validateTimeZoneName(cx, timeZone, &validatedTimeZone))
-        return false;
-
-    if (validatedTimeZone) {
-        cx->markAtom(validatedTimeZone);
-        args.rval().setString(validatedTimeZone);
-    } else {
-        args.rval().setNull();
-    }
-
-    return true;
-}
-
-bool
-js::intl_canonicalizeTimeZone(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString());
-
-    SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
-
-    // Some time zone names are canonicalized differently by ICU -- handle
-    // those first:
-    RootedString timeZone(cx, args[0].toString());
-    RootedAtom ianaTimeZone(cx);
-    if (!sharedIntlData.tryCanonicalizeTimeZoneConsistentWithIANA(cx, timeZone, &ianaTimeZone))
-        return false;
-
-    if (ianaTimeZone) {
-        cx->markAtom(ianaTimeZone);
-        args.rval().setString(ianaTimeZone);
-        return true;
-    }
-
-    AutoStableStringChars stableChars(cx);
-    if (!stableChars.initTwoByte(cx, timeZone))
-        return false;
-
-    mozilla::Range<const char16_t> tzchars = stableChars.twoByteRange();
-
-    JSString* str = CallICU(cx, [&tzchars](UChar* chars, uint32_t size, UErrorCode* status) {
-        return ucal_getCanonicalTimeZoneID(tzchars.begin().get(), tzchars.length(),
-                                           chars, size, nullptr, status);
-    });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-bool
-js::intl_defaultTimeZone(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    // The current default might be stale, because JS::ResetTimeZone() doesn't
-    // immediately update ICU's default time zone. So perform an update if
-    // needed.
-    js::ResyncICUDefaultTimeZone();
-
-    JSString* str = CallICU(cx, ucal_getDefaultTimeZone);
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-bool
-js::intl_defaultTimeZoneOffset(JSContext* cx, unsigned argc, Value* vp) {
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    UErrorCode status = U_ZERO_ERROR;
-    const UChar* uTimeZone = nullptr;
-    int32_t uTimeZoneLength = 0;
-    const char* rootLocale = "";
-    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, rootLocale, UCAL_DEFAULT, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UCalendar, ucal_close> toClose(cal);
-
-    int32_t offset = ucal_get(cal, UCAL_ZONE_OFFSET, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    args.rval().setInt32(offset);
-    return true;
-}
-
-bool
-js::intl_isDefaultTimeZone(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isString() || args[0].isUndefined());
-
-    // |undefined| is the default value when the Intl runtime caches haven't
-    // yet been initialized. Handle it the same way as a cache miss.
-    if (args[0].isUndefined()) {
-        args.rval().setBoolean(false);
-        return true;
-    }
-
-    // The current default might be stale, because JS::ResetTimeZone() doesn't
-    // immediately update ICU's default time zone. So perform an update if
-    // needed.
-    js::ResyncICUDefaultTimeZone();
-
-    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
-    int32_t size = CallICU(cx, ucal_getDefaultTimeZone, chars);
-    if (size < 0)
-        return false;
-
-    JSLinearString* str = args[0].toString()->ensureLinear(cx);
-    if (!str)
-        return false;
-
-    bool equals;
-    if (str->length() == size_t(size)) {
-        JS::AutoCheckCannotGC nogc;
-        equals = str->hasLatin1Chars()
-                 ? EqualChars(str->latin1Chars(nogc), chars.begin(), str->length())
-                 : EqualChars(str->twoByteChars(nogc), chars.begin(), str->length());
-    } else {
-        equals = false;
-    }
-
-    args.rval().setBoolean(equals);
-    return true;
-}
-
-bool
-js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isString());
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    AutoStableStringChars skeleton(cx);
-    if (!skeleton.initTwoByte(cx, args[1].toString()))
-        return false;
-
-    mozilla::Range<const char16_t> skelChars = skeleton.twoByteRange();
-
-    UErrorCode status = U_ZERO_ERROR;
-    UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale.ptr()), &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
-
-    JSString* str =
-        CallICU(cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) {
-            return udatpg_getBestPattern(gen, skelChars.begin().get(), skelChars.length(),
-                                         chars, size, status);
-        });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-bool
-js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 4);
-    MOZ_ASSERT(args[0].isString());
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    UDateFormatStyle dateStyle = UDAT_NONE;
-    UDateFormatStyle timeStyle = UDAT_NONE;
-
-    if (args[1].isString()) {
-        JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
-        if (!dateStyleStr)
-            return false;
-
-        if (StringEqualsAscii(dateStyleStr, "full"))
-            dateStyle = UDAT_FULL;
-        else if (StringEqualsAscii(dateStyleStr, "long"))
-            dateStyle = UDAT_LONG;
-        else if (StringEqualsAscii(dateStyleStr, "medium"))
-            dateStyle = UDAT_MEDIUM;
-        else if (StringEqualsAscii(dateStyleStr, "short"))
-            dateStyle = UDAT_SHORT;
-        else
-            MOZ_ASSERT_UNREACHABLE("unexpected dateStyle");
-    }
-
-    if (args[2].isString()) {
-        JSLinearString* timeStyleStr = args[2].toString()->ensureLinear(cx);
-        if (!timeStyleStr)
-            return false;
-
-        if (StringEqualsAscii(timeStyleStr, "full"))
-            timeStyle = UDAT_FULL;
-        else if (StringEqualsAscii(timeStyleStr, "long"))
-            timeStyle = UDAT_LONG;
-        else if (StringEqualsAscii(timeStyleStr, "medium"))
-            timeStyle = UDAT_MEDIUM;
-        else if (StringEqualsAscii(timeStyleStr, "short"))
-            timeStyle = UDAT_SHORT;
-        else
-            MOZ_ASSERT_UNREACHABLE("unexpected timeStyle");
-    }
-
-    AutoStableStringChars timeZone(cx);
-    if (!timeZone.initTwoByte(cx, args[3].toString()))
-        return false;
-
-    mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
-
-    UErrorCode status = U_ZERO_ERROR;
-    UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.ptr()),
-                                timeZoneChars.begin().get(), timeZoneChars.length(),
-                                nullptr, -1, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UDateFormat, udat_close> toClose(df);
-
-    JSString* str = CallICU(cx, [df](UChar* chars, uint32_t size, UErrorCode* status) {
-        return udat_toPattern(df, false, chars, size, status);
-    });
-    if (!str)
-        return false;
-    args.rval().setString(str);
-    return true;
-}
-
-/**
- * Returns a new UDateFormat with the locale and date-time formatting options
- * of the given DateTimeFormat.
- */
-static UDateFormat*
-NewUDateFormat(JSContext* cx, Handle<DateTimeFormatObject*> dateTimeFormat)
-{
-    RootedValue value(cx);
-
-    RootedObject internals(cx, intl::GetInternalsObject(cx, dateTimeFormat));
-    if (!internals)
-       return nullptr;
-
-    if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
-        return nullptr;
-    JSAutoByteString locale(cx, value.toString());
-    if (!locale)
-        return nullptr;
-
-    // We don't need to look at calendar and numberingSystem - they can only be
-    // set via the Unicode locale extension and are therefore already set on
-    // locale.
-
-    if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
-        return nullptr;
-
-    AutoStableStringChars timeZone(cx);
-    if (!timeZone.initTwoByte(cx, value.toString()))
-        return nullptr;
-
-    mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
-
-    if (!GetProperty(cx, internals, internals, cx->names().pattern, &value))
-        return nullptr;
-
-    AutoStableStringChars pattern(cx);
-    if (!pattern.initTwoByte(cx, value.toString()))
-        return nullptr;
-
-    mozilla::Range<const char16_t> patternChars = pattern.twoByteRange();
-
-    UErrorCode status = U_ZERO_ERROR;
-    UDateFormat* df =
-        udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.ptr()),
-                  timeZoneChars.begin().get(), timeZoneChars.length(),
-                  patternChars.begin().get(), patternChars.length(), &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return nullptr;
-    }
-
-    // ECMAScript requires the Gregorian calendar to be used from the beginning
-    // of ECMAScript time.
-    UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(df));
-    ucal_setGregorianChange(cal, StartOfTime, &status);
-
-    // An error here means the calendar is not Gregorian, so we don't care.
-
-    return df;
-}
-
-static bool
-intl_FormatDateTime(JSContext* cx, UDateFormat* df, ClippedTime x, MutableHandleValue result)
-{
-    MOZ_ASSERT(x.isValid());
-
-    JSString* str = CallICU(cx, [df, x](UChar* chars, int32_t size, UErrorCode* status) {
-        return udat_format(df, x.toDouble(), chars, size, nullptr, status);
-    });
-    if (!str)
-        return false;
-
-    result.setString(str);
-    return true;
-}
-
-using FieldType = ImmutablePropertyNamePtr JSAtomState::*;
-
-static FieldType
-GetFieldTypeForFormatField(UDateFormatField fieldName)
-{
-    // See intl/icu/source/i18n/unicode/udat.h for a detailed field list.  This
-    // switch is deliberately exhaustive: cases might have to be added/removed
-    // if this code is compiled with a different ICU with more
-    // UDateFormatField enum initializers.  Please guard such cases with
-    // appropriate ICU version-testing #ifdefs, should cross-version divergence
-    // occur.
-    switch (fieldName) {
-      case UDAT_ERA_FIELD:
-        return &JSAtomState::era;
-      case UDAT_YEAR_FIELD:
-      case UDAT_YEAR_WOY_FIELD:
-      case UDAT_EXTENDED_YEAR_FIELD:
-      case UDAT_YEAR_NAME_FIELD:
-        return &JSAtomState::year;
-
-      case UDAT_MONTH_FIELD:
-      case UDAT_STANDALONE_MONTH_FIELD:
-        return &JSAtomState::month;
-
-      case UDAT_DATE_FIELD:
-      case UDAT_JULIAN_DAY_FIELD:
-        return &JSAtomState::day;
-
-      case UDAT_HOUR_OF_DAY1_FIELD:
-      case UDAT_HOUR_OF_DAY0_FIELD:
-      case UDAT_HOUR1_FIELD:
-      case UDAT_HOUR0_FIELD:
-        return &JSAtomState::hour;
-
-      case UDAT_MINUTE_FIELD:
-        return &JSAtomState::minute;
-
-      case UDAT_SECOND_FIELD:
-        return &JSAtomState::second;
-
-      case UDAT_DAY_OF_WEEK_FIELD:
-      case UDAT_STANDALONE_DAY_FIELD:
-      case UDAT_DOW_LOCAL_FIELD:
-      case UDAT_DAY_OF_WEEK_IN_MONTH_FIELD:
-        return &JSAtomState::weekday;
-
-      case UDAT_AM_PM_FIELD:
-        return &JSAtomState::dayPeriod;
-
-      case UDAT_TIMEZONE_FIELD:
-        return &JSAtomState::timeZoneName;
-
-      case UDAT_FRACTIONAL_SECOND_FIELD:
-      case UDAT_DAY_OF_YEAR_FIELD:
-      case UDAT_WEEK_OF_YEAR_FIELD:
-      case UDAT_WEEK_OF_MONTH_FIELD:
-      case UDAT_MILLISECONDS_IN_DAY_FIELD:
-      case UDAT_TIMEZONE_RFC_FIELD:
-      case UDAT_TIMEZONE_GENERIC_FIELD:
-      case UDAT_QUARTER_FIELD:
-      case UDAT_STANDALONE_QUARTER_FIELD:
-      case UDAT_TIMEZONE_SPECIAL_FIELD:
-      case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
-      case UDAT_TIMEZONE_ISO_FIELD:
-      case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
-#ifndef U_HIDE_INTERNAL_API
-      case UDAT_RELATED_YEAR_FIELD:
-#endif
-      case UDAT_AM_PM_MIDNIGHT_NOON_FIELD:
-      case UDAT_FLEXIBLE_DAY_PERIOD_FIELD:
-#ifndef U_HIDE_INTERNAL_API
-      case UDAT_TIME_SEPARATOR_FIELD:
-#endif
-        // These fields are all unsupported.
-        return nullptr;
-
-#ifndef U_HIDE_DEPRECATED_API
-      case UDAT_FIELD_COUNT:
-        MOZ_ASSERT_UNREACHABLE("format field sentinel value returned by "
-                               "iterator!");
-#endif
-    }
-
-    MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented format field returned "
-                           "by iterator");
-    return nullptr;
-}
-
-static bool
-intl_FormatToPartsDateTime(JSContext* cx, UDateFormat* df, ClippedTime x,
-                           MutableHandleValue result)
-{
-    MOZ_ASSERT(x.isValid());
-
-    UErrorCode status = U_ZERO_ERROR;
-    UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UFieldPositionIterator, ufieldpositer_close> toClose(fpositer);
-
-    RootedString overallResult(cx);
-    overallResult = CallICU(cx, [df, x, fpositer](UChar* chars, int32_t size, UErrorCode* status) {
-        return udat_formatForFields(df, x.toDouble(), chars, size, fpositer, status);
-    });
-    if (!overallResult)
-        return false;
-
-    RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
-    if (!partsArray)
-        return false;
-
-    if (overallResult->length() == 0) {
-        // An empty string contains no parts, so avoid extra work below.
-        result.setObject(*partsArray);
-        return true;
-    }
-
-    size_t lastEndIndex = 0;
-
-    uint32_t partIndex = 0;
-    RootedObject singlePart(cx);
-    RootedValue partType(cx);
-    RootedValue val(cx);
-
-    auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
-        singlePart = NewBuiltinClassInstance<PlainObject>(cx);
-        if (!singlePart)
-            return false;
-
-        partType = StringValue(cx->names().*type);
-        if (!DefineDataProperty(cx, singlePart, cx->names().type, partType))
-            return false;
-
-        JSLinearString* partSubstr =
-            NewDependentString(cx, overallResult, beginIndex, endIndex - beginIndex);
-        if (!partSubstr)
-            return false;
-
-        val = StringValue(partSubstr);
-        if (!DefineDataProperty(cx, singlePart, cx->names().value, val))
-            return false;
-
-        val = ObjectValue(*singlePart);
-        if (!DefineDataElement(cx, partsArray, partIndex, val))
-            return false;
-
-        lastEndIndex = endIndex;
-        partIndex++;
-        return true;
-    };
-
-    int32_t fieldInt, beginIndexInt, endIndexInt;
-    while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt, &endIndexInt)) >= 0) {
-        MOZ_ASSERT(beginIndexInt >= 0);
-        MOZ_ASSERT(endIndexInt >= 0);
-        MOZ_ASSERT(beginIndexInt <= endIndexInt,
-                   "field iterator returning invalid range");
-
-        size_t beginIndex(beginIndexInt);
-        size_t endIndex(endIndexInt);
-
-        // Technically this isn't guaranteed.  But it appears true in pratice,
-        // and http://bugs.icu-project.org/trac/ticket/12024 is expected to
-        // correct the documentation lapse.
-        MOZ_ASSERT(lastEndIndex <= beginIndex,
-                   "field iteration didn't return fields in order start to "
-                   "finish as expected");
-
-        if (FieldType type = GetFieldTypeForFormatField(static_cast<UDateFormatField>(fieldInt))) {
-            if (lastEndIndex < beginIndex) {
-                if (!AppendPart(&JSAtomState::literal, lastEndIndex, beginIndex))
-                    return false;
-            }
-
-            if (!AppendPart(type, beginIndex, endIndex))
-                return false;
-        }
-    }
-
-    // Append any final literal.
-    if (lastEndIndex < overallResult->length()) {
-        if (!AppendPart(&JSAtomState::literal, lastEndIndex, overallResult->length()))
-            return false;
-    }
-
-    result.setObject(*partsArray);
-    return true;
-}
-
-bool
-js::intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-    MOZ_ASSERT(args[0].isObject());
-    MOZ_ASSERT(args[1].isNumber());
-    MOZ_ASSERT(args[2].isBoolean());
-
-    Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
-    dateTimeFormat = &args[0].toObject().as<DateTimeFormatObject>();
-
-    ClippedTime x = TimeClip(args[1].toNumber());
-    if (!x.isValid()) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
-        return false;
-    }
-
-    // Obtain a cached UDateFormat object.
-    void* priv =
-        dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT).toPrivate();
-    UDateFormat* df = static_cast<UDateFormat*>(priv);
-    if (!df) {
-        df = NewUDateFormat(cx, dateTimeFormat);
-        if (!df)
-            return false;
-        dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT, PrivateValue(df));
-    }
-
-    // Use the UDateFormat to actually format the time stamp.
-    return args[2].toBoolean()
-           ? intl_FormatToPartsDateTime(cx, df, x, args.rval())
-           : intl_FormatDateTime(cx, df, x, args.rval());
-}
-
-
-/**************** PluralRules *****************/
-
-const ClassOps PluralRulesObject::classOps_ = {
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* enumerate */
-    nullptr, /* newEnumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    PluralRulesObject::finalize
-};
-
-const Class PluralRulesObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(PluralRulesObject::SLOT_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
-    &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);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec pluralRules_static_methods[] = {
-    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_PluralRules_supportedLocalesOf", 1, 0),
-    JS_FS_END
-};
-
-static const JSFunctionSpec pluralRules_methods[] = {
-    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_PluralRules_resolvedOptions", 0, 0),
-    JS_SELF_HOSTED_FN("select", "Intl_PluralRules_select", 1, 0),
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str, pluralRules_toSource, 0, 0),
-#endif
-    JS_FS_END
-};
-
-/**
- * PluralRules constructor.
- * Spec: ECMAScript 402 API, PluralRules, 1.1
- */
-static bool
-PluralRules(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Step 1.
-    if (!ThrowIfNotConstructing(cx, args, "Intl.PluralRules"))
-        return false;
-
-    // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
-    RootedObject proto(cx);
-    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
-        return false;
-
-    if (!proto) {
-        proto = GlobalObject::getOrCreatePluralRulesPrototype(cx, cx->global());
-        if (!proto)
-            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));
-
-    HandleValue locales = args.get(0);
-    HandleValue options = args.get(1);
-
-    // Step 3.
-    if (!intl::InitializeObject(cx, pluralRules, cx->names().InitializePluralRules, locales,
-                                options))
-    {
-        return false;
-    }
-
-    args.rval().setObject(*pluralRules);
-    return true;
-}
-
-void
-PluralRulesObject::finalize(FreeOp* fop, JSObject* obj)
-{
-    MOZ_ASSERT(fop->onActiveCooperatingThread());
-
-    const Value& slot =
-        obj->as<PluralRulesObject>().getReservedSlot(PluralRulesObject::UPLURAL_RULES_SLOT);
-    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;
-
-    RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
-    if (!proto)
-        return 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))
-        return nullptr;
-
-    RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineDataProperty(cx, Intl, cx->names().PluralRules, ctorValue, 0))
-        return nullptr;
-
-    return proto;
-}
-
-bool
-js::intl_PluralRules_availableLocales(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    RootedValue result(cx);
-    // We're going to use ULocale availableLocales as per ICU recommendation:
-    // https://ssl.icu-project.org/trac/ticket/12756
-    if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
-        return false;
-    args.rval().set(result);
-    return true;
-}
-
-/**
- * This creates new UNumberFormat with calculated digit formatting
- * properties for PluralRules.
- *
- * This is similar to NewUNumberFormat but doesn't allow for currency or
- * percent types.
- */
-static UNumberFormat*
-NewUNumberFormatForPluralRules(JSContext* cx, Handle<PluralRulesObject*> pluralRules)
-{
-    RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
-    if (!internals)
-       return nullptr;
-
-    RootedValue value(cx);
-
-    if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
-        return nullptr;
-    JSAutoByteString locale(cx, value.toString());
-    if (!locale)
-        return nullptr;
-
-    uint32_t uMinimumIntegerDigits = 1;
-    uint32_t uMinimumFractionDigits = 0;
-    uint32_t uMaximumFractionDigits = 3;
-    int32_t uMinimumSignificantDigits = -1;
-    int32_t uMaximumSignificantDigits = -1;
-
-    bool hasP;
-    if (!HasProperty(cx, internals, cx->names().minimumSignificantDigits, &hasP))
-        return nullptr;
-
-    if (hasP) {
-        if (!GetProperty(cx, internals, internals, cx->names().minimumSignificantDigits, &value))
-            return nullptr;
-        uMinimumSignificantDigits = value.toInt32();
-
-        if (!GetProperty(cx, internals, internals, cx->names().maximumSignificantDigits, &value))
-            return nullptr;
-        uMaximumSignificantDigits = value.toInt32();
-    } else {
-        if (!GetProperty(cx, internals, internals, cx->names().minimumIntegerDigits, &value))
-            return nullptr;
-        uMinimumIntegerDigits = AssertedCast<uint32_t>(value.toInt32());
-
-        if (!GetProperty(cx, internals, internals, cx->names().minimumFractionDigits, &value))
-            return nullptr;
-        uMinimumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
-
-        if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits, &value))
-            return nullptr;
-        uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
-    }
-
-    UErrorCode status = U_ZERO_ERROR;
-    UNumberFormat* nf =
-        unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return nullptr;
-    }
-    ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
-
-    if (uMinimumSignificantDigits != -1) {
-        unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
-        unum_setAttribute(nf, UNUM_MIN_SIGNIFICANT_DIGITS, uMinimumSignificantDigits);
-        unum_setAttribute(nf, UNUM_MAX_SIGNIFICANT_DIGITS, uMaximumSignificantDigits);
-    } else {
-        unum_setAttribute(nf, UNUM_MIN_INTEGER_DIGITS, uMinimumIntegerDigits);
-        unum_setAttribute(nf, UNUM_MIN_FRACTION_DIGITS, uMinimumFractionDigits);
-        unum_setAttribute(nf, UNUM_MAX_FRACTION_DIGITS, uMaximumFractionDigits);
-    }
-
-    return toClose.forget();
-}
-
-bool
-js::intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    Rooted<PluralRulesObject*> pluralRules(cx, &args[0].toObject().as<PluralRulesObject>());
-
-    double x = args[1].toNumber();
-
-    UNumberFormat* nf = NewUNumberFormatForPluralRules(cx, pluralRules);
-    if (!nf)
-        return false;
-
-    ScopedICUObject<UNumberFormat, unum_close> closeNumberFormat(nf);
-
-    RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
-    if (!internals)
-        return false;
-
-    RootedValue value(cx);
-
-    if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
-        return false;
-    JSAutoByteString locale(cx, value.toString());
-    if (!locale)
-        return false;
-
-    if (!GetProperty(cx, internals, internals, cx->names().type, &value))
-        return false;
-
-    UPluralType category;
-    {
-        JSLinearString* type = value.toString()->ensureLinear(cx);
-        if (!type)
-            return false;
-
-        if (StringEqualsAscii(type, "cardinal")) {
-            category = UPLURAL_TYPE_CARDINAL;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
-            category = UPLURAL_TYPE_ORDINAL;
-        }
-    }
-
-    // TODO: Cache UPluralRules in PluralRulesObject::UPluralRulesSlot.
-    UErrorCode status = U_ZERO_ERROR;
-    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.ptr()), category, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
-
-    JSString* str = CallICU(cx, [pr, x, nf](UChar* chars, int32_t size, UErrorCode* status) {
-        return uplrules_selectWithFormat(pr, x, nf, chars, size, status);
-    });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-bool
-js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    JSLinearString* type = args[1].toString()->ensureLinear(cx);
-    if (!type)
-        return false;
-
-    UPluralType category;
-    if (StringEqualsAscii(type, "cardinal")) {
-        category = UPLURAL_TYPE_CARDINAL;
-    } else {
-        MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
-        category = UPLURAL_TYPE_ORDINAL;
-    }
-
-    UErrorCode status = U_ZERO_ERROR;
-    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.ptr()), category, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UPluralRules, uplrules_close> closePluralRules(pr);
-
-    UEnumeration* ue = uplrules_getKeywords(pr, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UEnumeration, uenum_close> closeEnum(ue);
-
-    RootedObject res(cx, NewDenseEmptyArray(cx));
-    if (!res)
-        return false;
-
-    RootedValue element(cx);
-    uint32_t i = 0;
-
-    do {
-        int32_t catSize;
-        const char* cat = uenum_next(ue, &catSize, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        if (!cat)
-            break;
-
-        MOZ_ASSERT(catSize >= 0);
-        JSString* str = NewStringCopyN<CanGC>(cx, cat, catSize);
-        if (!str)
-            return false;
-
-        element.setString(str);
-        if (!DefineDataElement(cx, res, i++, element))
-            return false;
-    } while (true);
-
-    args.rval().setObject(*res);
-    return true;
-}
-
-/**************** RelativeTimeFormat *****************/
-
-const ClassOps RelativeTimeFormatObject::classOps_ = {
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* enumerate */
-    nullptr, /* newEnumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    RelativeTimeFormatObject::finalize
-};
-
-const Class RelativeTimeFormatObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(RelativeTimeFormatObject::SLOT_COUNT) |
-    JSCLASS_FOREGROUND_FINALIZE,
-    &RelativeTimeFormatObject::classOps_
-};
-
-#if JS_HAS_TOSOURCE
-static bool
-relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setString(cx->names().RelativeTimeFormat);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
-    JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
-    JS_FS_END
-};
-
-static const JSFunctionSpec relativeTimeFormat_methods[] = {
-    JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
-    JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
-#endif
-    JS_FS_END
-};
-
-/**
- * RelativeTimeFormat constructor.
- * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
- */
-static bool
-RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Step 1.
-    if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
-        return false;
-
-    // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
-    RootedObject proto(cx);
-    if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
-        return false;
-
-    if (!proto) {
-        proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
-        if (!proto)
-            return false;
-    }
-
-    Rooted<RelativeTimeFormatObject*> relativeTimeFormat(cx);
-    relativeTimeFormat = NewObjectWithGivenProto<RelativeTimeFormatObject>(cx, proto);
-    if (!relativeTimeFormat)
-        return false;
-
-    relativeTimeFormat->setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT, NullValue());
-    relativeTimeFormat->setReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
-
-    HandleValue locales = args.get(0);
-    HandleValue options = args.get(1);
-
-    // Step 3.
-    if (!intl::InitializeObject(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat,
-                                locales, options))
-    {
-        return false;
-    }
-
-    args.rval().setObject(*relativeTimeFormat);
-    return true;
-}
-
-void
-RelativeTimeFormatObject::finalize(FreeOp* fop, JSObject* obj)
-{
-    MOZ_ASSERT(fop->onActiveCooperatingThread());
-
-    const Value& slot =
-        obj->as<RelativeTimeFormatObject>().getReservedSlot(RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT);
-    if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
-        ureldatefmt_close(rtf);
-}
-
-static JSObject*
-CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
-{
-    RootedFunction ctor(cx);
-    ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
-    if (!ctor)
-        return nullptr;
-
-    RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
-    if (!proto)
-        return nullptr;
-
-    if (!LinkConstructorAndPrototype(cx, ctor, proto))
-        return nullptr;
-
-    if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
-        return nullptr;
-
-    if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
-        return nullptr;
-
-    RootedValue ctorValue(cx, ObjectValue(*ctor));
-    if (!DefineDataProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, 0))
-        return nullptr;
-
-    return proto;
-}
-
-/* static */ bool
-js::GlobalObject::addRelativeTimeFormatConstructor(JSContext* cx, HandleObject intl)
-{
-    Handle<GlobalObject*> global = cx->global();
-
-    {
-        const HeapSlot& slot = global->getReservedSlotRef(RELATIVE_TIME_FORMAT_PROTO);
-        if (!slot.isUndefined()) {
-            MOZ_ASSERT(slot.isObject());
-            JS_ReportErrorASCII(cx,
-                                "the RelativeTimeFormat constructor can't be added "
-                                "multiple times in the same global");
-            return false;
-        }
-    }
-
-    JSObject* relativeTimeFormatProto = CreateRelativeTimeFormatPrototype(cx, intl, global);
-    if (!relativeTimeFormatProto)
-        return false;
-
-    global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFormatProto));
-    return true;
-}
-
-bool
-js::AddRelativeTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
-{
-    return GlobalObject::addRelativeTimeFormatConstructor(cx, intl);
-}
-
-
-bool
-js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 0);
-
-    RootedValue result(cx);
-    // We're going to use ULocale availableLocales as per ICU recommendation:
-    // https://ssl.icu-project.org/trac/ticket/12756
-    if (!GetAvailableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
-        return false;
-    args.rval().set(result);
-    return true;
-}
-
-enum class RelativeTimeType
-{
-    /**
-     * Only strings with numeric components like `1 day ago`.
-     */
-    Numeric,
-    /**
-     * Natural-language strings like `yesterday` when possible,
-     * otherwise strings with numeric components as in `7 months ago`.
-     */
-    Text,
-};
-
-bool
-js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-
-    RootedObject relativeTimeFormat(cx, &args[0].toObject());
-
-    double t = args[1].toNumber();
-
-    RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
-    if (!internals)
-        return false;
-
-    RootedValue value(cx);
-
-    if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
-        return false;
-    JSAutoByteString locale(cx, value.toString());
-    if (!locale)
-        return false;
-
-    if (!GetProperty(cx, internals, internals, cx->names().style, &value))
-        return false;
-
-    UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
-    {
-        JSLinearString* style = value.toString()->ensureLinear(cx);
-        if (!style)
-            return false;
-
-        if (StringEqualsAscii(style, "short")) {
-            relDateTimeStyle = UDAT_STYLE_SHORT;
-        } else if (StringEqualsAscii(style, "narrow")) {
-            relDateTimeStyle = UDAT_STYLE_NARROW;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(style, "long"));
-            relDateTimeStyle = UDAT_STYLE_LONG;
-        }
-    }
-
-    if (!GetProperty(cx, internals, internals, cx->names().type, &value))
-        return false;
-
-    RelativeTimeType relDateTimeType;
-    {
-        JSLinearString* type = value.toString()->ensureLinear(cx);
-        if (!type)
-            return false;
-
-        if (StringEqualsAscii(type, "text")) {
-            relDateTimeType = RelativeTimeType::Text;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(type, "numeric"));
-            relDateTimeType = RelativeTimeType::Numeric;
-        }
-    }
-
-    URelativeDateTimeUnit relDateTimeUnit;
-    {
-        JSLinearString* unit = args[2].toString()->ensureLinear(cx);
-        if (!unit)
-            return false;
-
-        if (StringEqualsAscii(unit, "second")) {
-            relDateTimeUnit = UDAT_REL_UNIT_SECOND;
-        } else if (StringEqualsAscii(unit, "minute")) {
-            relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
-        } else if (StringEqualsAscii(unit, "hour")) {
-            relDateTimeUnit = UDAT_REL_UNIT_HOUR;
-        } else if (StringEqualsAscii(unit, "day")) {
-            relDateTimeUnit = UDAT_REL_UNIT_DAY;
-        } else if (StringEqualsAscii(unit, "week")) {
-            relDateTimeUnit = UDAT_REL_UNIT_WEEK;
-        } else if (StringEqualsAscii(unit, "month")) {
-            relDateTimeUnit = UDAT_REL_UNIT_MONTH;
-        } else if (StringEqualsAscii(unit, "quarter")) {
-            relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(unit, "year"));
-            relDateTimeUnit = UDAT_REL_UNIT_YEAR;
-        }
-    }
-
-    // ICU doesn't handle -0 well: work around this by converting it to +0.
-    // See: http://bugs.icu-project.org/trac/ticket/12936
-    if (IsNegativeZero(t))
-        t = +0.0;
-
-    UErrorCode status = U_ZERO_ERROR;
-    URelativeDateTimeFormatter* rtf =
-        ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
-                         UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
-
-    JSString* str =
-        CallICU(cx, [rtf, t, relDateTimeUnit, relDateTimeType](UChar* chars, int32_t size,
-                                                               UErrorCode* status)
-        {
-            auto fmt = relDateTimeType == RelativeTimeType::Text
-                       ? ureldatefmt_format
-                       : ureldatefmt_formatNumeric;
-            return fmt(rtf, t, relDateTimeUnit, chars, size, status);
-        });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-
-/******************** String ********************/
-
-static const char*
-CaseMappingLocale(JSContext* cx, JSString* str)
-{
-    JSLinearString* locale = str->ensureLinear(cx);
-    if (!locale)
-        return nullptr;
-
-    MOZ_ASSERT(locale->length() >= 2, "locale is a valid language tag");
-
-    // Lithuanian, Turkish, and Azeri have language dependent case mappings.
-    static const char languagesWithSpecialCasing[][3] = { "lt", "tr", "az" };
-
-    // All strings in |languagesWithSpecialCasing| are of length two, so we
-    // only need to compare the first two characters to find a matching locale.
-    // ES2017 Intl, ยง9.2.2 BestAvailableLocale
-    if (locale->length() == 2 || locale->latin1OrTwoByteChar(2) == '-') {
-        for (const auto& language : languagesWithSpecialCasing) {
-            if (locale->latin1OrTwoByteChar(0) == language[0] &&
-                locale->latin1OrTwoByteChar(1) == language[1])
-            {
-                return language;
-            }
-        }
-    }
-
-    return ""; // ICU root locale
-}
-
-bool
-js::intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isString());
-
-    RootedString string(cx, args[0].toString());
-
-    const char* locale = CaseMappingLocale(cx, args[1].toString());
-    if (!locale)
-        return false;
-
-    // Call String.prototype.toLowerCase() for language independent casing.
-    if (StringsAreEqual(locale, "")) {
-        JSString* str = js::StringToLowerCase(cx, string);
-        if (!str)
-            return false;
-
-        args.rval().setString(str);
-        return true;
-    }
-
-    AutoStableStringChars inputChars(cx);
-    if (!inputChars.initTwoByte(cx, string))
-        return false;
-    mozilla::Range<const char16_t> input = inputChars.twoByteRange();
-
-    // Maximum case mapping length is three characters.
-    static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
-                  "Case conversion doesn't overflow int32_t indices");
-
-    JSString* str = CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
-        return u_strToLower(chars, size, input.begin().get(), input.length(), locale, status);
-    });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-bool
-js::intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isString());
-
-    RootedString string(cx, args[0].toString());
-
-    const char* locale = CaseMappingLocale(cx, args[1].toString());
-    if (!locale)
-        return false;
-
-    // Call String.prototype.toUpperCase() for language independent casing.
-    if (StringsAreEqual(locale, "")) {
-        JSString* str = js::StringToUpperCase(cx, string);
-        if (!str)
-            return false;
-
-        args.rval().setString(str);
-        return true;
-    }
-
-    AutoStableStringChars inputChars(cx);
-    if (!inputChars.initTwoByte(cx, string))
-        return false;
-    mozilla::Range<const char16_t> input = inputChars.twoByteRange();
-
-    // Maximum case mapping length is three characters.
-    static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
-                  "Case conversion doesn't overflow int32_t indices");
-
-    JSString* str = CallICU(cx, [&input, locale](UChar* chars, int32_t size, UErrorCode* status) {
-        return u_strToUpper(chars, size, input.begin().get(), input.length(), locale, status);
-    });
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
-}
-
-
-/******************** Intl ********************/
-
-bool
-js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    UErrorCode status = U_ZERO_ERROR;
-    const UChar* uTimeZone = nullptr;
-    int32_t uTimeZoneLength = 0;
-    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.ptr(), UCAL_DEFAULT, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UCalendar, ucal_close> toClose(cal);
-
-    RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
-    if (!info)
-        return false;
-
-    RootedValue v(cx);
-    int32_t firstDayOfWeek = ucal_getAttribute(cal, UCAL_FIRST_DAY_OF_WEEK);
-    v.setInt32(firstDayOfWeek);
-
-    if (!DefineDataProperty(cx, info, cx->names().firstDayOfWeek, v))
-        return false;
-
-    int32_t minDays = ucal_getAttribute(cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK);
-    v.setInt32(minDays);
-    if (!DefineDataProperty(cx, info, cx->names().minDays, v))
-        return false;
-
-    UCalendarWeekdayType prevDayType = ucal_getDayOfWeekType(cal, UCAL_SATURDAY, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-
-    RootedValue weekendStart(cx), weekendEnd(cx);
-
-    for (int i = UCAL_SUNDAY; i <= UCAL_SATURDAY; i++) {
-        UCalendarDaysOfWeek dayOfWeek = static_cast<UCalendarDaysOfWeek>(i);
-        UCalendarWeekdayType type = ucal_getDayOfWeekType(cal, dayOfWeek, &status);
-        if (U_FAILURE(status)) {
-            intl::ReportInternalError(cx);
-            return false;
-        }
-
-        if (prevDayType != type) {
-            switch (type) {
-              case UCAL_WEEKDAY:
-                // If the first Weekday after Weekend is Sunday (1),
-                // then the last Weekend day is Saturday (7).
-                // Otherwise we'll just take the previous days number.
-                weekendEnd.setInt32(i == 1 ? 7 : i - 1);
-                break;
-              case UCAL_WEEKEND:
-                weekendStart.setInt32(i);
-                break;
-              case UCAL_WEEKEND_ONSET:
-              case UCAL_WEEKEND_CEASE:
-                // At the time this code was added, ICU apparently never behaves this way,
-                // so just throw, so that users will report a bug and we can decide what to
-                // do.
-                intl::ReportInternalError(cx);
-                return false;
-              default:
-                break;
-            }
-        }
-
-        prevDayType = type;
-    }
-
-    MOZ_ASSERT(weekendStart.isInt32());
-    MOZ_ASSERT(weekendEnd.isInt32());
-
-    if (!DefineDataProperty(cx, info, cx->names().weekendStart, weekendStart))
-        return false;
-
-    if (!DefineDataProperty(cx, info, cx->names().weekendEnd, weekendEnd))
-        return false;
-
-    args.rval().setObject(*info);
-    return true;
-}
-
-static void
-ReportBadKey(JSContext* cx, const Range<const JS::Latin1Char>& range)
-{
-    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
-                               range.begin().get());
-}
-
-static void
-ReportBadKey(JSContext* cx, const Range<const char16_t>& range)
-{
-    JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY,
-                           range.begin().get());
-}
-
-template<typename ConstChar>
-static bool
-MatchPart(RangedPtr<ConstChar> iter, const RangedPtr<ConstChar> end,
-          const char* part, size_t partlen)
-{
-    for (size_t i = 0; i < partlen; iter++, i++) {
-        if (iter == end || *iter != part[i])
-            return false;
-    }
-
-    return true;
-}
-
-template<typename ConstChar, size_t N>
-inline bool
-MatchPart(RangedPtr<ConstChar>* iter, const RangedPtr<ConstChar> end, const char (&part)[N])
-{
-    if (!MatchPart(*iter, end, part, N - 1))
-        return false;
-
-    *iter += N - 1;
-    return true;
-}
-
-enum class DisplayNameStyle
-{
-    Narrow,
-    Short,
-    Long,
-};
-
-template<typename ConstChar>
-static JSString*
-ComputeSingleDisplayName(JSContext* cx, UDateFormat* fmt, UDateTimePatternGenerator* dtpg,
-                         DisplayNameStyle style, const Range<ConstChar>& pattern)
-{
-    RangedPtr<ConstChar> iter = pattern.begin();
-    const RangedPtr<ConstChar> end = pattern.end();
-
-    auto MatchSlash = [cx, pattern, &iter, end]() {
-        if (MOZ_LIKELY(iter != end && *iter == '/')) {
-            iter++;
-            return true;
-        }
-
-        ReportBadKey(cx, pattern);
-        return false;
-    };
-
-    if (!MatchPart(&iter, end, "dates")) {
-        ReportBadKey(cx, pattern);
-        return nullptr;
-    }
-
-    if (!MatchSlash())
-        return nullptr;
-
-    if (MatchPart(&iter, end, "fields")) {
-        if (!MatchSlash())
-            return nullptr;
-
-        UDateTimePatternField fieldType;
-
-        if (MatchPart(&iter, end, "year")) {
-            fieldType = UDATPG_YEAR_FIELD;
-        } else if (MatchPart(&iter, end, "month")) {
-            fieldType = UDATPG_MONTH_FIELD;
-        } else if (MatchPart(&iter, end, "week")) {
-            fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
-        } else if (MatchPart(&iter, end, "day")) {
-            fieldType = UDATPG_DAY_FIELD;
-        } else {
-            ReportBadKey(cx, pattern);
-            return nullptr;
-        }
-
-        // This part must be the final part with no trailing data.
-        if (iter != end) {
-            ReportBadKey(cx, pattern);
-            return nullptr;
-        }
-
-        int32_t resultSize;
-        const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
-        MOZ_ASSERT(resultSize >= 0);
-
-        return NewStringCopyN<CanGC>(cx, value, size_t(resultSize));
-    }
-
-    if (MatchPart(&iter, end, "gregorian")) {
-        if (!MatchSlash())
-            return nullptr;
-
-        UDateFormatSymbolType symbolType;
-        int32_t index;
-
-        if (MatchPart(&iter, end, "months")) {
-            if (!MatchSlash())
-                return nullptr;
-
-            switch (style) {
-              case DisplayNameStyle::Narrow:
-                symbolType = UDAT_STANDALONE_NARROW_MONTHS;
-                break;
-
-              case DisplayNameStyle::Short:
-                symbolType = UDAT_STANDALONE_SHORT_MONTHS;
-                break;
-
-              case DisplayNameStyle::Long:
-                symbolType = UDAT_STANDALONE_MONTHS;
-                break;
-            }
-
-            if (MatchPart(&iter, end, "january")) {
-                index = UCAL_JANUARY;
-            } else if (MatchPart(&iter, end, "february")) {
-                index = UCAL_FEBRUARY;
-            } else if (MatchPart(&iter, end, "march")) {
-                index = UCAL_MARCH;
-            } else if (MatchPart(&iter, end, "april")) {
-                index = UCAL_APRIL;
-            } else if (MatchPart(&iter, end, "may")) {
-                index = UCAL_MAY;
-            } else if (MatchPart(&iter, end, "june")) {
-                index = UCAL_JUNE;
-            } else if (MatchPart(&iter, end, "july")) {
-                index = UCAL_JULY;
-            } else if (MatchPart(&iter, end, "august")) {
-                index = UCAL_AUGUST;
-            } else if (MatchPart(&iter, end, "september")) {
-                index = UCAL_SEPTEMBER;
-            } else if (MatchPart(&iter, end, "october")) {
-                index = UCAL_OCTOBER;
-            } else if (MatchPart(&iter, end, "november")) {
-                index = UCAL_NOVEMBER;
-            } else if (MatchPart(&iter, end, "december")) {
-                index = UCAL_DECEMBER;
-            } else {
-                ReportBadKey(cx, pattern);
-                return nullptr;
-            }
-        } else if (MatchPart(&iter, end, "weekdays")) {
-            if (!MatchSlash())
-                return nullptr;
-
-            switch (style) {
-              case DisplayNameStyle::Narrow:
-                symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
-                break;
-
-              case DisplayNameStyle::Short:
-                symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
-                break;
-
-              case DisplayNameStyle::Long:
-                symbolType = UDAT_STANDALONE_WEEKDAYS;
-                break;
-            }
-
-            if (MatchPart(&iter, end, "monday")) {
-                index = UCAL_MONDAY;
-            } else if (MatchPart(&iter, end, "tuesday")) {
-                index = UCAL_TUESDAY;
-            } else if (MatchPart(&iter, end, "wednesday")) {
-                index = UCAL_WEDNESDAY;
-            } else if (MatchPart(&iter, end, "thursday")) {
-                index = UCAL_THURSDAY;
-            } else if (MatchPart(&iter, end, "friday")) {
-                index = UCAL_FRIDAY;
-            } else if (MatchPart(&iter, end, "saturday")) {
-                index = UCAL_SATURDAY;
-            } else if (MatchPart(&iter, end, "sunday")) {
-                index = UCAL_SUNDAY;
-            } else {
-                ReportBadKey(cx, pattern);
-                return nullptr;
-            }
-        } else if (MatchPart(&iter, end, "dayperiods")) {
-            if (!MatchSlash())
-                return nullptr;
-
-            symbolType = UDAT_AM_PMS;
-
-            if (MatchPart(&iter, end, "am")) {
-                index = UCAL_AM;
-            } else if (MatchPart(&iter, end, "pm")) {
-                index = UCAL_PM;
-            } else {
-                ReportBadKey(cx, pattern);
-                return nullptr;
-            }
-        } else {
-            ReportBadKey(cx, pattern);
-            return nullptr;
-        }
-
-        // This part must be the final part with no trailing data.
-        if (iter != end) {
-            ReportBadKey(cx, pattern);
-            return nullptr;
-        }
-
-        return CallICU(cx, [fmt, symbolType, index](UChar* chars, int32_t size,
-                                                    UErrorCode* status)
-        {
-            return udat_getSymbols(fmt, symbolType, index, chars, size, status);
-        });
-    }
-
-    ReportBadKey(cx, pattern);
-    return nullptr;
-}
-
-bool
-js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-
-    // 1. Assert: locale is a string.
-    RootedString str(cx, args[0].toString());
-    JSAutoByteString locale;
-    if (!locale.encodeUtf8(cx, str))
-        return false;
-
-    // 2. Assert: style is a string.
-    DisplayNameStyle dnStyle;
-    {
-        JSLinearString* style = args[1].toString()->ensureLinear(cx);
-        if (!style)
-            return false;
-
-        if (StringEqualsAscii(style, "narrow")) {
-            dnStyle = DisplayNameStyle::Narrow;
-        } else if (StringEqualsAscii(style, "short")) {
-            dnStyle = DisplayNameStyle::Short;
-        } else {
-            MOZ_ASSERT(StringEqualsAscii(style, "long"));
-            dnStyle = DisplayNameStyle::Long;
-        }
-    }
-
-    // 3. Assert: keys is an Array.
-    RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
-    if (!keys)
-        return false;
-
-    // 4. Let result be ArrayCreate(0).
-    RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
-    if (!result)
-        return false;
-
-    UErrorCode status = U_ZERO_ERROR;
-
-    UDateFormat* fmt =
-        udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.ptr()),
-        nullptr, 0, nullptr, 0, &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
-
-    // UDateTimePatternGenerator will be needed for translations of date and
-    // time fields like "month", "week", "day" etc.
-    UDateTimePatternGenerator* dtpg = udatpg_open(IcuLocale(locale.ptr()), &status);
-    if (U_FAILURE(status)) {
-        intl::ReportInternalError(cx);
-        return false;
-    }
-    ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
-
-    // 5. For each element of keys,
-    RootedString keyValStr(cx);
-    RootedValue v(cx);
-    for (uint32_t i = 0; i < keys->length(); i++) {
-        if (!GetElement(cx, keys, keys, i, &v))
-            return false;
-
-        keyValStr = v.toString();
-
-        AutoStableStringChars stablePatternChars(cx);
-        if (!stablePatternChars.init(cx, keyValStr))
-            return false;
-
-        // 5.a. Perform an implementation dependent algorithm to map a key to a
-        //      corresponding display name.
-        JSString* displayName =
-            stablePatternChars.isLatin1()
-            ? ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, stablePatternChars.latin1Range())
-            : ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle, stablePatternChars.twoByteRange());
-        if (!displayName)
-            return false;
-
-        // 5.b. Append the result string to result.
-        v.setString(displayName);
-        if (!DefineDataElement(cx, result, i, v))
-            return false;
-    }
-
-    // 6. Return result.
-    args.rval().setObject(*result);
-    return true;
-}
-
-bool
-js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-
-    JSAutoByteString locale(cx, args[0].toString());
-    if (!locale)
-        return false;
-
-    RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
-    if (!info)
-        return false;
-
-    if (!DefineDataProperty(cx, info, cx->names().locale, args[0]))
-        return false;
-
-    bool rtl = uloc_isRightToLeft(IcuLocale(locale.ptr()));
-
-    RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
-
-    if (!DefineDataProperty(cx, info, cx->names().direction, dir))
-        return false;
-
-    args.rval().setObject(*info);
-    return true;
-}
-
-const Class js::IntlClass = {
-    js_Object_str,
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Intl)
-};
-
-#if JS_HAS_TOSOURCE
-static bool
-intl_toSource(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setString(cx->names().Intl);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec intl_static_methods[] = {
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,  intl_toSource,        0, 0),
-#endif
-    JS_SELF_HOSTED_FN("getCanonicalLocales", "Intl_getCanonicalLocales", 1, 0),
-    JS_FS_END
-};
-
-/**
- * Initializes the Intl Object and its standard built-in properties.
- * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
- */
-/* static */ bool
-GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
-{
-    RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
-    if (!proto)
-        return false;
-
-    // The |Intl| object is just a plain object with some "static" function
-    // properties and some constructor properties.
-    RootedObject intl(cx, NewObjectWithGivenProto(cx, &IntlClass, proto, SingletonObject));
-    if (!intl)
-        return false;
-
-    // Add the static functions.
-    if (!JS_DefineFunctions(cx, intl, intl_static_methods))
-        return false;
-
-    // Add the constructor properties, computing and returning the relevant
-    // prototype objects needed below.
-    RootedObject collatorProto(cx, CreateCollatorPrototype(cx, intl, global));
-    if (!collatorProto)
-        return false;
-    RootedObject dateTimeFormatProto(cx), dateTimeFormat(cx);
-    dateTimeFormatProto = CreateDateTimeFormatPrototype(cx, intl, global, &dateTimeFormat, DateTimeFormatOptions::Standard);
-    if (!dateTimeFormatProto)
-        return false;
-    RootedObject numberFormatProto(cx), numberFormat(cx);
-    numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
-    if (!numberFormatProto)
-        return false;
-    RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
-    if (!pluralRulesProto)
-        return false;
-
-    // The |Intl| object is fully set up now, so define the global property.
-    RootedValue intlValue(cx, ObjectValue(*intl));
-    if (!DefineDataProperty(cx, global, cx->names().Intl, intlValue, JSPROP_RESOLVING))
-        return false;
-
-    // Now that the |Intl| object is successfully added, we can OOM-safely fill
-    // in all relevant reserved global slots.
-
-    // Cache the various prototypes, for use in creating instances of these
-    // objects with the proper [[Prototype]] as "the original value of
-    // |Intl.Collator.prototype|" and similar.  For builtin classes like
-    // |String.prototype| we have |JSProto_*| that enables
-    // |getPrototype(JSProto_*)|, but that has global-object-property-related
-    // baggage we don't need or want, so we use one-off reserved slots.
-    global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
-    global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
-    global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
-    global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
-    global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
-    global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
-
-    // Also cache |Intl| to implement spec language that conditions behavior
-    // based on values being equal to "the standard built-in |Intl| object".
-    // Use |setConstructor| to correspond with |JSProto_Intl|.
-    //
-    // XXX We should possibly do a one-off reserved slot like above.
-    global->setConstructor(JSProto_Intl, ObjectValue(*intl));
-    return true;
-}
-
-JSObject*
-js::InitIntlClass(JSContext* cx, HandleObject obj)
-{
-    Handle<GlobalObject*> global = obj.as<GlobalObject>();
-    if (!GlobalObject::initIntlObject(cx, global))
-        return nullptr;
-
-    return &global->getConstructor(JSProto_Intl).toObject();
-}
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/intl/Collator.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef builtin_intl_Collator_h
+#define builtin_intl_Collator_h
+
+#include "mozilla/Attributes.h"
+
+#include <stdint.h>
+
+#include "builtin/SelfHostingDefines.h"
+#include "js/Class.h"
+#include "vm/NativeObject.h"
+
+namespace js {
+
+class FreeOp;
+class GlobalObject;
+
+/******************** Collator ********************/
+
+class CollatorObject : public NativeObject
+{
+  public:
+    static const Class class_;
+
+    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);
+};
+
+extern JSObject*
+CreateCollatorPrototype(JSContext* cx, JS::Handle<JSObject*> Intl,
+                        JS::Handle<GlobalObject*> global);
+
+/**
+ * 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
+intl_Collator(JSContext* cx, unsigned argc, JS::Value* vp);
+
+/**
+ * Returns an object indicating the supported locales for collation
+ * 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_Collator_availableLocales()
+ */
+extern MOZ_MUST_USE bool
+intl_Collator_availableLocales(JSContext* cx, unsigned argc, JS::Value* vp);
+
+/**
+ * Returns an array with the collation type identifiers per Unicode
+ * Technical Standard 35, Unicode Locale Data Markup Language, for the
+ * collations supported for the given locale. "standard" and "search" are
+ * excluded.
+ *
+ * Usage: collations = intl_availableCollations(locale)
+ */
+extern MOZ_MUST_USE bool
+intl_availableCollations(JSContext* cx, unsigned argc, JS::Value* vp);
+
+/**
+ * Compares x and y (which must be String values), and returns a number less
+ * than 0 if x < y, 0 if x = y, or a number greater than 0 if x > y according
+ * to the sort order for the locale and collation options of the given
+ * Collator.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 10.3.2.
+ *
+ * Usage: result = intl_CompareStrings(collator, x, y)
+ */
+extern MOZ_MUST_USE bool
+intl_CompareStrings(JSContext* cx, unsigned argc, JS::Value* vp);
+
+/**
+ * Returns true if the given locale sorts upper-case before lower-case
+ * characters.
+ *
+ * Usage: result = intl_isUpperCaseFirst(locale)
+ */
+extern MOZ_MUST_USE bool
+intl_isUpperCaseFirst(JSContext* cx, unsigned argc, JS::Value* vp);
+
+} // namespace js
+
+#endif /* builtin_intl_Collator_h */
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/Casting.h"
 
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/Intl.h"
+#include "builtin/intl/Collator.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/MapObject.h"
 #include "builtin/SIMD.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -155,16 +155,17 @@ EXPORTS.js += [
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'builtin/AtomicsObject.cpp',
     'builtin/DataViewObject.cpp',
     'builtin/Eval.cpp',
     'builtin/Intl.cpp',
+    'builtin/intl/Collator.cpp',
     'builtin/intl/CommonFunctions.cpp',
     'builtin/intl/NumberFormat.cpp',
     'builtin/MapObject.cpp',
     'builtin/ModuleObject.cpp',
     'builtin/Object.cpp',
     'builtin/Profilers.cpp',
     'builtin/Promise.cpp',
     'builtin/Reflect.cpp',
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -19,16 +19,17 @@
 #include "jsfun.h"
 #include "jshashutil.h"
 #include "jsiter.h"
 #include "jsstr.h"
 #include "jswrapper.h"
 #include "selfhosted.out.h"
 
 #include "builtin/Intl.h"
+#include "builtin/intl/Collator.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/MapObject.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/Promise.h"
 #include "builtin/Reflect.h"
 #include "builtin/RegExp.h"
 #include "builtin/SelfHostingDefines.h"