summaryrefslogtreecommitdiff
path: root/js/src/builtin/intl/CommonFunctions.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/intl/CommonFunctions.js')
-rw-r--r--js/src/builtin/intl/CommonFunctions.js1231
1 files changed, 411 insertions, 820 deletions
diff --git a/js/src/builtin/intl/CommonFunctions.js b/js/src/builtin/intl/CommonFunctions.js
index 48337e666a..effabf020f 100644
--- a/js/src/builtin/intl/CommonFunctions.js
+++ b/js/src/builtin/intl/CommonFunctions.js
@@ -5,487 +5,139 @@
/* Portions Copyright Norbert Lindenberg 2011-2012. */
+
/**
- * Holder object for encapsulating regexp instances.
- *
- * Regular expression instances should be created after the initialization of
- * self-hosted global.
+ * Shorthand for hasOwnProperty.
*/
-var internalIntlRegExps = std_Object_create(null);
-internalIntlRegExps.unicodeLocaleExtensionSequenceRE = null;
-internalIntlRegExps.languageTagRE = null;
-internalIntlRegExps.duplicateVariantRE = null;
-internalIntlRegExps.duplicateSingletonRE = null;
-internalIntlRegExps.isWellFormedCurrencyCodeRE = null;
-internalIntlRegExps.currencyDigitsRE = null;
+function hasOwn(propName, object) {
+ return callFunction(std_Object_hasOwnProperty, object, propName);
+}
+
+#ifdef DEBUG
+#define assertIsValidAndCanonicalLanguageTag(locale, desc) \
+ do { \
+ let canonical = intl_TryValidateAndCanonicalizeLanguageTag(locale); \
+ assert(canonical !== null, \
+ `${desc} is a structurally valid language tag`); \
+ assert(canonical === locale, \
+ `${desc} is a canonicalized language tag`); \
+ } while (false)
+#else
+#define assertIsValidAndCanonicalLanguageTag(locale, desc) ; // Elided assertion.
+#endif
/**
- * Regular expression matching a "Unicode locale extension sequence", which the
+ * Returns the start index of a "Unicode locale extension sequence", which the
* specification defines as: "any substring of a language tag that starts with
* a separator '-' and the singleton 'u' and includes the maximum sequence of
* following non-singleton subtags and their preceding '-' separators."
*
* Alternatively, this may be defined as: the components of a language tag that
- * match the extension production in RFC 5646, where the singleton component is
- * "u".
+ * match the `unicode_locale_extensions` production in UTS 35.
*
* Spec: ECMAScript Internationalization API Specification, 6.2.1.
*/
-function getUnicodeLocaleExtensionSequenceRE() {
- return internalIntlRegExps.unicodeLocaleExtensionSequenceRE ||
- (internalIntlRegExps.unicodeLocaleExtensionSequenceRE =
- RegExpCreate("-u(?:-[a-z0-9]{2,8})+"));
-}
-
-
-/**
- * Removes Unicode locale extension sequences from the given language tag.
- */
-function removeUnicodeExtensions(locale) {
- // A wholly-privateuse locale has no extension sequences.
- if (callFunction(std_String_startsWith, locale, "x-"))
- return locale;
+function startOfUnicodeExtensions(locale) {
+ assert(typeof locale === "string", "locale is a string");
- // Otherwise, split on "-x-" marking the start of any privateuse component.
- // Replace Unicode locale extension sequences in the left half, and return
- // the concatenation.
- var pos = callFunction(std_String_indexOf, locale, "-x-");
- if (pos < 0)
- pos = locale.length;
-
- var left = callFunction(String_substring, locale, 0, pos);
- var right = callFunction(String_substring, locale, pos);
-
- var extensions;
- var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
- while ((extensions = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, left)) !== null) {
- left = StringReplaceString(left, extensions[0], "");
- unicodeLocaleExtensionSequenceRE.lastIndex = 0;
- }
+ // Search for "-u-" marking the start of a Unicode extension sequence.
+ var start = callFunction(std_String_indexOf, locale, "-u-");
+ if (start < 0)
+ return -1;
- var combined = left + right;
- assert(IsStructurallyValidLanguageTag(combined), "recombination produced an invalid language tag");
- assert(function() {
- var uindex = callFunction(std_String_indexOf, combined, "-u-");
- if (uindex < 0)
- return true;
- var xindex = callFunction(std_String_indexOf, combined, "-x-");
- return xindex > 0 && xindex < uindex;
- }(), "recombination failed to remove all Unicode locale extension sequences");
+ // And search for "-x-" marking the start of any privateuse component to
+ // handle the case when "-u-" was only found within a privateuse subtag.
+ var privateExt = callFunction(std_String_indexOf, locale, "-x-");
+ if (privateExt >= 0 && privateExt < start)
+ return -1;
- return combined;
+ return start;
}
-
/**
- * Regular expression defining BCP 47 language tags.
- *
- * Spec: RFC 5646 section 2.1.
+ * Returns the end index of a Unicode locale extension sequence.
*/
-function getLanguageTagRE() {
- if (internalIntlRegExps.languageTagRE)
- return internalIntlRegExps.languageTagRE;
-
- // RFC 5234 section B.1
- // ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- var ALPHA = "[a-zA-Z]";
- // DIGIT = %x30-39
- // ; 0-9
- var DIGIT = "[0-9]";
-
- // RFC 5646 section 2.1
- // alphanum = (ALPHA / DIGIT) ; letters and numbers
- var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
- // regular = "art-lojban" ; these tags match the 'langtag'
- // / "cel-gaulish" ; production, but their subtags
- // / "no-bok" ; are not extended language
- // / "no-nyn" ; or variant subtags: their meaning
- // / "zh-guoyu" ; is defined by their registration
- // / "zh-hakka" ; and all of these are deprecated
- // / "zh-min" ; in favor of a more modern
- // / "zh-min-nan" ; subtag or sequence of subtags
- // / "zh-xiang"
- var regular = "(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)";
- // irregular = "en-GB-oed" ; irregular tags do not match
- // / "i-ami" ; the 'langtag' production and
- // / "i-bnn" ; would not otherwise be
- // / "i-default" ; considered 'well-formed'
- // / "i-enochian" ; These tags are all valid,
- // / "i-hak" ; but most are deprecated
- // / "i-klingon" ; in favor of more modern
- // / "i-lux" ; subtags or subtag
- // / "i-mingo" ; combination
- // / "i-navajo"
- // / "i-pwn"
- // / "i-tao"
- // / "i-tay"
- // / "i-tsu"
- // / "sgn-BE-FR"
- // / "sgn-BE-NL"
- // / "sgn-CH-DE"
- var irregular = "(?:en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)";
- // grandfathered = irregular ; non-redundant tags registered
- // / regular ; during the RFC 3066 era
- var grandfathered = "(?:" + irregular + "|" + regular + ")";
- // privateuse = "x" 1*("-" (1*8alphanum))
- var privateuse = "(?:x(?:-[a-z0-9]{1,8})+)";
- // singleton = DIGIT ; 0 - 9
- // / %x41-57 ; A - W
- // / %x59-5A ; Y - Z
- // / %x61-77 ; a - w
- // / %x79-7A ; y - z
- var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
- // extension = singleton 1*("-" (2*8alphanum))
- var extension = "(?:" + singleton + "(?:-" + alphanum + "{2,8})+)";
- // variant = 5*8alphanum ; registered variants
- // / (DIGIT 3alphanum)
- var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
- // region = 2ALPHA ; ISO 3166-1 code
- // / 3DIGIT ; UN M.49 code
- var region = "(?:" + ALPHA + "{2}|" + DIGIT + "{3})";
- // script = 4ALPHA ; ISO 15924 code
- var script = "(?:" + ALPHA + "{4})";
- // extlang = 3ALPHA ; selected ISO 639 codes
- // *2("-" 3ALPHA) ; permanently reserved
- var extlang = "(?:" + ALPHA + "{3}(?:-" + ALPHA + "{3}){0,2})";
- // language = 2*3ALPHA ; shortest ISO 639 code
- // ["-" extlang] ; sometimes followed by
- // ; extended language subtags
- // / 4ALPHA ; or reserved for future use
- // / 5*8ALPHA ; or registered language subtag
- var language = "(?:" + ALPHA + "{2,3}(?:-" + extlang + ")?|" + ALPHA + "{4}|" + ALPHA + "{5,8})";
- // langtag = language
- // ["-" script]
- // ["-" region]
- // *("-" variant)
- // *("-" extension)
- // ["-" privateuse]
- var langtag = language + "(?:-" + script + ")?(?:-" + region + ")?(?:-" +
- variant + ")*(?:-" + extension + ")*(?:-" + privateuse + ")?";
- // Language-Tag = langtag ; normal language tags
- // / privateuse ; private use tag
- // / grandfathered ; grandfathered tags
- var languageTag = "^(?:" + langtag + "|" + privateuse + "|" + grandfathered + ")$";
-
- // Language tags are case insensitive (RFC 5646 section 2.1.1).
- return (internalIntlRegExps.languageTagRE = RegExpCreate(languageTag, "i"));
-}
+function endOfUnicodeExtensions(locale, start) {
+ assert(typeof locale === "string", "locale is a string");
+ assert(0 <= start && start < locale.length, "start is an index into locale");
+ assert(Substring(locale, start, 3) === "-u-", "start points to Unicode extension sequence");
+ #define HYPHEN 0x2D
+ assert(std_String_fromCharCode(HYPHEN) === "-",
+ "code unit constant should match the expected character");
-function getDuplicateVariantRE() {
- if (internalIntlRegExps.duplicateVariantRE)
- return internalIntlRegExps.duplicateVariantRE;
-
- // RFC 5234 section B.1
- // ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- var ALPHA = "[a-zA-Z]";
- // DIGIT = %x30-39
- // ; 0-9
- var DIGIT = "[0-9]";
-
- // RFC 5646 section 2.1
- // alphanum = (ALPHA / DIGIT) ; letters and numbers
- var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
- // variant = 5*8alphanum ; registered variants
- // / (DIGIT 3alphanum)
- var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
-
- // Match a langtag that contains a duplicate variant.
- var duplicateVariant =
- // Match everything in a langtag prior to any variants, and maybe some
- // of the variants as well (which makes this pattern inefficient but
- // not wrong, for our purposes);
- "(?:" + alphanum + "{2,8}-)+" +
- // a variant, parenthesised so that we can refer back to it later;
- "(" + variant + ")-" +
- // zero or more subtags at least two characters long (thus stopping
- // before extension and privateuse components);
- "(?:" + alphanum + "{2,8}-)*" +
- // and the same variant again
- "\\1" +
- // ...but not followed by any characters that would turn it into a
- // different subtag.
- "(?!" + alphanum + ")";
-
- // Language tags are case insensitive (RFC 5646 section 2.1.1). Using
- // character classes covering both upper- and lower-case characters nearly
- // addresses this -- but for the possibility of variant repetition with
- // differing case, e.g. "en-variant-Variant". Use a case-insensitive
- // regular expression to address this. (Note that there's no worry about
- // case transformation accepting invalid characters here: users have
- // already verified the string is alphanumeric Latin plus "-".)
- return (internalIntlRegExps.duplicateVariantRE = RegExpCreate(duplicateVariant, "i"));
-}
+ // Search for the start of the next singleton or privateuse subtag.
+ //
+ // Begin searching after the smallest possible Unicode locale extension
+ // sequence, namely |"-u-" 2alphanum|. End searching once the remaining
+ // characters can't fit the smallest possible singleton or privateuse
+ // subtag, namely |"-x-" alphanum|. Note the reduced end-limit means
+ // indexing inside the loop is always in-range.
+ for (var i = start + 5, end = locale.length - 4; i <= end; i++) {
+ if (callFunction(std_String_charCodeAt, locale, i) !== HYPHEN)
+ continue;
+ if (callFunction(std_String_charCodeAt, locale, i + 2) === HYPHEN)
+ return i;
+
+ // Skip over (i + 1) and (i + 2) because we've just verified they
+ // aren't "-", so the next possible delimiter can only be at (i + 3).
+ i += 2;
+ }
+ #undef HYPHEN
-function getDuplicateSingletonRE() {
- if (internalIntlRegExps.duplicateSingletonRE)
- return internalIntlRegExps.duplicateSingletonRE;
-
- // RFC 5234 section B.1
- // ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
- var ALPHA = "[a-zA-Z]";
- // DIGIT = %x30-39
- // ; 0-9
- var DIGIT = "[0-9]";
-
- // RFC 5646 section 2.1
- // alphanum = (ALPHA / DIGIT) ; letters and numbers
- var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
- // singleton = DIGIT ; 0 - 9
- // / %x41-57 ; A - W
- // / %x59-5A ; Y - Z
- // / %x61-77 ; a - w
- // / %x79-7A ; y - z
- var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
-
- // Match a langtag that contains a duplicate singleton.
- var duplicateSingleton =
- // Match a singleton subtag, parenthesised so that we can refer back to
- // it later;
- "-(" + singleton + ")-" +
- // then zero or more subtags;
- "(?:" + alphanum + "+-)*" +
- // and the same singleton again
- "\\1" +
- // ...but not followed by any characters that would turn it into a
- // different subtag.
- "(?!" + alphanum + ")";
-
- // Language tags are case insensitive (RFC 5646 section 2.1.1). Using
- // character classes covering both upper- and lower-case characters nearly
- // addresses this -- but for the possibility of singleton repetition with
- // differing case, e.g. "en-u-foo-U-foo". Use a case-insensitive regular
- // expression to address this. (Note that there's no worry about case
- // transformation accepting invalid characters here: users have already
- // verified the string is alphanumeric Latin plus "-".)
- return (internalIntlRegExps.duplicateSingletonRE = RegExpCreate(duplicateSingleton, "i"));
+ // If no singleton or privateuse subtag was found, the Unicode extension
+ // sequence extends until the end of the string.
+ return locale.length;
}
/**
- * Verifies that the given string is a well-formed BCP 47 language tag
- * with no duplicate variant or singleton subtags.
- *
- * Spec: ECMAScript Internationalization API Specification, 6.2.2.
+ * Removes Unicode locale extension sequences from the given language tag.
*/
-function IsStructurallyValidLanguageTag(locale) {
- assert(typeof locale === "string", "IsStructurallyValidLanguageTag");
- var languageTagRE = getLanguageTagRE();
- if (!regexp_test_no_statics(languageTagRE, locale))
- return false;
-
- // Before checking for duplicate variant or singleton subtags with
- // regular expressions, we have to get private use subtag sequences
- // out of the picture.
- if (callFunction(std_String_startsWith, locale, "x-"))
- return true;
- var pos = callFunction(std_String_indexOf, locale, "-x-");
- if (pos !== -1)
- locale = callFunction(String_substring, locale, 0, pos);
-
- // Check for duplicate variant or singleton subtags.
- var duplicateVariantRE = getDuplicateVariantRE();
- var duplicateSingletonRE = getDuplicateSingletonRE();
- return !regexp_test_no_statics(duplicateVariantRE, locale) &&
- !regexp_test_no_statics(duplicateSingletonRE, locale);
-}
+function removeUnicodeExtensions(locale) {
+ var start = startOfUnicodeExtensions(locale);
+ if (start < 0)
+ return locale;
-/**
- * Joins the array elements in the given range with the supplied separator.
- */
-function ArrayJoinRange(array, separator, from, to = array.length) {
- assert(typeof separator === "string", "|separator| is a string value");
- assert(typeof from === "number", "|from| is a number value");
- assert(typeof to === "number", "|to| is a number value");
- assert(0 <= from && from <= to && to <= array.length, "|from| and |to| form a valid range");
+ var end = endOfUnicodeExtensions(locale, start);
- if (from === to)
- return "";
+ var left = Substring(locale, 0, start);
+ var right = Substring(locale, end, locale.length - end);
+ var combined = left + right;
- var result = array[from];
- for (var i = from + 1; i < to; i++) {
- result += separator + array[i];
- }
- return result;
+ assertIsValidAndCanonicalLanguageTag(combined, "the recombined locale");
+ assert(startOfUnicodeExtensions(combined) < 0,
+ "recombination failed to remove all Unicode locale extension sequences");
+
+ return combined;
}
/**
- * Canonicalizes the given structurally valid BCP 47 language tag, including
- * regularized case of subtags. For example, the language tag
- * Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, where
- *
- * Zh ; 2*3ALPHA
- * -NAN ; ["-" extlang]
- * -haNS ; ["-" script]
- * -bu ; ["-" region]
- * -variant2 ; *("-" variant)
- * -Variant1
- * -u-ca-chinese ; *("-" extension)
- * -t-Zh-laTN
- * -x-PRIVATE ; ["-" privateuse]
- *
- * becomes nan-Hans-mm-variant2-variant1-t-zh-latn-u-ca-chinese-x-private
- *
- * Spec: ECMAScript Internationalization API Specification, 6.2.3.
- * Spec: RFC 5646, section 4.5.
+ * Returns Unicode locale extension sequences from the given language tag.
*/
-function CanonicalizeLanguageTag(locale) {
- assert(IsStructurallyValidLanguageTag(locale), "CanonicalizeLanguageTag");
-
- // The input
- // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE"
- // will be used throughout this method to illustrate how it works.
-
- // Language tags are compared and processed case-insensitively, so
- // technically it's not necessary to adjust case. But for easier processing,
- // and because the canonical form for most subtags is lower case, we start
- // with lower case for all.
- // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
- // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
- locale = callFunction(std_String_toLowerCase, locale);
-
- // Handle mappings for complete tags.
- if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
- return langTagMappings[locale];
-
- var subtags = StringSplitString(ToString(locale), "-");
- var i = 0;
-
- // Handle the standard part: All subtags before the first singleton or "x".
- // "zh-nan-hans-bu-variant2-variant1"
- while (i < subtags.length) {
- var subtag = subtags[i];
-
- // If we reach the start of an extension sequence or private use part,
- // we're done with this loop. We have to check for i > 0 because for
- // irregular language tags, such as i-klingon, the single-character
- // subtag "i" is not the start of an extension sequence.
- // In the example, we break at "u".
- if (subtag.length === 1 && (i > 0 || subtag === "x"))
- break;
-
- if (i !== 0) {
- if (subtag.length === 4) {
- // 4-character subtags that are not in initial position are
- // script codes; their first character needs to be capitalized.
- // "hans" -> "Hans"
- subtag = callFunction(std_String_toUpperCase, subtag[0]) +
- callFunction(String_substring, subtag, 1);
- } else if (subtag.length === 2) {
- // 2-character subtags that are not in initial position are
- // region codes; they need to be upper case. "bu" -> "BU"
- subtag = callFunction(std_String_toUpperCase, subtag);
- }
- }
- if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
- // Replace deprecated subtags with their preferred values.
- // "BU" -> "MM"
- // This has to come after we capitalize region codes because
- // otherwise some language and region codes could be confused.
- // For example, "in" is an obsolete language code for Indonesian,
- // but "IN" is the country code for India.
- // Note that the script generating langSubtagMappings makes sure
- // that no regular subtag mapping will replace an extlang code.
- subtag = langSubtagMappings[subtag];
- } else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
- // Replace deprecated extlang subtags with their preferred values,
- // and remove the preceding subtag if it's a redundant prefix.
- // "zh-nan" -> "nan"
- // Note that the script generating extlangMappings makes sure that
- // no extlang mapping will replace a normal language code.
- subtag = extlangMappings[subtag].preferred;
- if (i === 1 && extlangMappings[subtag].prefix === subtags[0]) {
- callFunction(std_Array_shift, subtags);
- i--;
- }
- }
- subtags[i] = subtag;
- i++;
- }
- var normal = ArrayJoinRange(subtags, "-", 0, i);
-
- // Extension sequences are sorted by their singleton characters.
- // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese"
- var extensions = new List();
- while (i < subtags.length && subtags[i] !== "x") {
- var extensionStart = i;
- i++;
- while (i < subtags.length && subtags[i].length > 1)
- i++;
- var extension = ArrayJoinRange(subtags, "-", extensionStart, i);
- callFunction(std_Array_push, extensions, extension);
- }
- callFunction(std_Array_sort, extensions);
-
- // Private use sequences are left as is. "x-private"
- var privateUse = "";
- if (i < subtags.length)
- privateUse = ArrayJoinRange(subtags, "-", i);
-
- // Put everything back together.
- var canonical = normal;
- if (extensions.length > 0)
- canonical += "-" + callFunction(std_Array_join, extensions, "-");
- if (privateUse.length > 0) {
- // Be careful of a Language-Tag that is entirely privateuse.
- if (canonical.length > 0)
- canonical += "-" + privateUse;
- else
- canonical = privateUse;
- }
-
- return canonical;
-}
-
-function localeContainsNoUnicodeExtensions(locale) {
- // No "-u-", no possible Unicode extension.
- if (callFunction(std_String_indexOf, locale, "-u-") === -1)
- return true;
+function getUnicodeExtensions(locale) {
+ var start = startOfUnicodeExtensions(locale);
+ assert(start >= 0, "start of Unicode extension sequence not found");
+ var end = endOfUnicodeExtensions(locale, start);
- // "-u-" within privateuse also isn't one.
- if (callFunction(std_String_indexOf, locale, "-u-") > callFunction(std_String_indexOf, locale, "-x-"))
- return true;
-
- // An entirely-privateuse tag doesn't contain extensions.
- if (callFunction(std_String_startsWith, locale, "x-"))
- return true;
-
- // Otherwise, we have a Unicode extension sequence.
- return false;
+ return Substring(locale, start, end - start);
}
+/**
+ * Returns true if the input contains only ASCII alphabetical characters.
+ */
+function IsASCIIAlphaString(s) {
+ assert(typeof s === "string", "IsASCIIAlphaString");
-// The last-ditch locale is used if none of the available locales satisfies a
-// request. "en-GB" is used based on the assumptions that English is the most
-// common second language, that both en-GB and en-US are normally available in
-// an implementation, and that en-GB is more representative of the English used
-// in other locales.
-function lastDitchLocale() {
- // Per bug 1177929, strings don't clone out of self-hosted code as atoms,
- // breaking IonBuilder::constant. Put this in a function for now.
- return "en-GB";
+ for (var i = 0; i < s.length; i++) {
+ var c = callFunction(std_String_charCodeAt, s, i);
+ if (!((0x41 <= c && c <= 0x5A) || (0x61 <= c && c <= 0x7A)))
+ return false
+ }
+ return true;
}
-
-// Certain old, commonly-used language tags that lack a script, are expected to
-// nonetheless imply one. This object maps these old-style tags to modern
-// equivalents.
-var oldStyleLanguageTagMappings = {
- "pa-PK": "pa-Arab-PK",
- "zh-CN": "zh-Hans-CN",
- "zh-HK": "zh-Hant-HK",
- "zh-SG": "zh-Hans-SG",
- "zh-TW": "zh-Hant-TW",
-};
-
-
-var localeCandidateCache = {
- runtimeDefaultLocale: undefined,
- candidateDefaultLocale: undefined,
-};
-
-
var localeCache = {
runtimeDefaultLocale: undefined,
defaultLocale: undefined,
@@ -493,47 +145,6 @@ var localeCache = {
/**
- * Compute the candidate default locale: the locale *requested* to be used as
- * the default locale. We'll use it if and only if ICU provides support (maybe
- * fallback support, e.g. supporting "de-ZA" through "de" support implied by a
- * "de-DE" locale).
- */
-function DefaultLocaleIgnoringAvailableLocales() {
- const runtimeDefaultLocale = RuntimeDefaultLocale();
- if (runtimeDefaultLocale === localeCandidateCache.runtimeDefaultLocale)
- return localeCandidateCache.candidateDefaultLocale;
-
- // If we didn't get a cache hit, compute the candidate default locale and
- // cache it. Fall back on the last-ditch locale when necessary.
- var candidate;
- if (!IsStructurallyValidLanguageTag(runtimeDefaultLocale)) {
- candidate = lastDitchLocale();
- } else {
- candidate = CanonicalizeLanguageTag(runtimeDefaultLocale);
-
- // The default locale must be in [[availableLocales]], and that list
- // must not contain any locales with Unicode extension sequences, so
- // remove any present in the candidate.
- candidate = removeUnicodeExtensions(candidate);
-
- if (callFunction(std_Object_hasOwnProperty, oldStyleLanguageTagMappings, candidate))
- candidate = oldStyleLanguageTagMappings[candidate];
- }
-
- // Cache the candidate locale until the runtime default locale changes.
- localeCandidateCache.candidateDefaultLocale = candidate;
- localeCandidateCache.runtimeDefaultLocale = runtimeDefaultLocale;
-
- assert(IsStructurallyValidLanguageTag(candidate),
- "the candidate must be structurally valid");
- assert(localeContainsNoUnicodeExtensions(candidate),
- "the candidate must not contain a Unicode extension sequence");
-
- return candidate;
-}
-
-
-/**
* Returns the BCP 47 language tag for the host environment's current locale.
*
* Spec: ECMAScript Internationalization API Specification, 6.2.4.
@@ -544,32 +155,13 @@ function DefaultLocale() {
return localeCache.defaultLocale;
// If we didn't have a cache hit, compute the candidate default locale.
- // Then use it as the actual default locale if ICU supports that locale
- // (perhaps via fallback). Otherwise use the last-ditch locale.
- var candidate = DefaultLocaleIgnoringAvailableLocales();
- var locale;
- if (BestAvailableLocaleIgnoringDefault(callFunction(collatorInternalProperties.availableLocales,
- collatorInternalProperties),
- candidate) &&
- BestAvailableLocaleIgnoringDefault(callFunction(numberFormatInternalProperties.availableLocales,
- numberFormatInternalProperties),
- candidate) &&
- BestAvailableLocaleIgnoringDefault(callFunction(dateTimeFormatInternalProperties.availableLocales,
- dateTimeFormatInternalProperties),
- candidate))
- {
- locale = candidate;
- } else {
- locale = lastDitchLocale();
- }
+ var locale = intl_supportedLocaleOrFallback(runtimeDefaultLocale);
- assert(IsStructurallyValidLanguageTag(locale),
- "the computed default locale must be structurally valid");
- assert(locale === CanonicalizeLanguageTag(locale),
- "the computed default locale must be canonical");
- assert(localeContainsNoUnicodeExtensions(locale),
+ assertIsValidAndCanonicalLanguageTag(locale, "the computed default locale");
+ assert(startOfUnicodeExtensions(locale) < 0,
"the computed default locale must not contain a Unicode extension sequence");
+ // Cache the computed locale until the runtime default locale changes.
localeCache.defaultLocale = locale;
localeCache.runtimeDefaultLocale = runtimeDefaultLocale;
@@ -578,105 +170,62 @@ function DefaultLocale() {
/**
- * Add old-style language tags without script code for locales that in current
- * usage would include a script subtag. Also add an entry for the last-ditch
- * locale, in case ICU doesn't directly support it (but does support it through
- * fallback, e.g. supporting "en-GB" indirectly using "en" support).
- */
-function addSpecialMissingLanguageTags(availableLocales) {
- // Certain old-style language tags lack a script code, but in current usage
- // they *would* include a script code. Map these over to modern forms.
- var oldStyleLocales = std_Object_getOwnPropertyNames(oldStyleLanguageTagMappings);
- for (var i = 0; i < oldStyleLocales.length; i++) {
- var oldStyleLocale = oldStyleLocales[i];
- if (availableLocales[oldStyleLanguageTagMappings[oldStyleLocale]])
- availableLocales[oldStyleLocale] = true;
- }
-
- // Also forcibly provide the last-ditch locale.
- var lastDitch = lastDitchLocale();
- assert(lastDitch === "en-GB" && availableLocales["en"],
- "shouldn't be a need to add every locale implied by the last-" +
- "ditch locale, merely just the last-ditch locale");
- availableLocales[lastDitch] = true;
-}
-
-
-/**
* Canonicalizes a locale list.
*
* Spec: ECMAScript Internationalization API Specification, 9.2.1.
*/
function CanonicalizeLocaleList(locales) {
+ // Step 1.
if (locales === undefined)
- return new List();
- var seen = new List();
- if (typeof locales === "string")
- locales = [locales];
+ return [];
+
+ // Step 3 (and the remaining steps).
+ var tag = intl_ValidateAndCanonicalizeLanguageTag(locales, false);
+ if (tag !== null) {
+ assert(typeof tag === "string",
+ "intl_ValidateAndCanonicalizeLanguageTag returns a string value");
+ return [tag];
+ }
+
+ // Step 2.
+ var seen = [];
+
+ // Step 4.
var O = ToObject(locales);
+
+ // Step 5.
var len = ToLength(O.length);
+
+ // Step 6.
var k = 0;
+
+ // Step 7.
while (k < len) {
- // Don't call ToString(k) - SpiderMonkey is faster with integers.
- var kPresent = HasProperty(O, k);
- if (kPresent) {
+ // Steps 7.a-c.
+ if (k in O) {
+ // Step 7.c.i.
var kValue = O[k];
+
+ // Step 7.c.ii.
if (!(typeof kValue === "string" || IsObject(kValue)))
ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
- var tag = ToString(kValue);
- if (!IsStructurallyValidLanguageTag(tag))
- ThrowRangeError(JSMSG_INVALID_LANGUAGE_TAG, tag);
- tag = CanonicalizeLanguageTag(tag);
- if (callFunction(ArrayIndexOf, seen, tag) === -1)
- callFunction(std_Array_push, seen, tag);
- }
- k++;
- }
- return seen;
-}
+ // Steps 7.c.iii-iv.
+ var tag = intl_ValidateAndCanonicalizeLanguageTag(kValue, true);
+ assert(typeof tag === "string",
+ "ValidateAndCanonicalizeLanguageTag returns a string value");
-function BestAvailableLocaleHelper(availableLocales, locale, considerDefaultLocale) {
- assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure");
- assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale");
- assert(localeContainsNoUnicodeExtensions(locale), "locale must contain no Unicode extensions");
-
- // In the spec, [[availableLocales]] is formally a list of all available
- // locales. But in our implementation, it's an *incomplete* list, not
- // necessarily including the default locale (and all locales implied by it,
- // e.g. "de" implied by "de-CH"), if that locale isn't in every
- // [[availableLocales]] list (because that locale is supported through
- // fallback, e.g. "de-CH" supported through "de").
- //
- // If we're considering the default locale, augment the spec loop with
- // additional checks to also test whether the current prefix is a prefix of
- // the default locale.
-
- var defaultLocale;
- if (considerDefaultLocale)
- defaultLocale = DefaultLocale();
-
- var candidate = locale;
- while (true) {
- if (availableLocales[candidate])
- return candidate;
-
- if (considerDefaultLocale && candidate.length <= defaultLocale.length) {
- if (candidate === defaultLocale)
- return candidate;
- if (callFunction(std_String_startsWith, defaultLocale, candidate + "-"))
- return candidate;
+ // Step 7.c.v.
+ if (callFunction(ArrayIndexOf, seen, tag) === -1)
+ _DefineDataProperty(seen, seen.length, tag);
}
- var pos = callFunction(std_String_lastIndexOf, candidate, "-");
- if (pos === -1)
- return undefined;
-
- if (pos >= 2 && candidate[pos - 2] === "-")
- pos -= 2;
-
- candidate = callFunction(String_substring, candidate, 0, pos);
+ // Step 7.d.
+ k++;
}
+
+ // Step 8.
+ return seen;
}
@@ -689,20 +238,17 @@ function BestAvailableLocaleHelper(availableLocales, locale, considerDefaultLoca
* Spec: RFC 4647, section 3.4.
*/
function BestAvailableLocale(availableLocales, locale) {
- return BestAvailableLocaleHelper(availableLocales, locale, true);
+ return intl_BestAvailableLocale(availableLocales, locale, DefaultLocale());
}
-
/**
* Identical to BestAvailableLocale, but does not consider the default locale
* during computation.
*/
function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
- return BestAvailableLocaleHelper(availableLocales, locale, false);
+ return intl_BestAvailableLocale(availableLocales, locale, null);
}
-var noRelevantExtensionKeys = [];
-
/**
* Compares a BCP 47 language priority list against the set of locales in
* availableLocales and determines the best available language to meet the
@@ -716,31 +262,37 @@ var noRelevantExtensionKeys = [];
* Spec: RFC 4647, section 3.4.
*/
function LookupMatcher(availableLocales, requestedLocales) {
- var i = 0;
- var len = requestedLocales.length;
- var availableLocale;
- var locale, noExtensionsLocale;
- while (i < len && availableLocale === undefined) {
- locale = requestedLocales[i];
- noExtensionsLocale = removeUnicodeExtensions(locale);
- availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
- i++;
- }
-
+ // Step 1.
var result = new Record();
- if (availableLocale !== undefined) {
- result.locale = availableLocale;
- if (locale !== noExtensionsLocale) {
- var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
- var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale);
- var extension = extensionMatch[0];
- var extensionIndex = extensionMatch.index;
- result.extension = extension;
- result.extensionIndex = extensionIndex;
+
+ // Step 2.
+ for (var i = 0; i < requestedLocales.length; i++) {
+ var locale = requestedLocales[i];
+
+ // Step 2.a.
+ var noExtensionsLocale = removeUnicodeExtensions(locale);
+
+ // Step 2.b.
+ var availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
+
+ // Step 2.c.
+ if (availableLocale !== undefined) {
+ // Step 2.c.i.
+ result.locale = availableLocale;
+
+ // Step 2.c.ii.
+ if (locale !== noExtensionsLocale)
+ result.extension = getUnicodeExtensions(locale);
+
+ // Step 2.c.iii.
+ return result;
}
- } else {
- result.locale = DefaultLocale();
}
+
+ // Steps 3-4.
+ result.locale = DefaultLocale();
+
+ // Step 5.
return result;
}
@@ -759,6 +311,77 @@ function BestFitMatcher(availableLocales, requestedLocales) {
return LookupMatcher(availableLocales, requestedLocales);
}
+/**
+ * Returns the Unicode extension value subtags for the requested key subtag.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.5.
+ */
+function UnicodeExtensionValue(extension, key) {
+ assert(typeof extension === "string", "extension is a string value");
+ assert(callFunction(std_String_startsWith, extension, "-u-") &&
+ getUnicodeExtensions("und" + extension) === extension,
+ "extension is a Unicode extension subtag");
+ assert(typeof key === "string", "key is a string value");
+
+ // Step 1.
+ assert(key.length === 2, "key is a Unicode extension key subtag");
+
+ // Step 2.
+ var size = extension.length;
+
+ // Step 3.
+ var searchValue = "-" + key + "-";
+
+ // Step 4.
+ var pos = callFunction(std_String_indexOf, extension, searchValue);
+
+ // Step 5.
+ if (pos !== -1) {
+ // Step 5.a.
+ var start = pos + 4;
+
+ // Step 5.b.
+ var end = start;
+
+ // Step 5.c.
+ var k = start;
+
+ // Steps 5.d-e.
+ while (true) {
+ // Step 5.e.i.
+ var e = callFunction(std_String_indexOf, extension, "-", k);
+
+ // Step 5.e.ii.
+ var len = e === -1 ? size - k : e - k;
+
+ // Step 5.e.iii.
+ if (len === 2)
+ break;
+
+ // Step 5.e.iv.
+ if (e === -1) {
+ end = size;
+ break;
+ }
+
+ // Step 5.e.v.
+ end = e;
+ k = e + 1;
+ }
+
+ // Step 5.f.
+ return callFunction(String_substring, extension, start, end);
+ }
+
+ // Step 6.
+ searchValue = "-" + key;
+
+ // Steps 7-8.
+ if (callFunction(std_String_endsWith, extension, searchValue))
+ return "";
+
+ // Step 9 (implicit).
+}
/**
* Compares a BCP 47 language priority list against availableLocales and
@@ -767,11 +390,9 @@ function BestFitMatcher(availableLocales, requestedLocales) {
* caller's relevant extensions and locale data as well as client-provided
* options into consideration.
*
- * Spec: ECMAScript Internationalization API Specification, 9.2.5.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.6.
*/
function ResolveLocale(availableLocales, requestedLocales, options, relevantExtensionKeys, localeData) {
- /*jshint laxbreak: true */
-
// Steps 1-3.
var matcher = options.localeMatcher;
var r = (matcher === "lookup")
@@ -780,121 +401,130 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
// Step 4.
var foundLocale = r.locale;
-
- // Step 5.a.
var extension = r.extension;
- var extensionIndex, extensionSubtags, extensionSubtagsLength;
// Step 5.
- if (extension !== undefined) {
- // Step 5.b.
- extensionIndex = r.extensionIndex;
-
- // Steps 5.d-e.
- extensionSubtags = StringSplitString(ToString(extension), "-");
- extensionSubtagsLength = extensionSubtags.length;
- }
-
- // Steps 6-7.
var result = new Record();
+
+ // Step 6.
result.dataLocale = foundLocale;
- // Step 8.
+ // Step 7.
var supportedExtension = "-u";
- // Steps 9-11.
- var i = 0;
- var len = relevantExtensionKeys.length;
- while (i < len) {
- // Steps 11.a-c.
- var key = relevantExtensionKeys[i];
+ // In this implementation, localeData is a function, not an object.
+ var localeDataProvider = localeData();
- // In this implementation, localeData is a function, not an object.
- var foundLocaleData = localeData(foundLocale);
- var keyLocaleData = foundLocaleData[key];
+ // Step 8.
+ for (var i = 0; i < relevantExtensionKeys.length; i++) {
+ var key = relevantExtensionKeys[i];
- // Locale data provides default value.
- // Step 11.d.
- var value = keyLocaleData[0];
+ // Steps 8.a-h (The locale data is only computed when needed).
+ var keyLocaleData = undefined;
+ var value = undefined;
// Locale tag may override.
- // Step 11.e.
+ // Step 8.g.
var supportedExtensionAddition = "";
- // Step 11.f is implemented by Utilities.js.
-
- var valuePos;
-
- // Step 11.g.
- if (extensionSubtags !== undefined) {
- // Step 11.g.i.
- var keyPos = callFunction(ArrayIndexOf, extensionSubtags, key);
-
- // Step 11.g.ii.
- if (keyPos !== -1) {
- // Step 11.g.ii.1.
- if (keyPos + 1 < extensionSubtagsLength &&
- extensionSubtags[keyPos + 1].length > 2)
- {
- // Step 11.g.ii.1.a.
- var requestedValue = extensionSubtags[keyPos + 1];
+ // Step 8.h.
+ if (extension !== undefined) {
+ // Step 8.h.i.
+ var requestedValue = UnicodeExtensionValue(extension, key);
- // Step 11.g.ii.1.b.
- valuePos = callFunction(ArrayIndexOf, keyLocaleData, requestedValue);
+ // Step 8.h.ii.
+ if (requestedValue !== undefined) {
+ // Steps 8.a-d.
+ keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
- // Step 11.g.ii.1.c.
- if (valuePos !== -1) {
+ // Step 8.h.ii.1.
+ if (requestedValue !== "") {
+ // Step 8.h.ii.1.a.
+ if (callFunction(ArrayIndexOf, keyLocaleData, requestedValue) !== -1) {
value = requestedValue;
supportedExtensionAddition = "-" + key + "-" + value;
}
} else {
- // Step 11.g.ii.2.
+ // Step 8.h.ii.2.
// According to the LDML spec, if there's no type value,
// and true is an allowed value, it's used.
- // Step 11.g.ii.2.a.
- valuePos = callFunction(ArrayIndexOf, keyLocaleData, "true");
-
- // Step 11.g.ii.2.b.
- if (valuePos !== -1)
+ if (callFunction(ArrayIndexOf, keyLocaleData, "true") !== -1) {
value = "true";
+ supportedExtensionAddition = "-" + key;
+ }
}
}
}
// Options override all.
- // Step 11.h.i.
+ // Step 8.i.i.
var optionsValue = options[key];
- // Step 11.h, 11.h.ii.
- if (optionsValue !== undefined &&
- callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1)
- {
- // Step 11.h.ii.1.
- if (optionsValue !== value) {
+ // Step 8.i.ii.
+ assert(typeof optionsValue === "string" ||
+ optionsValue === undefined ||
+ optionsValue === null,
+ "unexpected type for options value");
+
+ // Steps 8.i, 8.i.iii.1.
+ if (optionsValue !== undefined && optionsValue !== value) {
+ // Steps 8.a-d.
+ if (keyLocaleData === undefined)
+ keyLocaleData = callFunction(localeDataProvider[key], null, foundLocale);
+
+ // Step 8.i.iii.
+ if (callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) {
value = optionsValue;
supportedExtensionAddition = "";
}
}
- // Steps 11.i-k.
+ // Locale data provides default value.
+ if (value === undefined) {
+ // Steps 8.a-f.
+ value = keyLocaleData === undefined
+ ? callFunction(localeDataProvider.default[key], null, foundLocale)
+ : keyLocaleData[0];
+ }
+
+ // Step 8.j.
+ assert(typeof value === "string" || value === null, "unexpected locale data value");
result[key] = value;
+
+ // Step 8.k.
supportedExtension += supportedExtensionAddition;
- i++;
}
- // Step 12.
+ // Step 9.
if (supportedExtension.length > 2) {
- var preExtension = callFunction(String_substring, foundLocale, 0, extensionIndex);
- var postExtension = callFunction(String_substring, foundLocale, extensionIndex);
- foundLocale = preExtension + supportedExtension + postExtension;
+ assert(!callFunction(std_String_startsWith, foundLocale, "x-"),
+ "unexpected privateuse-only locale returned from ICU");
+
+ // Step 9.a.
+ var privateIndex = callFunction(std_String_indexOf, foundLocale, "-x-");
+
+ // Steps 9.b-c.
+ if (privateIndex === -1) {
+ foundLocale += supportedExtension;
+ } else {
+ var preExtension = callFunction(String_substring, foundLocale, 0, privateIndex);
+ var postExtension = callFunction(String_substring, foundLocale, privateIndex);
+ foundLocale = preExtension + supportedExtension + postExtension;
+ }
+
+ // Step 9.d-e (Step 9.e is not required in this implementation, because we don't
+ // canonicalize Unicode extension subtags).
+ assertIsValidAndCanonicalLanguageTag(foundLocale, "same locale with extension");
}
- // Steps 13-14.
+ // Step 10.
result.locale = foundLocale;
+
+ // Step 11.
return result;
}
@@ -904,31 +534,29 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte
* matching (possibly fallback) locale. Locales appear in the same order in the
* returned list as in the input list.
*
- * Spec: ECMAScript Internationalization API Specification, 9.2.6.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.7.
*/
function LookupSupportedLocales(availableLocales, requestedLocales) {
- // Steps 1-2.
- var len = requestedLocales.length;
- var subset = new List();
+ // Step 1.
+ var subset = [];
- // Steps 3-4.
- var k = 0;
- while (k < len) {
- // Steps 4.a-b.
- var locale = requestedLocales[k];
+ // Step 2.
+ for (var i = 0; i < requestedLocales.length; i++) {
+ var locale = requestedLocales[i];
+
+ // Step 2.a.
var noExtensionsLocale = removeUnicodeExtensions(locale);
- // Step 4.c-d.
+ // Step 2.b.
var availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
- if (availableLocale !== undefined)
- callFunction(std_Array_push, subset, locale);
- // Step 4.e.
- k++;
+ // Step 2.c.
+ if (availableLocale !== undefined)
+ _DefineDataProperty(subset, subset.length, locale);
}
- // Steps 5-6.
- return callFunction(std_Array_slice, subset, 0);
+ // Step 3.
+ return subset;
}
@@ -937,7 +565,7 @@ function LookupSupportedLocales(availableLocales, requestedLocales) {
* matching (possibly fallback) locale. Locales appear in the same order in the
* returned list as in the input list.
*
- * Spec: ECMAScript Internationalization API Specification, 9.2.7.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.8.
*/
function BestFitSupportedLocales(availableLocales, requestedLocales) {
// don't have anything better
@@ -950,19 +578,17 @@ function BestFitSupportedLocales(availableLocales, requestedLocales) {
* matching (possibly fallback) locale. Locales appear in the same order in the
* returned list as in the input list.
*
- * Spec: ECMAScript Internationalization API Specification, 9.2.8.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.9.
*/
function SupportedLocales(availableLocales, requestedLocales, options) {
- /*jshint laxbreak: true */
-
// Step 1.
var matcher;
if (options !== undefined) {
- // Steps 1.a-b.
+ // Step 1.a.
options = ToObject(options);
- matcher = options.localeMatcher;
- // Step 1.c.
+ // Step 1.b
+ matcher = options.localeMatcher;
if (matcher !== undefined) {
matcher = ToString(matcher);
if (matcher !== "lookup" && matcher !== "best fit")
@@ -970,12 +596,12 @@ function SupportedLocales(availableLocales, requestedLocales, options) {
}
}
- // Steps 2-3.
+ // Steps 2-5.
var subset = (matcher === undefined || matcher === "best fit")
? BestFitSupportedLocales(availableLocales, requestedLocales)
: LookupSupportedLocales(availableLocales, requestedLocales);
- // Step 4.
+ // Steps 6-7.
for (var i = 0; i < subset.length; i++) {
_DefineDataProperty(subset, i, subset[i],
ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
@@ -983,7 +609,7 @@ function SupportedLocales(availableLocales, requestedLocales, options) {
_DefineDataProperty(subset, "length", subset.length,
ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
- // Step 5.
+ // Step 8.
return subset;
}
@@ -993,7 +619,7 @@ function SupportedLocales(availableLocales, requestedLocales, options) {
* the required type, checks whether it is one of a list of allowed values,
* and fills in a fallback value if necessary.
*
- * Spec: ECMAScript Internationalization API Specification, 9.2.9.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.10.
*/
function GetOption(options, property, type, values, fallback) {
// Step 1.
@@ -1049,26 +675,38 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
}
+// Symbols in the self-hosting compartment can't be cloned, use a separate
+// object to hold the actual symbol value.
+// TODO: Can we add support to clone symbols?
+var intlFallbackSymbolHolder = { value: undefined };
+
/**
- * Weak map used to track the initialize-as-Intl status (and, if an object has
- * been so initialized, the Intl-specific internal properties) of all objects.
- * Presence of an object as a key within this map indicates that the object has
- * its [[initializedIntlObject]] internal property set to true. The associated
- * value is an object whose structure is documented in |initializeIntlObject|
- * below.
+ * The [[FallbackSymbol]] symbol of the %Intl% intrinsic object.
*
- * Ideally we'd be using private symbols for internal properties, but
- * SpiderMonkey doesn't have those yet.
+ * This symbol is used to implement the legacy constructor semantics for
+ * Intl.DateTimeFormat and Intl.NumberFormat.
*/
-var internalsMap = new WeakMap();
+function intlFallbackSymbol() {
+ var fallbackSymbol = intlFallbackSymbolHolder.value;
+ if (!fallbackSymbol)
+ intlFallbackSymbolHolder.value = fallbackSymbol = std_Symbol();
+ return fallbackSymbol;
+}
/**
- * Set the [[initializedIntlObject]] internal property of |obj| to true.
+ * Initializes the INTL_INTERNALS_OBJECT_SLOT of the given object.
*/
-function initializeIntlObject(obj) {
+function initializeIntlObject(obj, type, lazyData) {
assert(IsObject(obj), "Non-object passed to initializeIntlObject");
+ assert((type === "Collator" && IsCollator(obj)) ||
+ (type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+ (type === "NumberFormat" && IsNumberFormat(obj)) ||
+ (type === "PluralRules" && IsPluralRules(obj)) ||
+ (type === "RelativeTimeFormat" && IsRelativeTimeFormat(obj)),
+ "type must match the object's class");
+ assert(IsObject(lazyData), "non-object lazy data");
// Intl-initialized objects are weird. They have [[initializedIntlObject]]
// set on them, but they don't *necessarily* have any other properties.
@@ -1076,51 +714,26 @@ function initializeIntlObject(obj) {
// The meaning of an internals object for an object |obj| is as follows.
//
- // If the .type is "partial", |obj| has [[initializedIntlObject]] set but
- // nothing else. No other property of |internals| can be used. (This
- // occurs when InitializeCollator or similar marks an object as
- // [[initializedIntlObject]] but fails before marking it as the appropriate
- // more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
+ // The .type property indicates the type of Intl object that |obj| is:
+ // "Collator", "DateTimeFormat", "NumberFormat", or "PluralRules" (likely
+ // with more coming in future Intl specs).
//
- // Otherwise, the .type indicates the type of Intl object that |obj| is:
- // "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
- // in future Intl specs). In these cases |obj| *conceptually* also has
- // [[initializedCollator]] or similar set, and all the other properties
- // implied by that.
- //
- // If |internals| doesn't have a "partial" .type, two additional properties
- // have meaning. The .lazyData property stores information needed to
- // compute -- without observable side effects -- the actual internal Intl
- // properties of |obj|. If it is non-null, then the actual internal
- // properties haven't been computed, and .lazyData must be processed by
+ // The .lazyData property stores information needed to compute -- without
+ // observable side effects -- the actual internal Intl properties of
+ // |obj|. If it is non-null, then the actual internal properties haven't
+ // been computed, and .lazyData must be processed by
// |setInternalProperties| before internal Intl property values are
// available. If it is null, then the .internalProps property contains an
// object whose properties are the internal Intl properties of |obj|.
- internals.type = "partial";
- internals.lazyData = null;
+ var internals = std_Object_create(null);
+ internals.type = type;
+ internals.lazyData = lazyData;
internals.internalProps = null;
- callFunction(std_WeakMap_set, internalsMap, obj, internals);
- return internals;
-}
-
-
-/**
- * Mark |internals| as having the given type and lazy data.
- */
-function setLazyData(internals, type, lazyData)
-{
- assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
- assert(type === "Collator" || type === "DateTimeFormat" ||
- type === "NumberFormat" || type === "PluralRules" ||
- type === "RelativeTimeFormat",
- "bad type");
- assert(IsObject(lazyData), "non-object lazy data");
-
- // Set in reverse order so that the .type change is a barrier.
- internals.lazyData = lazyData;
- internals.type = type;
+ assert(UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT) === null,
+ "Internal slot already initialized?");
+ UnsafeSetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT, internals);
}
@@ -1128,9 +741,7 @@ function setLazyData(internals, type, lazyData)
* Set the internal properties object for an |internals| object previously
* associated with lazy data.
*/
-function setInternalProperties(internals, internalProps)
-{
- assert(internals.type !== "partial", "newborn internals can't have computed internals");
+function setInternalProperties(internals, internalProps) {
assert(IsObject(internals.lazyData), "lazy data must exist already");
assert(IsObject(internalProps), "internalProps argument should be an object");
@@ -1144,10 +755,8 @@ function setInternalProperties(internals, internalProps)
* Get the existing internal properties out of a non-newborn |internals|, or
* null if none have been computed.
*/
-function maybeInternalProperties(internals)
-{
+function maybeInternalProperties(internals) {
assert(IsObject(internals), "non-object passed to maybeInternalProperties");
- assert(internals.type !== "partial", "maybeInternalProperties must only be used on completely-initialized internals objects");
var lazyData = internals.lazyData;
if (lazyData)
return null;
@@ -1157,48 +766,33 @@ function maybeInternalProperties(internals)
/**
- * Return whether |obj| has an[[initializedIntlObject]] property set to true.
- */
-function isInitializedIntlObject(obj) {
-#ifdef DEBUG
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
- if (IsObject(internals)) {
- assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
- var type = internals.type;
- assert(type === "partial" || type === "Collator" ||
- type === "DateTimeFormat" || type === "NumberFormat" ||
- type === "PluralRules" || type === "RelativeTimeFormat",
- "unexpected type");
- assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
- assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
- } else {
- assert(internals === undefined, "bad mapping for |obj|");
- }
-#endif
- return callFunction(std_WeakMap_has, internalsMap, obj);
-}
-
-
-/**
- * Check that |obj| meets the requirements for "this Collator object", "this
- * NumberFormat object", or "this DateTimeFormat object" as used in the method
- * with the given name. Throw a TypeError if |obj| doesn't meet these
- * requirements. But if it does, return |obj|'s internals object (*not* the
- * object holding its internal properties!), associated with it by
- * |internalsMap|, with structure specified above.
+ * Return |obj|'s internals object (*not* the object holding its internal
+ * properties!), with structure specified above.
*
* Spec: ECMAScript Internationalization API Specification, 10.3.
* Spec: ECMAScript Internationalization API Specification, 11.3.
* Spec: ECMAScript Internationalization API Specification, 12.3.
*/
-function getIntlObjectInternals(obj, className, methodName) {
- assert(typeof className === "string", "bad className for getIntlObjectInternals");
-
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
- assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
-
- if (internals === undefined || internals.type !== className)
- ThrowTypeError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
+function getIntlObjectInternals(obj) {
+ assert(IsObject(obj), "getIntlObjectInternals called with non-Object");
+ assert(IsCollator(obj) || IsDateTimeFormat(obj) || IsNumberFormat(obj) ||
+ IsPluralRules(obj) || IsRelativeTimeFormat(obj),
+ "getIntlObjectInternals called with non-Intl object");
+
+ var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
+
+ assert(IsObject(internals), "internals not an object");
+ assert(hasOwn("type", internals), "missing type");
+ assert((internals.type === "Collator" && IsCollator(obj)) ||
+ (internals.type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
+ (internals.type === "NumberFormat" && IsNumberFormat(obj)) ||
+ (internals.type === "PluralRules" && IsPluralRules(obj)) ||
+ (internals.type === "RelativeTimeFormat" && IsRelativeTimeFormat(obj)),
+ "type must match the object's class");
+ assert(hasOwn("lazyData", internals),
+ "missing lazyData");
+ assert(hasOwn("internalProps", internals),
+ "missing internalProps");
return internals;
}
@@ -1208,35 +802,32 @@ function getIntlObjectInternals(obj, className, methodName) {
* Get the internal properties of known-Intl object |obj|. For use only by
* C++ code that knows what it's doing!
*/
-function getInternals(obj)
-{
- assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
+function getInternals(obj) {
+ var internals = getIntlObjectInternals(obj);
- var internals = callFunction(std_WeakMap_get, internalsMap, obj);
+ // If internal properties have already been computed, use them.
+ var internalProps = maybeInternalProperties(internals);
+ if (internalProps)
+ return internalProps;
- assert(internals.type !== "partial", "must have been successfully initialized");
- var lazyData = internals.lazyData;
- if (!lazyData)
- return internals.internalProps;
-
- var internalProps;
+ // Otherwise it's time to fully create them.
var type = internals.type;
-
+
switch (type) {
case "Collator":
- internalProps = resolveCollatorInternals(lazyData);
+ internalProps = resolveCollatorInternals(internals.lazyData);
break;
case "DateTimeFormat":
- internalProps = resolveDateTimeFormatInternals(lazyData);
+ internalProps = resolveDateTimeFormatInternals(internals.lazyData);
break;
case "PluralRules":
- internalProps = resolvePluralRulesInternals(lazyData);
+ internalProps = resolvePluralRulesInternals(internals.lazyData);
break;
- case "RelativeTimeFormat":
- internalProps = resolveRelativeTimeFormatInternals(lazyData);
+ case "NumberFormat":
+ internalProps = resolveNumberFormatInternals(internals.lazyData);
break;
- default: // type === "NumberFormat"
- internalProps = resolveNumberFormatInternals(lazyData);
+ default: // type === "RelativeTimeFormat"
+ internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
break;
}
setInternalProperties(internals, internalProps);