Bug 1570370 - Part 3: Switch Intl.Locale from JS to C++ version. r=jwalden
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 11 Oct 2019 19:24:45 +0000
changeset 497316 64993f76caaf72ed630c44da86250d06dda52ccd
parent 497315 4eca32dbe70f9c1d7803c2cb51a266842b4e77ab
child 497317 22ff988b77c6218bd6556702bb926898869b7ecb
push id36682
push userncsoregi@mozilla.com
push dateSat, 12 Oct 2019 09:52:03 +0000
treeherdermozilla-central@06ea2371f897 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1570370
milestone71.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 1570370 - Part 3: Switch Intl.Locale from JS to C++ version. r=jwalden Differential Revision: https://phabricator.services.mozilla.com/D40069
js/src/builtin/intl/CommonFunctions.js
js/src/builtin/intl/Locale.cpp
js/src/builtin/intl/Locale.h
js/src/builtin/intl/Locale.js
js/src/builtin/intl/NativeLocale.cpp
js/src/jit/InlinableNatives.h
js/src/jit/MCallOptimize.cpp
js/src/moz.build
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/intl/CommonFunctions.js
+++ b/js/src/builtin/intl/CommonFunctions.js
@@ -564,92 +564,16 @@ function parseLanguageTag(locale) {
     if (hasOwn(ts.localeLowercase, grandfatheredMappings))
         updateGrandfatheredMappings(tagObj);
 
     // Return if the complete input was successfully parsed.
     return tagObj;
 }
 
 /**
- * Returns the input normalized to lower case if it matches the
- * 'unicode_language_subtag' production. Otherwise returns null.
- */
-function parseStandaloneLanguage(language) {
-    // unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
-    var length = language.length;
-    if (length < 2 || length === 4 || length > 8 || !IsASCIIAlphaString(language)) {
-        // Four character language subtags are not allowed in Unicode BCP 47
-        // locale identifiers. Also see the comparison to Unicode CLDR locale
-        // identifiers in <https://unicode.org/reports/tr35/#BCP_47_Conformance>.
-        return null;
-    }
-
-    return callFunction(std_String_toLowerCase, language);
-}
-
-/**
- * Returns the input normalized to title case if it matches the
- * 'unicode_script_subtag' production. Otherwise returns null.
- */
-function parseStandaloneScript(script) {
-    // unicode_script_subtag = alpha{4} ;
-    if (script.length !== 4 || !IsASCIIAlphaString(script)) {
-        return null;
-    }
-
-    // The first character of a script code needs to be capitalized.
-    // "hans" -> "Hans"
-    return callFunction(std_String_toUpperCase, script[0]) +
-           callFunction(std_String_toLowerCase, Substring(script, 1, script.length - 1));
-}
-
-/**
- * Returns the input normalized to upper case if it matches the
- * 'unicode_region_subtag' production. Otherwise returns null.
- */
-function parseStandaloneRegion(region) {
-    // unicode_region_subtag = (alpha{2} | digit{3}) ;
-    var length = region.length;
-    if ((length !== 2 || !IsASCIIAlphaString(region)) &&
-        (length !== 3 || !IsASCIIDigitString(region)))
-    {
-        return null;
-    }
-
-    // Region codes need to be in upper-case. "bu" -> "BU"
-    return callFunction(std_String_toUpperCase, region);
-}
-
-/**
- * Returns the input normalized to lower case if it can be parsed as a
- * '(3*8alphanum) *("-" (3*8alphanum))' subtag sequence. Otherwise returns
- * null.
- */
-function parseStandaloneUnicodeExtensionType(type) {
-    // Reuse the BCP 47 parser for Unicode extension types.
-    var ts = new BCP47TokenStream(type);
-    NEXT_TOKEN_OR_RETURN_NULL(ts);
-
-    // Unicode extension 'type' subtags must match the following ABNF.
-    //
-    // type     = (3*8alphanum) *("-" (3*8alphanum))
-    // alphanum = (ALPHA / DIGIT)       ; letters and numbers
-    // ALPHA    = %x41-5A / %x61-7A     ; A-Z / a-z
-    // DIGIT    = %x30-39               ; 0-9
-    do {
-        if (ts.tokenLength < 3 || ts.tokenLength > 8)
-            return null;
-
-        NEXT_TOKEN_OR_RETURN_NULL(ts);
-    } while (ts.token !== NONE);
-
-    return ts.localeLowercase;
-}
-
-/**
  * Return the locale and fields components of the given valid Transform
  * extension subtag.
  */
 function TransformExtensionComponents(extension) {
     assert(typeof extension === "string", "extension is a String value");
     assert(callFunction(std_String_startsWith, extension, "t-"),
            "extension starts with 't-'");
 
@@ -1232,30 +1156,16 @@ function IsASCIIAlphaString(s) {
         var c = callFunction(std_String_charCodeAt, s, i);
         if (!((0x41 <= c && c <= 0x5A) || (0x61 <= c && c <= 0x7A)))
             return false;
     }
     return true;
 }
 
 /**
- * Returns true if the input contains only ASCII digit characters.
- */
-function IsASCIIDigitString(s) {
-    assert(typeof s === "string", "IsASCIIDigitString");
-
-    for (var i = 0; i < s.length; i++) {
-        var c = callFunction(std_String_charCodeAt, s, i);
-        if (!(0x30 <= c && c <= 0x39))
-            return false;
-    }
-    return true;
-}
-
-/**
  * Validates and canonicalizes the given language tag.
  */
 function ValidateAndCanonicalizeLanguageTag(locale) {
     assert(typeof locale === "string", "ValidateAndCanonicalizeLanguageTag");
 
     // Handle the common case (a standalone language) first.
     // Only the following Unicode BCP 47 locale identifier subset is accepted:
     //   unicode_locale_id = unicode_language_id
@@ -1438,19 +1348,19 @@ function CanonicalizeLocaleList(locales)
     // Step 1.
     if (locales === undefined)
         return [];
 
     // Step 3 (and the remaining steps).
     if (typeof locales === "string")
         return [ValidateAndCanonicalizeLanguageTag(locales)];
 
-    var unboxedLocale = callFunction(unboxLocaleOrNull, locales);
+    var unboxedLocale = LocaleToStringOrNull(locales);
     if (unboxedLocale !== null)
-        return [StringFromLanguageTagObject(unboxedLocale.locale)]
+        return [unboxedLocale];
 
     // Step 2.
     var seen = [];
 
     // Step 4.
     var O = ToObject(locales);
 
     // Step 5.
@@ -1466,19 +1376,19 @@ function CanonicalizeLocaleList(locales)
             // Step 7.c.i.
             var kValue = O[k];
 
             // Step 7.c.ii.
             if (!(typeof kValue === "string" || IsObject(kValue)))
                 ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
 
             // Steps 7.c.iii-iv.
-            var unboxedLocale = callFunction(unboxLocaleOrNull, kValue);
+            var unboxedLocale = LocaleToStringOrNull(kValue);
             var tag = unboxedLocale !== null
-                      ? StringFromLanguageTagObject(unboxedLocale.locale)
+                      ? unboxedLocale
                       : ValidateAndCanonicalizeLanguageTag(ToString(kValue));
 
             // Step 7.c.v.
             if (callFunction(ArrayIndexOf, seen, tag) === -1)
                 _DefineDataProperty(seen, seen.length, tag);
         }
 
         // Step 7.d.
--- a/js/src/builtin/intl/Locale.cpp
+++ b/js/src/builtin/intl/Locale.cpp
@@ -27,618 +27,36 @@
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
-const JSClass LocaleObject::class_ = {
-    js_Object_str,
-    JSCLASS_HAS_RESERVED_SLOTS(LocaleObject::SLOT_COUNT),
-};
-
-static bool locale_toSource(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-  args.rval().setString(cx->names().Locale);
-  return true;
-}
-
-static const JSFunctionSpec locale_methods[] = {
-    JS_SELF_HOSTED_FN("maximize", "Intl_Locale_maximize", 0, 0),
-    JS_SELF_HOSTED_FN("minimize", "Intl_Locale_minimize", 0, 0),
-    JS_SELF_HOSTED_FN("toString", "Intl_Locale_toString", 0, 0),
-    JS_FN(js_toSource_str, locale_toSource, 0, 0), JS_FS_END};
-
-static const JSPropertySpec locale_properties[] = {
-    JS_SELF_HOSTED_GET("baseName", "$Intl_Locale_baseName_get", 0),
-    JS_SELF_HOSTED_GET("calendar", "$Intl_Locale_calendar_get", 0),
-    JS_SELF_HOSTED_GET("collation", "$Intl_Locale_collation_get", 0),
-    JS_SELF_HOSTED_GET("hourCycle", "$Intl_Locale_hourCycle_get", 0),
-    JS_SELF_HOSTED_GET("caseFirst", "$Intl_Locale_caseFirst_get", 0),
-    JS_SELF_HOSTED_GET("numeric", "$Intl_Locale_numeric_get", 0),
-    JS_SELF_HOSTED_GET("numberingSystem", "$Intl_Locale_numberingSystem_get",
-                       0),
-    JS_SELF_HOSTED_GET("language", "$Intl_Locale_language_get", 0),
-    JS_SELF_HOSTED_GET("script", "$Intl_Locale_script_get", 0),
-    JS_SELF_HOSTED_GET("region", "$Intl_Locale_region_get", 0),
-    JS_STRING_SYM_PS(toStringTag, "Intl.Locale", JSPROP_READONLY),
-    JS_PS_END};
-
-static LocaleObject* CreateLocaleObject(JSContext* cx, HandleObject prototype) {
-  RootedObject proto(cx, prototype);
-  if (!proto) {
-    proto = GlobalObject::getOrCreateLocalePrototype(cx, cx->global());
-    if (!proto) {
-      return nullptr;
-    }
-  }
-
-  LocaleObject* locale = NewObjectWithGivenProto<LocaleObject>(cx, proto);
-  if (!locale) {
-    return nullptr;
-  }
-
-  locale->setReservedSlot(LocaleObject::INTERNALS_SLOT, NullValue());
-
-  return locale;
-}
-
-/**
- * Intl.Locale( tag[, options] )
- */
-static bool Locale(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  // Step 1.
-  if (!ThrowIfNotConstructing(cx, args, "Intl.Locale")) {
-    return false;
-  }
-
-  // Steps 2-6 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
-  RootedObject proto(cx);
-  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
-    return false;
-  }
-
-  Rooted<LocaleObject*> locale(cx, CreateLocaleObject(cx, proto));
-  if (!locale) {
-    return false;
-  }
-
-  HandleValue tag = args.get(0);
-  HandleValue options = args.get(1);
-
-  // Steps 7-37.
-  if (!intl::InitializeObject(cx, locale, cx->names().InitializeLocale, tag,
-                              options)) {
-    return false;
-  }
-
-  // Step 38.
-  args.rval().setObject(*locale);
-  return true;
-}
-
-JSObject* js::CreateLocalePrototype(JSContext* cx, HandleObject Intl,
-                                    Handle<GlobalObject*> global) {
-  RootedFunction ctor(
-      cx, GlobalObject::createConstructor(cx, &Locale, cx->names().Locale, 1));
-  if (!ctor) {
-    return nullptr;
-  }
-
-  RootedObject proto(
-      cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
-  if (!proto) {
-    return nullptr;
-  }
-
-  if (!LinkConstructorAndPrototype(cx, ctor, proto)) {
-    return nullptr;
-  }
-
-  if (!DefinePropertiesAndFunctions(cx, proto, locale_properties,
-                                    locale_methods)) {
-    return nullptr;
-  }
-
-  RootedValue ctorValue(cx, ObjectValue(*ctor));
-  if (!DefineDataProperty(cx, Intl, cx->names().Locale, ctorValue, 0)) {
-    return nullptr;
-  }
-
-  return proto;
-}
-
 /* static */ bool js::GlobalObject::addLocaleConstructor(JSContext* cx,
                                                          HandleObject intl) {
   Handle<GlobalObject*> global = cx->global();
 
   {
-    const Value& proto = global->getReservedSlot(LOCALE_PROTO);
-    if (!proto.isUndefined()) {
-      MOZ_ASSERT(proto.isObject());
-      JS_ReportErrorASCII(cx,
-                          "the Locale constructor can't be added multiple "
-                          "times in the same global");
-      return false;
-    }
-  }
-
-  JSObject* localeProto = CreateLocalePrototype(cx, intl, global);
-  if (!localeProto) {
-    return false;
-  }
-
-  global->setReservedSlot(LOCALE_PROTO, ObjectValue(*localeProto));
-
-  {
     const Value& proto = global->getReservedSlot(NATIVE_LOCALE_PROTO);
     if (!proto.isUndefined()) {
       MOZ_ASSERT(proto.isObject());
       JS_ReportErrorASCII(
           cx,
           "the Locale constructor can't be added multiple times in the"
           "same global");
       return false;
     }
   }
 
-  localeProto = CreateNativeLocalePrototype(cx, intl, global);
+  JSObject* localeProto = CreateNativeLocalePrototype(cx, intl, global);
   if (!localeProto) {
     return false;
   }
 
   global->setReservedSlot(NATIVE_LOCALE_PROTO, ObjectValue(*localeProto));
   return true;
 }
 
 bool js::AddLocaleConstructor(JSContext* cx, JS::Handle<JSObject*> intl) {
   return GlobalObject::addLocaleConstructor(cx, intl);
 }
-
-bool js::intl_CreateUninitializedLocale(JSContext* cx, unsigned argc,
-                                        Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-  MOZ_ASSERT(args.length() == 0);
-
-  LocaleObject* locale = CreateLocaleObject(cx, nullptr);
-  if (!locale) {
-    return false;
-  }
-
-  args.rval().setObject(*locale);
-  return true;
-}
-
-#ifdef DEBUG
-
-template <typename CharT>
-struct LanguageTagValidator {
-  bool operator()(mozilla::Range<const CharT> language) const {
-    // Tell the analysis the |std::all_of| function can't GC.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    // BNF: unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
-    // Canonical form is lower case.
-    return ((2 <= language.length() && language.length() <= 3) ||
-            (5 <= language.length() && language.length() <= 8)) &&
-           std::all_of(language.begin().get(), language.end().get(),
-                       mozilla::IsAsciiLowercaseAlpha<CharT>);
-  }
-};
-
-template <typename CharT>
-struct ScriptTagValidator {
-  bool operator()(mozilla::Range<const CharT> script) const {
-    // Tell the analysis the |std::all_of| function can't GC.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    // BNF: unicode_script_subtag = alpha{4} ;
-    // Canonical form is title case.
-    return script.length() == 4 && mozilla::IsAsciiUppercaseAlpha(script[0]) &&
-           std::all_of(std::next(script.begin().get()), script.end().get(),
-                       mozilla::IsAsciiLowercaseAlpha<CharT>);
-  }
-};
-
-template <typename CharT>
-struct RegionTagValidator {
-  bool operator()(mozilla::Range<const CharT> region) const {
-    // Tell the analysis the |std::all_of| function can't GC.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    // BNF: unicode_region_subtag = (alpha{2} | digit{3}) ;
-    // Canonical form is upper case.
-    return (region.length() == 2 &&
-            std::all_of(region.begin().get(), region.end().get(),
-                        mozilla::IsAsciiUppercaseAlpha<CharT>)) ||
-           (region.length() == 3 &&
-            std::all_of(region.begin().get(), region.end().get(),
-                        mozilla::IsAsciiDigit<CharT>));
-  }
-};
-
-template <template <typename> class Validator, typename CharT>
-static bool IsStructurallyValidSubtag(mozilla::Range<const CharT> subtag) {
-  return Validator<CharT>{}(subtag);
-}
-
-template <template <typename> class Validator>
-static bool IsStructurallyValidSubtag(JSLinearString* subtag) {
-  JS::AutoCheckCannotGC nogc;
-  return subtag->hasLatin1Chars()
-             ? Validator<JS::Latin1Char>{}(subtag->latin1Range(nogc))
-             : Validator<char16_t>{}(subtag->twoByteRange(nogc));
-}
-
-template <typename T>
-static bool IsStructurallyValidLanguageTag(const T& language) {
-  return IsStructurallyValidSubtag<LanguageTagValidator>(language);
-}
-
-template <typename T>
-static bool IsStructurallyValidScriptTag(const T& script) {
-  return IsStructurallyValidSubtag<ScriptTagValidator>(script);
-}
-
-template <typename T>
-static bool IsStructurallyValidRegionTag(const T& region) {
-  return IsStructurallyValidSubtag<RegionTagValidator>(region);
-}
-
-#endif /* DEBUG */
-
-// unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
-static constexpr size_t LanguageTagMaxLength = 8;
-
-// unicode_script_subtag = alpha{4} ;
-static constexpr size_t ScriptTagMaxLength = 4;
-
-// unicode_region_subtag = (alpha{2} | digit{3}) ;
-static constexpr size_t RegionTagMaxLength = 3;
-
-// Zero-terminated ICU Locale ID.
-using LocaleId =
-    js::Vector<char, LanguageTagMaxLength + 1 + ScriptTagMaxLength + 1 +
-                         RegionTagMaxLength + 1>;
-
-struct LocaleSubtags {
-  using Subtag = mozilla::Range<const char>;
-
-  Subtag language;
-  Subtag script;
-  Subtag region;
-
-  LocaleSubtags(Subtag language, Subtag script, Subtag region)
-      : language(language), script(script), region(region) {}
-};
-
-// Split the language, script, and region subtags from the input ICU locale ID.
-//
-// ICU provides |uloc_getLanguage|, |uloc_getScript|, and |uloc_getCountry| to
-// retrieve these subtags, but unfortunately these functions are rather slow, so
-// we use our own implementation.
-static LocaleSubtags GetLocaleSubtags(const LocaleId& localeId) {
-  // Locale ID should be zero-terminated for ICU.
-  MOZ_ASSERT(localeId.back() == '\0');
-
-  using Subtag = LocaleSubtags::Subtag;
-
-  // mozilla::Range uses 'const' members, so we can't assign to it. Use |Maybe|
-  // to defer initialization to workaround this.
-  mozilla::Maybe<Subtag> language, script, region;
-
-  size_t subtagStart = 0;
-  for (size_t i = 0; i < localeId.length(); i++) {
-    char c = localeId[i];
-
-    // Skip over any characters until we hit either separator char or the end.
-    if (c != '_' && c != '-' && c != '\0') {
-      continue;
-    }
-
-    size_t length = i - subtagStart;
-    const char* start = localeId.begin() + subtagStart;
-    if (subtagStart == 0) {
-      // unicode_language_subtag = alpha{2,3} | alpha{5,8} ;
-      if ((2 <= length && length <= 3) || (5 <= length && length <= 8)) {
-        language.emplace(start, length);
-        MOZ_ASSERT(IsStructurallyValidLanguageTag(*language));
-      }
-    } else if (length == 4) {
-      // unicode_script_subtag = alpha{4} ;
-      script.emplace(start, length);
-      MOZ_ASSERT(IsStructurallyValidScriptTag(*script));
-    } else if (length == 2 || length == 3) {
-      // unicode_region_subtag = (alpha{2} | digit{3}) ;
-      region.emplace(start, length);
-      MOZ_ASSERT(IsStructurallyValidRegionTag(*region));
-    } else {
-      // Ignore any trailing variant or extension subtags.
-      break;
-    }
-
-    subtagStart = i + 1;
-  }
-
-  return {language.valueOr(Subtag()), script.valueOr(Subtag()),
-          region.valueOr(Subtag())};
-}
-
-enum class LikelySubtags : bool { Add, Remove };
-
-// Return true iff the input subtags are already maximized resp. minimized.
-static bool HasLikelySubtags(LikelySubtags likelySubtags,
-                             JSLinearString* language, JSLinearString* script,
-                             JSLinearString* region) {
-  // The language tag is already maximized if language, script, and region
-  // subtags are present and no placeholder subtags ("und", "Zzzz", "ZZ")
-  // are used.
-  if (likelySubtags == LikelySubtags::Add) {
-    return !StringEqualsLiteral(language, "und") && script &&
-           !StringEqualsLiteral(script, "Zzzz") && region &&
-           !StringEqualsLiteral(region, "ZZ");
-  }
-
-  // The language tag is already minimized if it only contains a language
-  // subtag whose value is not the placeholder value "und".
-  return !StringEqualsLiteral(language, "und") && !script && !region;
-}
-
-// Create an ICU locale ID from the given language, script, and region subtags.
-static bool CreateLocaleForLikelySubtags(JSLinearString* language,
-                                         JSLinearString* script,
-                                         JSLinearString* region,
-                                         LocaleId& locale) {
-  MOZ_ASSERT(locale.length() == 0);
-
-  auto appendSubtag = [&locale](JSLinearString* subtag) {
-    if (locale.length() > 0 && !locale.append('_')) {
-      return false;
-    }
-    if (!locale.growBy(subtag->length())) {
-      return false;
-    }
-    char* dest = locale.end() - subtag->length();
-    CopyChars(reinterpret_cast<Latin1Char*>(dest), *subtag);
-    return true;
-  };
-
-  // Append the language subtag.
-  if (!appendSubtag(language)) {
-    return false;
-  }
-
-  // Append the script subtag if present.
-  if (script && !appendSubtag(script)) {
-    return false;
-  }
-
-  // Append the region subtag if present.
-  if (region && !appendSubtag(region)) {
-    return false;
-  }
-
-  // Zero-terminated for use with ICU.
-  return locale.append('\0');
-}
-
-template <decltype(uloc_addLikelySubtags) likelySubtagsFn>
-static bool CallLikelySubtags(JSContext* cx, const LocaleId& localeId,
-                              LocaleId& result) {
-  // Locale ID must be zero-terminated before passing it to ICU.
-  MOZ_ASSERT(localeId.back() == '\0');
-  MOZ_ASSERT(result.length() == 0);
-
-  // Ensure there's enough room for the result.
-  MOZ_ALWAYS_TRUE(result.resize(LocaleId::InlineLength));
-
-  int32_t length = intl::CallICU(
-      cx,
-      [&localeId](char* chars, int32_t size, UErrorCode* status) {
-        return likelySubtagsFn(localeId.begin(), chars, size, status);
-      },
-      result);
-  if (length < 0) {
-    return false;
-  }
-
-  MOZ_ASSERT(
-      size_t(length) <= LocaleId::InlineLength,
-      "Unexpected extra subtags were added by ICU. If this assertion ever "
-      "fails, simply remove it and move on like nothing ever happended.");
-
-  // Resize the vector to the actual string length.
-  result.shrinkTo(length);
-
-  // Zero-terminated for use with ICU.
-  return result.append('\0');
-}
-
-// Return the array |[language, script or undefined, region or undefined]|.
-static ArrayObject* CreateLikelySubtagsResult(JSContext* cx,
-                                              HandleValue language,
-                                              HandleValue script,
-                                              HandleValue region) {
-  enum LikelySubtagsResult {
-    LikelySubtagsResult_Language = 0,
-    LikelySubtagsResult_Script,
-    LikelySubtagsResult_Region,
-
-    LikelySubtagsResult_Length
-  };
-
-  ArrayObject* result =
-      NewDenseFullyAllocatedArray(cx, LikelySubtagsResult_Length);
-  if (!result) {
-    return nullptr;
-  }
-  result->setDenseInitializedLength(LikelySubtagsResult_Length);
-
-  result->initDenseElement(LikelySubtagsResult_Language, language);
-  result->initDenseElement(LikelySubtagsResult_Script, script);
-  result->initDenseElement(LikelySubtagsResult_Region, region);
-
-  return result;
-}
-
-// The canonical way to compute the Unicode BCP 47 locale identifier with likely
-// subtags is as follows:
-//
-// 1. Call uloc_forLanguageTag() to transform the input locale identifer into an
-//    ICU locale ID.
-// 2. Call uloc_addLikelySubtags() to add the likely subtags to the locale ID.
-// 3. Call uloc_toLanguageTag() to transform the resulting locale ID back into
-//    a Unicode BCP 47 locale identifier.
-//
-// Since uloc_forLanguageTag() and uloc_toLanguageTag() are both kind of slow
-// and we know, by construction, that the input Unicode BCP 47 locale identifier
-// only contains a language, script, and region subtag, we can avoid both calls
-// if we implement their guts ourselves, see CreateLocaleForLikelySubtags().
-// (Where "slow" means about 50% of the execution time of Locale.p.maximize.)
-static ArrayObject* LikelySubtags(JSContext* cx, LikelySubtags likelySubtags,
-                                  const CallArgs& args) {
-  MOZ_ASSERT(args.length() == 3);
-  MOZ_ASSERT(args[0].isString());
-  MOZ_ASSERT(args[1].isString() || args[1].isUndefined());
-  MOZ_ASSERT(args[2].isString() || args[2].isUndefined());
-
-  RootedLinearString language(cx, args[0].toString()->ensureLinear(cx));
-  if (!language) {
-    return nullptr;
-  }
-  MOZ_ASSERT(IsStructurallyValidLanguageTag(language));
-
-  RootedLinearString script(cx);
-  if (args[1].isString()) {
-    script = args[1].toString()->ensureLinear(cx);
-    if (!script) {
-      return nullptr;
-    }
-    MOZ_ASSERT(IsStructurallyValidScriptTag(script));
-  }
-
-  RootedLinearString region(cx);
-  if (args[2].isString()) {
-    region = args[2].toString()->ensureLinear(cx);
-    if (!region) {
-      return nullptr;
-    }
-    MOZ_ASSERT(IsStructurallyValidRegionTag(region));
-  }
-
-  // Return early if the input is already maximized/minimized.
-  if (HasLikelySubtags(likelySubtags, language, script, region)) {
-    return CreateLikelySubtagsResult(cx, args[0], args[1], args[2]);
-  }
-
-  // Create the locale ID for the input arguments.
-  LocaleId locale(cx);
-  if (!CreateLocaleForLikelySubtags(language, script, region, locale)) {
-    return nullptr;
-  }
-
-  // UTS #35 requires that locale ID is maximized before its likely subtags are
-  // removed, so we need to call uloc_addLikelySubtags() for both cases.
-  // See <https://ssl.icu-project.org/trac/ticket/10220> and
-  // <https://ssl.icu-project.org/trac/ticket/12345>.
-
-  LocaleId localeLikelySubtags(cx);
-
-  // Add likely subtags to the locale ID. When minimizing we can skip adding the
-  // likely subtags for already maximized tags. (When maximizing we've already
-  // verified above that the tag is missing likely subtags.)
-  bool addLikelySubtags =
-      likelySubtags == LikelySubtags::Add ||
-      !HasLikelySubtags(LikelySubtags::Add, language, script, region);
-
-  if (addLikelySubtags) {
-    if (!CallLikelySubtags<uloc_addLikelySubtags>(cx, locale,
-                                                  localeLikelySubtags)) {
-      return nullptr;
-    }
-  }
-
-  // Now that we've succesfully maximized the locale, we can minimize it.
-  if (likelySubtags == LikelySubtags::Remove) {
-    if (addLikelySubtags) {
-      // Copy the maximized subtags back into |locale|.
-      locale = std::move(localeLikelySubtags);
-      localeLikelySubtags = LocaleId(cx);
-    }
-
-    // Remove likely subtags from the locale ID.
-    if (!CallLikelySubtags<uloc_minimizeSubtags>(cx, locale,
-                                                 localeLikelySubtags)) {
-      return nullptr;
-    }
-  }
-
-  // Retrieve the individual language, script, and region subtags from the
-  // resulting locale ID.
-  LocaleSubtags subtags = GetLocaleSubtags(localeLikelySubtags);
-
-  // Convert each subtag back to a JSString. Use |undefined| for absent subtags.
-  auto toValue = [cx](const auto& subtag, MutableHandleValue result) {
-    if (subtag.length() > 0) {
-      JSLinearString* str =
-          NewStringCopyN<CanGC>(cx, subtag.begin().get(), subtag.length());
-      if (!str) {
-        return false;
-      }
-      result.setString(str);
-    } else {
-      result.setUndefined();
-    }
-    return true;
-  };
-
-  RootedValue languageValue(cx);
-  if (!toValue(subtags.language, &languageValue)) {
-    return nullptr;
-  }
-  if (languageValue.isUndefined()) {
-    // ICU replaces "und" with the empty string. Handle this case separately.
-    JSLinearString* str = NewStringCopyZ<CanGC>(cx, "und");
-    if (!str) {
-      return nullptr;
-    }
-    languageValue.setString(str);
-  }
-
-  RootedValue scriptValue(cx);
-  if (!toValue(subtags.script, &scriptValue)) {
-    return nullptr;
-  }
-
-  RootedValue regionValue(cx);
-  if (!toValue(subtags.region, &regionValue)) {
-    return nullptr;
-  }
-
-  // Return the language, script, and region subtags in an array.
-  return CreateLikelySubtagsResult(cx, languageValue, scriptValue, regionValue);
-}
-
-bool js::intl_AddLikelySubtags(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  ArrayObject* result = LikelySubtags(cx, LikelySubtags::Add, args);
-  if (!result) {
-    return false;
-  }
-  args.rval().setObject(*result);
-  return true;
-}
-
-bool js::intl_RemoveLikelySubtags(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  ArrayObject* result = LikelySubtags(cx, LikelySubtags::Remove, args);
-  if (!result) {
-    return false;
-  }
-  args.rval().setObject(*result);
-  return true;
-}
--- a/js/src/builtin/intl/Locale.h
+++ b/js/src/builtin/intl/Locale.h
@@ -12,32 +12,16 @@
 #include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class GlobalObject;
 
-class LocaleObject : public NativeObject {
- public:
-  static const JSClass class_;
-
-  static constexpr uint32_t INTERNALS_SLOT = 0;
-  static constexpr uint32_t SLOT_COUNT = 1;
-
-  static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
-                "INTERNALS_SLOT must match self-hosting define for internals "
-                "object slot");
-};
-
-extern JSObject* CreateLocalePrototype(JSContext* cx,
-                                       JS::Handle<JSObject*> Intl,
-                                       JS::Handle<GlobalObject*> global);
-
 class NativeLocaleObject : public NativeObject {
  public:
   static const JSClass class_;
 
   static constexpr uint32_t LANGUAGE_TAG_SLOT = 0;
   static constexpr uint32_t BASENAME_SLOT = 1;
   static constexpr uint32_t UNICODE_EXTENSION_SLOT = 2;
   static constexpr uint32_t SLOT_COUNT = 3;
@@ -60,36 +44,11 @@ class NativeLocaleObject : public Native
     return getFixedSlot(UNICODE_EXTENSION_SLOT);
   }
 };
 
 extern JSObject* CreateNativeLocalePrototype(JSContext* cx,
                                              JS::Handle<JSObject*> Intl,
                                              JS::Handle<GlobalObject*> global);
 
-/**
- * Creates an uninitialized Intl.Locale object.
- */
-extern MOZ_MUST_USE bool intl_CreateUninitializedLocale(JSContext* cx,
-                                                        unsigned argc,
-                                                        Value* vp);
-
-/**
- * Adds likely subtags to the given canonicalized language BCP47 subtags per
- * the "Add Likely Subtags" algorithm from UTS #35.
- *
- * Usage: subtags = intl_AddLikelySubtags(language, script, region)
- */
-extern MOZ_MUST_USE bool intl_AddLikelySubtags(JSContext* cx, unsigned argc,
-                                               Value* vp);
-
-/**
- * Removes likely subtags from the given canonicalized BCP47 subtags per
- * the "Remove Likely Subtags" algorithm from UTS #35.
- *
- * Usage: subtags = intl_RemoveLikelySubtags(language, script, region)
- */
-extern MOZ_MUST_USE bool intl_RemoveLikelySubtags(JSContext* cx, unsigned argc,
-                                                  Value* vp);
-
 }  // namespace js
 
 #endif /* builtin_intl_Locale_h */
deleted file mode 100644
--- a/js/src/builtin/intl/Locale.js
+++ /dev/null
@@ -1,690 +0,0 @@
-/* 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/. */
-
-/**
- * Intl.Locale internal properties.
- */
-var localeInternalProperties = {
-    relevantExtensionKeys: ["ca", "co", "hc", "kf", "kn", "nu"],
-};
-
-/**
- * ApplyOptionsToTag( tag, options )
- */
-function ApplyOptionsToTag(tagObj, options) {
-    // Steps 1-2 (Already performed in caller).
-
-    // Step 3.
-    var languageOption = GetOption(options, "language", "string", undefined, undefined);
-
-    // Step 4.
-    var language;
-    if (languageOption !== undefined) {
-        language = parseStandaloneLanguage(languageOption);
-        if (language === null)
-            ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "language", languageOption);
-    }
-
-    // Step 5.
-    var scriptOption = GetOption(options, "script", "string", undefined, undefined);
-
-    // Step 6.
-    var script;
-    if (scriptOption !== undefined) {
-        script = parseStandaloneScript(scriptOption);
-        if (script === null)
-            ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "script", scriptOption);
-    }
-
-    // Step 7.
-    var regionOption = GetOption(options, "region", "string", undefined, undefined);
-
-    // Step 8.
-    var region;
-    if (regionOption !== undefined) {
-        region = parseStandaloneRegion(regionOption);
-        if (region === null)
-            ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "region", regionOption);
-    }
-
-    // Step 9 (Already performed in caller).
-
-    // Return early when no subtags were modified.
-    if (language === undefined && script === undefined && region === undefined)
-        return;
-
-    // Step 10.
-    if (language !== undefined)
-        tagObj.language = language;
-
-    // Step 11.
-    if (script !== undefined)
-        tagObj.script = script;
-
-    // Step 12.
-    if (region !== undefined)
-        tagObj.region = region;
-
-    // Replacing the "language" subtag may have turned this locale tag
-    // into a grandfathered language tag. For example consider the
-    // case `new Intl.Locale("en-hakka", {language: "zh"})`, where
-    // "zh-hakka" is a regular grandfathered language tag.
-    if (language !== undefined)
-        updateGrandfatheredMappings(tagObj);
-
-    // Step 13.
-    // Optimized to only update the mappings, because all other canonicalization
-    // steps already happended earlier, so there's no need to repeat them here.
-    updateLocaleIdMappings(tagObj);
-}
-
-/**
- * ApplyUnicodeExtensionToTag( tag, options, relevantExtensionKeys )
- */
-function ApplyUnicodeExtensionToTag(tagObj, options, relevantExtensionKeys) {
-    // Steps 1-2 (Not applicable).
-
-    // Step 3.a
-    // Find the Unicode extension subtag index in |tagObj.extensions|.
-    var extensions = tagObj.extensions;
-    var extensionIndex = -1;
-    for (var i = 0; i < extensions.length; i++) {
-        if (extensions[i][0] === "u") {
-            extensionIndex = i;
-            break;
-        }
-    }
-
-    // If Unicode extensions are neither present in |tagObj| nor in |options|,
-    // we can skip everything below and directly return here.
-    if (extensionIndex < 0) {
-        var hasUnicodeOptions = false;
-        for (var i = 0; i < relevantExtensionKeys.length; i++) {
-            if (options[relevantExtensionKeys[i]] !== undefined) {
-                hasUnicodeOptions = true;
-                break;
-            }
-        }
-        if (!hasUnicodeOptions)
-            return;
-    }
-
-    var attributes, keywords;
-    if (extensionIndex >= 0) {
-        // Step 3.b.
-        var components = UnicodeExtensionComponents(extensions[extensionIndex]);
-
-        // Step 3.c.
-        attributes = components.attributes;
-
-        // Step 3.d.
-        keywords = components.keywords;
-    } else {
-        // Step 4.a.
-        attributes = [];
-
-        // Step 4.b.
-        keywords = [];
-    }
-
-    // Step 5 (Not applicable).
-
-    // Step 6.
-    for (var i = 0; i < relevantExtensionKeys.length; i++) {
-        var key = relevantExtensionKeys[i];
-
-        // Step 6.a.
-        var value = undefined;
-
-        // Steps 6.b-c.
-        var entry = null;
-        for (var j = 0; j < keywords.length; j++) {
-            if (keywords[j].key === key) {
-                entry = keywords[j];
-                value = entry.value;
-                break;
-            }
-        }
-
-        // Step 6.d.
-        assert(hasOwn(key, options), "option value for each extension key present");
-
-        // Step 6.e.
-        var optionsValue = options[key];
-
-        // Step 6.f.
-        if (optionsValue !== undefined) {
-            // Step 6.f.i.
-            assert(typeof optionsValue === "string", "optionsValue is a string");
-
-            // Step 6.f.ii.
-            value = optionsValue;
-
-            // Steps 6.f.iii-iv.
-            if (entry !== null)
-                entry.value = value;
-            else
-                _DefineDataProperty(keywords, keywords.length, {key, value});
-        }
-
-        // Step 6.g (Modify |options| in place).
-        options[key] = value;
-    }
-
-    // Step 7 (Not applicable).
-
-    // Step 8.
-    var newExtension = CanonicalizeUnicodeExtension(attributes, keywords, true);
-
-    // Step 9 (Inlined call to InsertUnicodeExtension).
-    // If the shortcut below step 3.a wasn't taken, the Unicode extension is
-    // definitely non-empty; assert this here.
-    assert(newExtension !== "", "unexpected empty Unicode extension");
-
-    // Update or add the new Unicode extension sequence.
-    if (extensionIndex >= 0) {
-        extensions[extensionIndex] = newExtension;
-    } else {
-        _DefineDataProperty(extensions, extensions.length, newExtension);
-
-        // Extension sequences are sorted by their singleton characters.
-        if (extensions.length > 1) {
-            callFunction(ArraySort, extensions);
-        }
-    }
-
-    // Steps 10-11 (Not applicable).
-}
-
-/**
- * Intl.Locale( tag[, options] )
- */
-function InitializeLocale(locale, tag, options) {
-    // Step 7.
-    if (!(typeof tag === "string" || IsObject(tag)))
-        ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
-
-    // Steps 8-9.
-    var unboxedLocale = callFunction(unboxLocaleOrNull, tag);
-    if (unboxedLocale === null)
-        tag = ToString(tag);
-
-    // Steps 10-11.
-    var hasOptions = options !== undefined;
-    if (hasOptions)
-        options = ToObject(options);
-
-    // Step 12.
-    var tagObj;
-    if (unboxedLocale === null) {
-        tagObj = parseLanguageTag(tag);
-        if (tagObj === null)
-            ThrowRangeError(JSMSG_INVALID_LANGUAGE_TAG, tag);
-
-        // ApplyOptionsToTag, step 9.
-        CanonicalizeLanguageTagObject(tagObj);
-    } else {
-        tagObj = copyLanguageTagObject(unboxedLocale.locale);
-
-        // |tagObj| is already canonicalized for Intl.Locale objects, so we can
-        // skip ApplyOptionsToTag, step 9.
-    }
-
-    // Step 13.
-    var opt = new Record();
-
-    // Skip rest of step 12 and steps 14-29 if no options were passed to the
-    // Intl.Locale constructor.
-    if (hasOptions) {
-        // Step 12.
-        ApplyOptionsToTag(tagObj, options);
-
-        // Step 14.
-        var calendar = GetOption(options, "calendar", "string", undefined, undefined);
-
-        // Step 15.
-        if (calendar !== undefined) {
-            var standaloneCalendar = parseStandaloneUnicodeExtensionType(calendar);
-            if (standaloneCalendar === null)
-                ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "calendar", calendar);
-
-            calendar = standaloneCalendar;
-        }
-
-        // Step 16.
-        opt.ca = calendar;
-
-        // Step 17.
-        var collation = GetOption(options, "collation", "string", undefined, undefined);
-
-        // Step 18.
-        if (collation !== undefined) {
-            var standaloneCollation = parseStandaloneUnicodeExtensionType(collation);
-            if (standaloneCollation === null)
-                ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "collation", collation);
-
-            collation = standaloneCollation;
-        }
-
-        // Step 19.
-        opt.co = collation;
-
-        // Steps 20-21.
-        opt.hc = GetOption(options, "hourCycle", "string", ["h11", "h12", "h23", "h24"],
-                           undefined);
-
-        // Steps 22-23.
-        opt.kf = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"],
-                           undefined);
-
-        // Step 24.
-        var numeric = GetOption(options, "numeric", "boolean", undefined, undefined);
-
-        // Step 25.
-        if (numeric !== undefined)
-            numeric = ToString(numeric);
-
-        // Step 26.
-        opt.kn = numeric;
-
-        // Step 27.
-        var numberingSystem = GetOption(options, "numberingSystem", "string", undefined,
-                                        undefined);
-
-        // Step 28.
-        if (numberingSystem !== undefined) {
-            var standaloneNumberingSystem = parseStandaloneUnicodeExtensionType(numberingSystem);
-            if (standaloneNumberingSystem === null)
-                ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "numberingSystem", numberingSystem);
-
-            numberingSystem = standaloneNumberingSystem;
-        }
-
-        // Step 29.
-        opt.nu = numberingSystem;
-    } else {
-        // Steps 14-29.
-        opt.ca = undefined;
-        opt.co = undefined;
-        opt.hc = undefined;
-        opt.kf = undefined;
-        opt.kn = undefined;
-        opt.nu = undefined;
-    }
-
-    // Step 2.
-    var {relevantExtensionKeys} = localeInternalProperties;
-
-    // Step 30.
-    if (hasOptions || unboxedLocale === null) {
-        ApplyUnicodeExtensionToTag(tagObj, opt, relevantExtensionKeys);
-    } else {
-        // Directly copy the Unicode extension keys from the source object if
-        // no options were passed to the constructor.
-        opt.ca = unboxedLocale.calendar;
-        opt.co = unboxedLocale.collation;
-        opt.hc = unboxedLocale.hourCycle;
-        opt.kf = unboxedLocale.caseFirst;
-        opt.kn = unboxedLocale.numeric;
-        opt.nu = unboxedLocale.numberingSystem;
-    }
-
-    // Steps 31-37.
-    var internals = new Record();
-    internals.locale = tagObj;
-    internals.calendar = opt.ca;
-    internals.collation = opt.co;
-    internals.hourCycle = opt.hc;
-    internals.caseFirst = opt.kf;
-    internals.numeric = opt.kn === "true" || opt.kn === "";
-    internals.numberingSystem = opt.nu;
-
-    assert(UnsafeGetReservedSlot(locale, INTL_INTERNALS_OBJECT_SLOT) === null,
-           "Internal slot already initialized?");
-    UnsafeSetReservedSlot(locale, INTL_INTERNALS_OBJECT_SLOT, internals);
-}
-
-/**
- * Creates a new Intl.Locale object using the language tag object |tagObj|.
- * The other internal slots are copied over from |otherLocale|.
- */
-function CreateLocale(tagObj, otherLocale) {
-    assert(IsObject(tagObj), "CreateLocale called with non-object");
-    assert(GuardToLocale(otherLocale) !== null, "CreateLocale called with non-Locale");
-
-#ifdef DEBUG
-    var localeTag = StringFromLanguageTagObject(tagObj);
-    assert(localeTag === CanonicalizeLanguageTag(localeTag),
-           "CreateLocale called with non-canonical language tag");
-#endif
-
-    var locInternals = getLocaleInternals(otherLocale);
-
-    var internals = new Record();
-    internals.locale = tagObj;
-    internals.calendar = locInternals.calendar;
-    internals.collation = locInternals.collation;
-    internals.hourCycle = locInternals.hourCycle;
-    internals.caseFirst = locInternals.caseFirst;
-    internals.numeric = locInternals.numeric;
-    internals.numberingSystem = locInternals.numberingSystem;
-
-    var locale = intl_CreateUninitializedLocale();
-    assert(UnsafeGetReservedSlot(locale, INTL_INTERNALS_OBJECT_SLOT) === null,
-                                 "Internal slot already initialized?");
-    UnsafeSetReservedSlot(locale, INTL_INTERNALS_OBJECT_SLOT, internals);
-
-    return locale;
-}
-
-/**
- * Unboxes the |this| argument if it is an Intl.Locale object, otherwise
- * returns null.
- */
-function unboxLocaleOrNull() {
-    if (!IsObject(this))
-        return null;
-
-    var loc = GuardToLocale(this);
-    if (loc !== null)
-        return getLocaleInternals(loc);
-    if (IsWrappedLocale(this))
-        return callFunction(CallLocaleMethodIfWrapped, this, "unboxLocaleOrNull");
-    return null;
-}
-
-/**
- * Creates a copy of the given language tag object.
- */
-function copyLanguageTagObject(tagObj) {
-    assert(IsObject(tagObj), "copyLanguageTagObject called with non-object");
-
-    var variants = [];
-    for (var i = 0; i < tagObj.variants.length; i++)
-        _DefineDataProperty(variants, i, tagObj.variants[i]);
-
-    var extensions = [];
-    for (var i = 0; i < tagObj.extensions.length; i++)
-        _DefineDataProperty(extensions, i, tagObj.extensions[i]);
-
-    return {
-        language: tagObj.language,
-        script: tagObj.script,
-        region: tagObj.region,
-        variants,
-        extensions,
-        privateuse: tagObj.privateuse,
-    };
-}
-
-function getLocaleInternals(obj) {
-    assert(IsObject(obj), "getLocaleInternals called with non-object");
-    assert(GuardToLocale(obj) !== null, "getLocaleInternals called with non-Locale");
-
-    var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
-    assert(IsObject(internals), "Internal slot not initialized?");
-
-    return internals;
-}
-
-/**
- * Intl.Locale.prototype.maximize ()
- */
-function Intl_Locale_maximize() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "Intl_Locale_maximize");
-
-    // Step 3.
-    var tagObj = copyLanguageTagObject(getLocaleInternals(loc).locale);
-
-    var maximal = intl_AddLikelySubtags(tagObj.language, tagObj.script, tagObj.region);
-    tagObj.language = maximal[0];
-    tagObj.script = maximal[1];
-    tagObj.region = maximal[2];
-
-    // Update mappings in case ICU returned a non-canonicalized locale.
-    updateLocaleIdMappings(tagObj);
-
-    // Step 4.
-    return CreateLocale(tagObj, loc);
-}
-
-/**
- * Intl.Locale.prototype.minimize ()
- */
-function Intl_Locale_minimize() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "Intl_Locale_minimize");
-
-    // Step 3.
-    var tagObj = copyLanguageTagObject(getLocaleInternals(loc).locale);
-
-    var minimal = intl_RemoveLikelySubtags(tagObj.language, tagObj.script, tagObj.region);
-    tagObj.language = minimal[0];
-    tagObj.script = minimal[1];
-    tagObj.region = minimal[2];
-
-    // Update mappings in case ICU returned a non-canonicalized locale.
-    updateLocaleIdMappings(tagObj);
-
-    // Step 4.
-    return CreateLocale(tagObj, loc);
-}
-
-/**
- * Intl.Locale.prototype.toString ()
- */
-function Intl_Locale_toString() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "Intl_Locale_toString");
-
-    // Step 3.
-    var tagObj = getLocaleInternals(loc).locale;
-    return StringFromLanguageTagObject(tagObj);
-}
-
-/**
- * get Intl.Locale.prototype.baseName
- */
-function $Intl_Locale_baseName_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_baseName_get");
-
-    // Step 3.
-    var tagObj = getLocaleInternals(loc).locale;
-
-    // Step 4.
-    // FIXME: spec bug - unicode_locale_id is always matched.
-
-    // Step 5.
-    // FIXME: spec bug - subtag production names not updated.
-    var baseName = tagObj.language;
-
-    if (tagObj.script !== undefined)
-        baseName += "-" + tagObj.script;
-    if (tagObj.region !== undefined)
-        baseName += "-" + tagObj.region;
-    if (tagObj.variants.length > 0)
-        baseName += "-" + callFunction(std_Array_join, tagObj.variants, "-");
-
-    return baseName;
-}
-_SetCanonicalName($Intl_Locale_baseName_get, "get baseName");
-
-/**
- * get Intl.Locale.prototype.calendar
- */
-function $Intl_Locale_calendar_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_calendar_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).calendar;
-}
-_SetCanonicalName($Intl_Locale_calendar_get, "get calendar");
-
-/**
- * get Intl.Locale.prototype.collation
- */
-function $Intl_Locale_collation_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_collation_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).collation;
-}
-_SetCanonicalName($Intl_Locale_collation_get, "get collation");
-
-/**
- * get Intl.Locale.prototype.hourCycle
- */
-function $Intl_Locale_hourCycle_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_hourCycle_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).hourCycle;
-}
-_SetCanonicalName($Intl_Locale_hourCycle_get, "get hourCycle");
-
-/**
- * get Intl.Locale.prototype.caseFirst
- */
-function $Intl_Locale_caseFirst_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_caseFirst_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).caseFirst;
-}
-_SetCanonicalName($Intl_Locale_caseFirst_get, "get caseFirst");
-
-/**
- * get Intl.Locale.prototype.numeric
- */
-function $Intl_Locale_numeric_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_numeric_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).numeric;
-}
-_SetCanonicalName($Intl_Locale_numeric_get, "get numeric");
-
-/**
- * get Intl.Locale.prototype.numberingSystem
- */
-function $Intl_Locale_numberingSystem_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_numberingSystem_get");
-
-    // Step 3.
-    return getLocaleInternals(loc).numberingSystem;
-}
-_SetCanonicalName($Intl_Locale_numberingSystem_get, "get numberingSystem");
-
-/**
- * get Intl.Locale.prototype.language
- */
-function $Intl_Locale_language_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_language_get");
-
-    // Step 3.
-    var tagObj = getLocaleInternals(loc).locale;
-
-    // Step 4 (Unnecessary assertion).
-
-    // Step 5.
-    return tagObj.language;
-}
-_SetCanonicalName($Intl_Locale_language_get, "get language");
-
-/**
- * get Intl.Locale.prototype.script
- */
-function $Intl_Locale_script_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_script_get");
-
-    // Step 3.
-    var tagObj = getLocaleInternals(loc).locale;
-
-    // Step 4 (Unnecessary assertion).
-
-    // Steps 5-6.
-    // FIXME: spec bug - not all production names updated.
-    return tagObj.script;
-}
-_SetCanonicalName($Intl_Locale_script_get, "get script");
-
-/**
- * get Intl.Locale.prototype.region
- */
-function $Intl_Locale_region_get() {
-    // Step 1.
-    var loc = this;
-
-    // Step 2.
-    if (!IsObject(loc) || (loc = GuardToLocale(loc)) === null)
-        return callFunction(CallLocaleMethodIfWrapped, this, "$Intl_Locale_region_get");
-
-    // Step 3.
-    var tagObj = getLocaleInternals(loc).locale;
-
-    // Step 4 (Unnecessary assertion).
-
-    // Steps 5-6.
-    // FIXME: spec bug - not all production names updated.
-    return tagObj.region;
-}
-_SetCanonicalName($Intl_Locale_region_get, "get region");
--- a/js/src/builtin/intl/NativeLocale.cpp
+++ b/js/src/builtin/intl/NativeLocale.cpp
@@ -1248,14 +1248,14 @@ JSObject* js::CreateNativeLocalePrototyp
   }
 
   if (!DefinePropertiesAndFunctions(cx, proto, locale_native_properties,
                                     locale_native_methods)) {
     return nullptr;
   }
 
   RootedValue ctorValue(cx, ObjectValue(*ctor));
-  if (!DefineDataProperty(cx, Intl, cx->names().locale, ctorValue, 0)) {
+  if (!DefineDataProperty(cx, Intl, cx->names().Locale, ctorValue, 0)) {
     return nullptr;
   }
 
   return proto;
 }
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -26,17 +26,16 @@
   _(AtomicsOr)                                     \
   _(AtomicsXor)                                    \
   _(AtomicsIsLockFree)                             \
                                                    \
   _(Boolean)                                       \
                                                    \
   _(IntlGuardToCollator)                           \
   _(IntlGuardToDateTimeFormat)                     \
-  _(IntlGuardToLocale)                             \
   _(IntlGuardToNumberFormat)                       \
   _(IntlGuardToPluralRules)                        \
   _(IntlGuardToRelativeTimeFormat)                 \
                                                    \
   _(MathAbs)                                       \
   _(MathFloor)                                     \
   _(MathCeil)                                      \
   _(MathRound)                                     \
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -7,17 +7,16 @@
 #include "mozilla/Casting.h"
 
 #include "jsmath.h"
 
 #include "builtin/AtomicsObject.h"
 #ifdef ENABLE_INTL_API
 #  include "builtin/intl/Collator.h"
 #  include "builtin/intl/DateTimeFormat.h"
-#  include "builtin/intl/Locale.h"
 #  include "builtin/intl/NumberFormat.h"
 #  include "builtin/intl/PluralRules.h"
 #  include "builtin/intl/RelativeTimeFormat.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/String.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
@@ -106,17 +105,16 @@ static bool CanInlineCrossRealm(Inlinabl
       return true;
 
     case InlinableNative::MathRandom:
       // RNG state is per-realm.
       return false;
 
     case InlinableNative::IntlGuardToCollator:
     case InlinableNative::IntlGuardToDateTimeFormat:
-    case InlinableNative::IntlGuardToLocale:
     case InlinableNative::IntlGuardToNumberFormat:
     case InlinableNative::IntlGuardToPluralRules:
     case InlinableNative::IntlGuardToRelativeTimeFormat:
     case InlinableNative::IsRegExpObject:
     case InlinableNative::RegExpMatcher:
     case InlinableNative::RegExpSearcher:
     case InlinableNative::RegExpTester:
     case InlinableNative::RegExpPrototypeOptimizable:
@@ -314,28 +312,25 @@ IonBuilder::InliningResult IonBuilder::i
       return inlineBoolean(callInfo);
 
 #ifdef ENABLE_INTL_API
     // Intl natives.
     case InlinableNative::IntlGuardToCollator:
       return inlineGuardToClass(callInfo, &CollatorObject::class_);
     case InlinableNative::IntlGuardToDateTimeFormat:
       return inlineGuardToClass(callInfo, &DateTimeFormatObject::class_);
-    case InlinableNative::IntlGuardToLocale:
-      return inlineGuardToClass(callInfo, &LocaleObject::class_);
     case InlinableNative::IntlGuardToNumberFormat:
       return inlineGuardToClass(callInfo, &NumberFormatObject::class_);
     case InlinableNative::IntlGuardToPluralRules:
       return inlineGuardToClass(callInfo, &PluralRulesObject::class_);
     case InlinableNative::IntlGuardToRelativeTimeFormat:
       return inlineGuardToClass(callInfo, &RelativeTimeFormatObject::class_);
 #else
     case InlinableNative::IntlGuardToCollator:
     case InlinableNative::IntlGuardToDateTimeFormat:
-    case InlinableNative::IntlGuardToLocale:
     case InlinableNative::IntlGuardToNumberFormat:
     case InlinableNative::IntlGuardToPluralRules:
     case InlinableNative::IntlGuardToRelativeTimeFormat:
       MOZ_CRASH("Intl API disabled");
 #endif
 
     // Math natives.
     case InlinableNative::MathAbs:
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -478,17 +478,16 @@ selfhosted_inputs = [
     'builtin/WeakSet.js'
 ] + ([
     'builtin/intl/Collator.js',
     'builtin/intl/CommonFunctions.js',
     'builtin/intl/CurrencyDataGenerated.js',
     'builtin/intl/DateTimeFormat.js',
     'builtin/intl/IntlObject.js',
     'builtin/intl/LangTagMappingsGenerated.js',
-    'builtin/intl/Locale.js',
     'builtin/intl/NumberFormat.js',
     'builtin/intl/PluralRules.js',
     'builtin/intl/RelativeTimeFormat.js',
     'builtin/intl/UnicodeExtensionsGenerated.js',
 ] if CONFIG['ENABLE_INTL_API'] else [])
 
 # Prepare self-hosted JS code for embedding
 GeneratedFile('selfhosted.out.h', 'selfhosted.js',
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2075,16 +2075,45 @@ static bool intrinsic_ToNumeric(JSContex
   MOZ_ASSERT(args.length() == 1);
   if (!ToNumeric(cx, args[0])) {
     return false;
   }
   args.rval().set(args[0]);
   return true;
 }
 
+static bool intrinsic_LocaleToStringOrNull(JSContext* cx, unsigned argc,
+                                           Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  MOZ_ASSERT(args.length() == 1);
+
+  if (!args[0].isObject()) {
+    args.rval().setNull();
+    return true;
+  }
+
+  JSObject* unwrapped = CheckedUnwrapStatic(&args[0].toObject());
+  if (!unwrapped) {
+    ReportAccessDenied(cx);
+    return false;
+  }
+
+  if (!unwrapped->is<NativeLocaleObject>()) {
+    args.rval().setNull();
+    return true;
+  }
+
+  RootedString str(cx, unwrapped->as<NativeLocaleObject>().languageTag());
+  if (!cx->compartment()->wrap(cx, &str)) {
+    return false;
+  }
+  args.rval().setString(str);
+  return true;
+}
+
 // The self-hosting global isn't initialized with the normal set of builtins.
 // Instead, individual C++-implemented functions that're required by
 // self-hosted code are defined as global functions. Accessing these
 // functions via a content compartment's builtins would be unsafe, because
 // content script might have changed the builtins' prototypes' members.
 // Installing the whole set of builtins in the self-hosting compartment, OTOH,
 // would be wasteful: it increases memory usage and initialization time for
 // self-hosting compartment.
@@ -2450,51 +2479,41 @@ static const JSFunctionSpec intrinsic_fu
           intl_PluralRules_availableLocales, 0, 0),
     JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 1, 0),
     JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2, 0),
     JS_FN("intl_RelativeTimeFormat_availableLocales",
           intl_RelativeTimeFormat_availableLocales, 0, 0),
     JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 4, 0),
     JS_FN("intl_toLocaleLowerCase", intl_toLocaleLowerCase, 2, 0),
     JS_FN("intl_toLocaleUpperCase", intl_toLocaleUpperCase, 2, 0),
-    JS_FN("intl_CreateUninitializedLocale", intl_CreateUninitializedLocale, 0,
-          0),
-    JS_FN("intl_AddLikelySubtags", intl_AddLikelySubtags, 3, 0),
-    JS_FN("intl_RemoveLikelySubtags", intl_RemoveLikelySubtags, 3, 0),
 
     JS_INLINABLE_FN("GuardToCollator", intrinsic_GuardToBuiltin<CollatorObject>,
                     1, 0, IntlGuardToCollator),
     JS_INLINABLE_FN("GuardToDateTimeFormat",
                     intrinsic_GuardToBuiltin<DateTimeFormatObject>, 1, 0,
                     IntlGuardToDateTimeFormat),
-    JS_INLINABLE_FN("GuardToLocale", intrinsic_GuardToBuiltin<LocaleObject>, 1,
-                    0, IntlGuardToLocale),
     JS_INLINABLE_FN("GuardToNumberFormat",
                     intrinsic_GuardToBuiltin<NumberFormatObject>, 1, 0,
                     IntlGuardToNumberFormat),
     JS_INLINABLE_FN("GuardToPluralRules",
                     intrinsic_GuardToBuiltin<PluralRulesObject>, 1, 0,
                     IntlGuardToPluralRules),
     JS_INLINABLE_FN("GuardToRelativeTimeFormat",
                     intrinsic_GuardToBuiltin<RelativeTimeFormatObject>, 1, 0,
                     IntlGuardToRelativeTimeFormat),
 
     JS_FN("IsWrappedDateTimeFormat",
           intrinsic_IsWrappedInstanceOfBuiltin<DateTimeFormatObject>, 1, 0),
-    JS_FN("IsWrappedLocale", intrinsic_IsWrappedInstanceOfBuiltin<LocaleObject>,
-          1, 0),
     JS_FN("IsWrappedNumberFormat",
           intrinsic_IsWrappedInstanceOfBuiltin<NumberFormatObject>, 1, 0),
 
     JS_FN("CallCollatorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<CollatorObject>>, 2, 0),
     JS_FN("CallDateTimeFormatMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<DateTimeFormatObject>>, 2, 0),
-    JS_FN("CallLocaleMethodIfWrapped",
-          CallNonGenericSelfhostedMethod<Is<LocaleObject>>, 2, 0),
     JS_FN("CallNumberFormatMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<NumberFormatObject>>, 2, 0),
     JS_FN("CallPluralRulesMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<PluralRulesObject>>, 2, 0),
     JS_FN("CallRelativeTimeFormatMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<RelativeTimeFormatObject>>, 2, 0),
 
     JS_FN("GetDateTimeFormatConstructor",
@@ -2504,16 +2523,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("GetNumberFormatConstructor",
           intrinsic_GetBuiltinIntlConstructor<
               GlobalObject::getOrCreateNumberFormatConstructor>,
           0, 0),
     JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0, 0),
     JS_FN("IsRuntimeDefaultLocale", intrinsic_IsRuntimeDefaultLocale, 1, 0),
 #endif  // ENABLE_INTL_API
 
+    JS_FN("LocaleToStringOrNull", intrinsic_LocaleToStringOrNull, 1, 0),
+
     JS_FN("GetOwnPropertyDescriptorToArray", GetOwnPropertyDescriptorToArray, 2,
           0),
 
     JS_INLINABLE_FN("IsRegExpObject",
                     intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1, 0,
                     IsRegExpObject),
     JS_FN("CallRegExpMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2, 0),