Bug 1433303 - Part 1: Implement Intl.Locale proposal. r=jwalden

Intl.Locale.m{ax,in}imize() are implemented in part 2.

Differential Revision: https://phabricator.services.mozilla.com/D38874

--HG--
extra : moz-landing-system : lando
This commit is contained in:
André Bargull 2019-07-29 09:35:53 +00:00
parent 15afab0390
commit 2684e5023e
15 changed files with 1587 additions and 8 deletions

View File

@ -839,7 +839,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
// application.
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 38;
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 39;
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | \

View File

@ -568,6 +568,82 @@ function parseLanguageTag(locale) {
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.
@ -807,7 +883,7 @@ function CanonicalizeLanguageTagObject(localeObj) {
// Canonicalize Unicode locale extension subtag if present.
if (ext[0] === "u") {
var {attributes, keywords} = UnicodeExtensionComponents(ext);
extensions[i] = CanonicalizeUnicodeExtension(attributes, keywords);
extensions[i] = CanonicalizeUnicodeExtension(attributes, keywords, false);
}
// Canonicalize Unicode BCP 47 T extension if present.
@ -934,7 +1010,7 @@ function UnicodeExtensionComponents(extension) {
* - All keys and types use the canonical form (from the name attribute;
* see Section 3.6.4 U Extension Data Files).
*/
function CanonicalizeUnicodeExtension(attributes, keywords) {
function CanonicalizeUnicodeExtension(attributes, keywords, canonicalForm) {
assert(attributes.length > 0 || keywords.length > 0,
"unexpected empty Unicode locale extension components");
@ -969,14 +1045,42 @@ function CanonicalizeUnicodeExtension(attributes, keywords) {
// Append all attributes.
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
assert(attribute === callFunction(std_String_toLowerCase, attribute),
"Attributes are already canonicalized to lower case");
// UnicodeExtensionComponents ignores duplicate attributes.
if (canonicalForm && i > 0 && attributes[i - 1] === attribute) {
continue;
}
extension += "-" + attributes[i];
}
// Append all keywords.
for (var i = 0; i < keywords.length; i++) {
var {key, value} = keywords[i];
assert(key === callFunction(std_String_toLowerCase, key) &&
value === callFunction(std_String_toLowerCase, value),
"Keywords are already canonicalized to lower case");
// UnicodeExtensionComponents ignores duplicate keys.
if (canonicalForm && i > 0 && keywords[i - 1].key === key) {
continue;
}
extension += "-" + key;
if (canonicalForm &&
hasOwn(key, deprecatedUnicodeExtensionTypes) &&
hasOwn(value, deprecatedUnicodeExtensionTypes[key]))
{
value = deprecatedUnicodeExtensionTypes[key][value];
assert(value === callFunction(std_String_toLowerCase, value),
"Preferred keyword value is already in lower case");
}
// Type value "true" is removed.
if (value !== "" && value !== "true")
extension += "-" + value;
@ -1132,6 +1236,20 @@ function IsASCIIAlphaString(s) {
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.
*/
@ -1325,6 +1443,10 @@ function CanonicalizeLocaleList(locales) {
if (typeof locales === "string")
return [ValidateAndCanonicalizeLanguageTag(locales)];
var unboxedLocale = callFunction(unboxLocaleOrNull, locales);
if (unboxedLocale !== null)
return [StringFromLanguageTagObject(unboxedLocale.locale)]
// Step 2.
var seen = [];
@ -1348,11 +1470,11 @@ function CanonicalizeLocaleList(locales) {
if (!(typeof kValue === "string" || IsObject(kValue)))
ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
// Step 7.c.iii.
var tag = ToString(kValue);
// Step 7.c.iv.
tag = ValidateAndCanonicalizeLanguageTag(tag);
// Steps 7.c.iii-iv.
var unboxedLocale = callFunction(unboxLocaleOrNull, kValue);
var tag = unboxedLocale !== null
? StringFromLanguageTagObject(unboxedLocale.locale)
: ValidateAndCanonicalizeLanguageTag(ToString(kValue));
// Step 7.c.v.
if (callFunction(ArrayIndexOf, seen, tag) === -1)

View File

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Intl.Locale implementation. */
#include "builtin/intl/Locale.h"
#include "mozilla/Assertions.h"
#include "jsapi.h"
#include "builtin/intl/CommonFunctions.h"
#include "js/TypeDecls.h"
#include "vm/GlobalObject.h"
#include "vm/JSContext.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
using namespace js;
const Class 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("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));
return true;
}
bool js::AddLocaleConstructor(JSContext* cx, JS::Handle<JSObject*> intl) {
return GlobalObject::addLocaleConstructor(cx, intl);
}

View File

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef builtin_intl_Locale_h
#define builtin_intl_Locale_h
#include <stdint.h>
#include "builtin/SelfHostingDefines.h"
#include "js/Class.h"
#include "vm/NativeObject.h"
namespace js {
class GlobalObject;
class LocaleObject : public NativeObject {
public:
static const Class 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);
} // namespace js
#endif /* builtin_intl_Locale_h */

View File

@ -0,0 +1,605 @@
/* 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);
}
/**
* 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.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");

View File

@ -0,0 +1,371 @@
// Generated by make_intl_data.py. DO NOT EDIT.
// Version: CLDR-35.1
// URL: https://unicode.org/Public/cldr/35.1/core.zip
/**
* Mapping from deprecated BCP 47 Unicode extension types to their preferred
* values.
*
* Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files
*/
var deprecatedUnicodeExtensionTypes = {
ca: {
// Ethiopic calendar, Amete Alem (epoch approx. 5493 B.C.E)
"ethiopic-amete-alem": "ethioaa",
// Civil (algorithmic) Arabic calendar
"islamicc": "islamic-civil",
},
kb: {
// The second level to be backwards
"yes": "true",
},
kc: {
// The case level is inserted in front of tertiary
"yes": "true",
},
kh: {
// Hiragana to be sorted before all non-variable on quaternary level
"yes": "true",
},
kk: {
// Convert text into Normalization Form D before calculating collation weights
"yes": "true",
},
kn: {
// A sequence of decimal digits is sorted at primary level with its numeric value
"yes": "true",
},
ks: {
// The primary level
"primary": "level1",
// The tertiary level
"tertiary": "level3",
},
ms: {
// UK System of measurement: feet, pints, etc.; pints are 20oz
"imperial": "uksystem",
},
rg: {
"cn11": "cnbj",
"cn12": "cntj",
"cn13": "cnhe",
"cn14": "cnsx",
"cn15": "cnmn",
"cn21": "cnln",
"cn22": "cnjl",
"cn23": "cnhl",
"cn31": "cnsh",
"cn32": "cnjs",
"cn33": "cnzj",
"cn34": "cnah",
"cn35": "cnfj",
"cn36": "cnjx",
"cn37": "cnsd",
"cn41": "cnha",
"cn42": "cnhb",
"cn43": "cnhn",
"cn44": "cngd",
"cn45": "cngx",
"cn46": "cnhi",
"cn50": "cncq",
"cn51": "cnsc",
"cn52": "cngz",
"cn53": "cnyn",
"cn54": "cnxz",
"cn61": "cnsn",
"cn62": "cngs",
"cn63": "cnqh",
"cn64": "cnnx",
"cn65": "cnxj",
"cz10a": "cz110",
"cz10b": "cz111",
"cz10c": "cz112",
"cz10d": "cz113",
"cz10e": "cz114",
"cz10f": "cz115",
"cz611": "cz663",
"cz612": "cz632",
"cz613": "cz633",
"cz614": "cz634",
"cz615": "cz635",
"cz621": "cz641",
"cz622": "cz642",
"cz623": "cz643",
"cz624": "cz644",
"cz626": "cz646",
"cz627": "cz647",
"czjc": "cz31",
"czjm": "cz64",
"czka": "cz41",
"czkr": "cz52",
"czli": "cz51",
"czmo": "cz80",
"czol": "cz71",
"czpa": "cz53",
"czpl": "cz32",
"czpr": "cz10",
"czst": "cz20",
"czus": "cz42",
"czvy": "cz63",
"czzl": "cz72",
"fra": "frges",
"frb": "frnaq",
"frc": "frara",
"frd": "frbfc",
"fre": "frbre",
"frf": "frcvl",
"frg": "frges",
"frh": "frcor",
"fri": "frbfc",
"frj": "fridf",
"frk": "frocc",
"frl": "frnaq",
"frm": "frges",
"frn": "frocc",
"fro": "frhdf",
"frp": "frnor",
"frq": "frnor",
"frr": "frpdl",
"frs": "frhdf",
"frt": "frnaq",
"fru": "frpac",
"frv": "frara",
"laxn": "laxs",
"lud": "lucl",
"lug": "luec",
"lul": "luca",
"mrnkc": "mr13",
"nzn": "nzauk",
"nzs": "nzcan",
"omba": "ombj",
"omsh": "omsj",
"plds": "pl02",
"plkp": "pl04",
"pllb": "pl08",
"plld": "pl10",
"pllu": "pl06",
"plma": "pl12",
"plmz": "pl14",
"plop": "pl16",
"plpd": "pl20",
"plpk": "pl18",
"plpm": "pl22",
"plsk": "pl26",
"plsl": "pl24",
"plwn": "pl28",
"plwp": "pl30",
"plzp": "pl32",
"tteto": "tttob",
"ttrcm": "ttmrc",
"ttwto": "tttob",
"twkhq": "twkhh",
"twtnq": "twtnn",
"twtpq": "twnwt",
"twtxq": "twtxg",
},
sd: {
"cn11": "cnbj",
"cn12": "cntj",
"cn13": "cnhe",
"cn14": "cnsx",
"cn15": "cnmn",
"cn21": "cnln",
"cn22": "cnjl",
"cn23": "cnhl",
"cn31": "cnsh",
"cn32": "cnjs",
"cn33": "cnzj",
"cn34": "cnah",
"cn35": "cnfj",
"cn36": "cnjx",
"cn37": "cnsd",
"cn41": "cnha",
"cn42": "cnhb",
"cn43": "cnhn",
"cn44": "cngd",
"cn45": "cngx",
"cn46": "cnhi",
"cn50": "cncq",
"cn51": "cnsc",
"cn52": "cngz",
"cn53": "cnyn",
"cn54": "cnxz",
"cn61": "cnsn",
"cn62": "cngs",
"cn63": "cnqh",
"cn64": "cnnx",
"cn65": "cnxj",
"cz10a": "cz110",
"cz10b": "cz111",
"cz10c": "cz112",
"cz10d": "cz113",
"cz10e": "cz114",
"cz10f": "cz115",
"cz611": "cz663",
"cz612": "cz632",
"cz613": "cz633",
"cz614": "cz634",
"cz615": "cz635",
"cz621": "cz641",
"cz622": "cz642",
"cz623": "cz643",
"cz624": "cz644",
"cz626": "cz646",
"cz627": "cz647",
"czjc": "cz31",
"czjm": "cz64",
"czka": "cz41",
"czkr": "cz52",
"czli": "cz51",
"czmo": "cz80",
"czol": "cz71",
"czpa": "cz53",
"czpl": "cz32",
"czpr": "cz10",
"czst": "cz20",
"czus": "cz42",
"czvy": "cz63",
"czzl": "cz72",
"fra": "frges",
"frb": "frnaq",
"frc": "frara",
"frd": "frbfc",
"fre": "frbre",
"frf": "frcvl",
"frg": "frges",
"frh": "frcor",
"fri": "frbfc",
"frj": "fridf",
"frk": "frocc",
"frl": "frnaq",
"frm": "frges",
"frn": "frocc",
"fro": "frhdf",
"frp": "frnor",
"frq": "frnor",
"frr": "frpdl",
"frs": "frhdf",
"frt": "frnaq",
"fru": "frpac",
"frv": "frara",
"laxn": "laxs",
"lud": "lucl",
"lug": "luec",
"lul": "luca",
"mrnkc": "mr13",
"nzn": "nzauk",
"nzs": "nzcan",
"omba": "ombj",
"omsh": "omsj",
"plds": "pl02",
"plkp": "pl04",
"pllb": "pl08",
"plld": "pl10",
"pllu": "pl06",
"plma": "pl12",
"plmz": "pl14",
"plop": "pl16",
"plpd": "pl20",
"plpk": "pl18",
"plpm": "pl22",
"plsk": "pl26",
"plsl": "pl24",
"plwn": "pl28",
"plwp": "pl30",
"plzp": "pl32",
"tteto": "tttob",
"ttrcm": "ttmrc",
"ttwto": "tttob",
"twkhq": "twkhh",
"twtnq": "twtnn",
"twtpq": "twnwt",
"twtxq": "twtxg",
},
tz: {
// Amundsen-Scott Station, South Pole
"aqams": "nzakl",
// Chongqing, China
"cnckg": "cnsha",
// Harbin, China
"cnhrb": "cnsha",
// Kashgar, China
"cnkhg": "cnurc",
// Havana, Cuba
"cuba": "cuhav",
// Cairo, Egypt
"egypt": "egcai",
// Dublin, Ireland
"eire": "iedub",
// 5 hours behind UTC
"est": "utcw05",
// Greenwich Mean Time
"gmt0": "gmt",
// Hong Kong SAR China
"hongkong": "hkhkg",
// 10 hours behind UTC
"hst": "utcw10",
// Reykjavik, Iceland
"iceland": "isrey",
// Tehran, Iran
"iran": "irthr",
// Jerusalem
"israel": "jeruslm",
// Jamaica
"jamaica": "jmkin",
// Tokyo, Japan
"japan": "jptyo",
// Tripoli, Libya
"libya": "lytip",
// 7 hours behind UTC
"mst": "utcw07",
// Denver, United States
"navajo": "usden",
// Warsaw, Poland
"poland": "plwaw",
// Lisbon, Portugal
"portugal": "ptlis",
// Shanghai, China
"prc": "cnsha",
// Taipei, Taiwan
"roc": "twtpe",
// Seoul, South Korea
"rok": "krsel",
// Istanbul, Turkey
"turkey": "trist",
// UTC (Coordinated Universal Time)
"uct": "utc",
// Shiprock (Navajo), United States
"usnavajo": "usden",
// UTC (Coordinated Universal Time)
"zulu": "utc",
},
};

View File

@ -9,6 +9,7 @@
make_intl_data.py langtags [ldmlSupplemental.dtd supplementalMetadata.xml likelySubtags.xml]
make_intl_data.py tzdata
make_intl_data.py currency
make_intl_data.py unicode-ext
Target "langtags":
@ -26,6 +27,10 @@
Target "currency":
Generates the mapping from currency codes to decimal digits used for them.
Target "unicode-ext":
Generates the mapping from deprecated BCP 47 Unicode extension values to
their preferred values.
"""
from __future__ import print_function
@ -42,6 +47,7 @@ from contextlib import closing
from functools import partial, total_ordering
from itertools import chain, groupby, tee
from operator import attrgetter, itemgetter
from zipfile import ZipFile
if sys.version_info.major == 2:
from itertools import ifilter as filter, ifilterfalse as filterfalse, imap as map
@ -1603,6 +1609,224 @@ def updateCurrency(topsrcdir, args):
updateFrom(currencyTmpFile.name)
def writeUnicodeExtensionsFile(version, url, mapping, out):
with io.open(out, mode="w", encoding="utf-8", newline="") as f:
println = partial(print, file=f)
println(generatedFileWarning)
println(u"// Version: CLDR-{}".format(version))
println(u"// URL: {}".format(url))
println(u"""
/**
* Mapping from deprecated BCP 47 Unicode extension types to their preferred
* values.
*
* Spec: https://www.unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files
*/""")
println(u"var deprecatedUnicodeExtensionTypes = {")
for ext_name in sorted(mapping):
println(u" {}: {{".format(ext_name))
is_first = True
for type in sorted(mapping[ext_name]):
mapped = mapping[ext_name][type]
has_description = mapped["description"] is not None
if not is_first and has_description:
println(u"")
is_first = False
if has_description:
println(u" // {}".format(mapped["description"]))
println(u" \"{}\": \"{}\",".format(type, mapped["preferred"]))
println(u" },")
println(u"};")
def updateUnicodeExtensions(args):
""" Update the UnicodeExtensionsGenerated.js file. """
import xml.etree.ElementTree as ET
version = args.version
url = args.url
out = args.out
filename = args.file
url = url.replace("<VERSION>", version)
print("Arguments:")
print("\tCLDR version: %s" % version)
print("\tDownload url: %s" % url)
if filename is not None:
print("\tLocal CLDR core.zip file: %s" % filename)
print("\tOutput file: %s" % out)
print("")
def updateFrom(data):
# Match all xml-files in the BCP 47 directory.
bcpFileRE = re.compile(r"^common/bcp47/.+\.xml$")
# https://www.unicode.org/reports/tr35/#Unicode_locale_identifier
#
# type = alphanum{3,8} (sep alphanum{3,8})* ;
typeRE = re.compile(r"^[a-z0-9]{3,8}(-[a-z0-9]{3,8})*$")
# Mapping from Unicode extension types to dict of deprecated to
# preferred values.
mapping = {}
with ZipFile(data) as zip_file:
for name in zip_file.namelist():
if not bcpFileRE.match(name):
continue
tree = ET.parse(zip_file.open(name))
for keyword in tree.iterfind(".//keyword/key"):
# Skip over keywords whose extension is not "u".
if keyword.get("extension", "u") != "u":
continue
extension_name = keyword.get("name")
for type in keyword.iterfind("type"):
# <https://unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files>:
#
# The key or type name used by Unicode locale extension with 'u' extension
# syntax or the 't' extensions syntax. When alias below is absent, this
# name can be also used with the old style "@key=type" syntax.
name = type.get("name")
# Ignore the special name:
# - <https://unicode.org/reports/tr35/#CODEPOINTS>
# - <https://unicode.org/reports/tr35/#REORDER_CODE>
# - <https://unicode.org/reports/tr35/#RG_KEY_VALUE>
# - <https://unicode.org/reports/tr35/#SUBDIVISION_CODE>
# - <https://unicode.org/reports/tr35/#PRIVATE_USE>
if name in ("CODEPOINTS", "REORDER_CODE", "RG_KEY_VALUE",
"SUBDIVISION_CODE", "PRIVATE_USE"):
continue
# All other names should match the 'type' production.
assert typeRE.match(name) is not None, (
"{} matches the 'type' production".format(name))
# <https://unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files>:
#
# The preferred value of the deprecated key, type or attribute element.
# When a key, type or attribute element is deprecated, this attribute is
# used for specifying a new canonical form if available.
preferred = type.get("preferred")
# <https://unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files>:
#
# The BCP 47 form is the canonical form, and recommended. Other aliases are
# included only for backwards compatibility.
alias = type.get("alias")
# <https://unicode.org/reports/tr35/#Unicode_Locale_Extension_Data_Files>:
#
# The description of the key, type or attribute element.
description = type.get("description")
# <https://unicode.org/reports/tr35/#Canonical_Unicode_Locale_Identifiers>
#
# Use the bcp47 data to replace keys, types, tfields, and tvalues by their
# canonical forms. See Section 3.6.4 U Extension Data Files) and Section
# 3.7.1 T Extension Data Files. The aliases are in the alias attribute
# value, while the canonical is in the name attribute value.
# 'preferred' contains the new preferred name, 'alias' the compatibility
# name, but then there's this entry where 'preferred' and 'alias' are the
# same. So which one to choose? Assume 'preferred' is the actual canonical
# name.
#
# <type name="islamicc"
# description="Civil (algorithmic) Arabic calendar"
# deprecated="true"
# preferred="islamic-civil"
# alias="islamic-civil"/>
if preferred is not None:
assert typeRE.match(preferred), preferred
mapping.setdefault(extension_name, {})[name] = {
"preferred": preferred,
"description": description,
}
if alias is not None:
for alias_name in alias.lower().split(" "):
# Ignore alias entries which don't match the 'type' production.
if typeRE.match(alias_name) is None:
continue
# See comment above when 'alias' and 'preferred' are both present.
if (preferred is not None and
name in mapping[extension_name]):
continue
# Skip over entries where 'name' and 'alias' are equal.
#
# <type name="pst8pdt"
# description="POSIX style time zone for US Pacific Time"
# alias="PST8PDT"
# since="1.8"/>
if name == alias_name:
continue
mapping.setdefault(extension_name, {})[alias_name] = {
"preferred": name,
"description": description,
}
# Find subdivision and region replacements.
#
# <https://www.unicode.org/reports/tr35/#Canonical_Unicode_Locale_Identifiers>
#
# Replace aliases in special key values:
# - If there is an 'sd' or 'rg' key, replace any subdivision alias
# in its value in the same way, using subdivisionAlias data.
tree = ET.parse(zip_file.open("common/supplemental/supplementalMetadata.xml"))
for alias in tree.iterfind(".//subdivisionAlias"):
type = alias.get("type")
assert typeRE.match(type) is not None, (
"{} matches the 'type' production".format(type))
# Take the first replacement when multiple ones are present.
replacement = alias.get("replacement").split(" ")[0].lower()
# Skip over invalid replacements.
#
# <subdivisionAlias type="fi01" replacement="AX" reason="overlong"/>
#
# It's not entirely clear to me if CLDR actually wants to use
# "axzzzz" as the replacement for this case.
if typeRE.match(replacement) is None:
continue
# 'subdivisionAlias' applies to 'rg' and 'sd' keys.
mapping.setdefault("rg", {})[type] = {
"preferred": replacement,
"description": None,
}
mapping.setdefault("sd", {})[type] = {
"preferred": replacement,
"description": None,
}
writeUnicodeExtensionsFile(version, url, mapping, out)
if filename is not None:
print("Always make sure you have the newest CLDR core.zip!")
with open(filename, "rb") as cldr_file:
updateFrom(cldr_file)
else:
print("Downloading CLDR core.zip...")
with closing(urlopen(url)) as cldr_file:
cldr_data = io.BytesIO(cldr_file.read())
updateFrom(cldr_data)
if __name__ == "__main__":
import argparse
@ -1673,5 +1897,23 @@ if __name__ == "__main__":
help="Local currency code list file, if omitted uses <URL>")
parser_currency.set_defaults(func=partial(updateCurrency, topsrcdir))
parser_unicode_ext = subparsers.add_parser("unicode-ext", help="Update Unicode extensions")
parser_unicode_ext.add_argument("--version",
metavar="VERSION",
required=True,
help="CLDR version number")
parser_unicode_ext.add_argument("--url",
metavar="URL",
default="https://unicode.org/Public/cldr/<VERSION>/core.zip",
type=EnsureHttps,
help="Download url CLDR data (default: %(default)s)")
parser_unicode_ext.add_argument("--out",
default="UnicodeExtensionsGenerated.js",
help="Output file (default: %(default)s)")
parser_unicode_ext.add_argument("file",
nargs="?",
help="Local cldr-core.zip file, if omitted uses <URL>")
parser_unicode_ext.set_defaults(func=updateUnicodeExtensions)
args = parser.parse_args()
args.func(args)

View File

@ -31,6 +31,7 @@
\
_(IntlGuardToCollator) \
_(IntlGuardToDateTimeFormat) \
_(IntlGuardToLocale) \
_(IntlGuardToNumberFormat) \
_(IntlGuardToPluralRules) \
_(IntlGuardToRelativeTimeFormat) \

View File

@ -11,6 +11,7 @@
#include "builtin/AtomicsObject.h"
#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"
@ -108,6 +109,7 @@ static bool CanInlineCrossRealm(InlinableNative native) {
case InlinableNative::IntlGuardToCollator:
case InlinableNative::IntlGuardToDateTimeFormat:
case InlinableNative::IntlGuardToLocale:
case InlinableNative::IntlGuardToNumberFormat:
case InlinableNative::IntlGuardToPluralRules:
case InlinableNative::IntlGuardToRelativeTimeFormat:
@ -314,6 +316,8 @@ IonBuilder::InliningResult IonBuilder::inlineNativeCall(CallInfo& callInfo,
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:

View File

@ -2630,6 +2630,10 @@ extern JS_FRIEND_API JSObject* ToWindowIfWindowProxy(JSObject* obj);
extern bool AddMozDateTimeFormatConstructor(JSContext* cx,
JS::Handle<JSObject*> intl);
// Create and add the Intl.Locale constructor function to the provided object.
// This function throws if called more than once per realm/global object.
extern bool AddLocaleConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
class MOZ_STACK_CLASS JS_FRIEND_API AutoAssertNoContentJS {
public:
explicit AutoAssertNoContentJS(JSContext* cx);

View File

@ -212,6 +212,7 @@ UNIFIED_SOURCES += [
'builtin/intl/CommonFunctions.cpp',
'builtin/intl/DateTimeFormat.cpp',
'builtin/intl/IntlObject.cpp',
'builtin/intl/Locale.cpp',
'builtin/intl/NumberFormat.cpp',
'builtin/intl/PluralRules.cpp',
'builtin/intl/RelativeTimeFormat.cpp',
@ -435,9 +436,11 @@ selfhosted.inputs = [
'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',
'builtin/Iterator.js',
'builtin/Map.js',
'builtin/Module.js',

View File

@ -1260,6 +1260,10 @@ static bool AddIntlExtras(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
if (!js::AddLocaleConstructor(cx, intl)) {
return false;
}
args.rval().setUndefined();
return true;
}

View File

@ -222,6 +222,7 @@
MACRO(InitializeCollator, InitializeCollator, "InitializeCollator") \
MACRO(InitializeDateTimeFormat, InitializeDateTimeFormat, \
"InitializeDateTimeFormat") \
MACRO(InitializeLocale, InitializeLocale, "InitializeLocale") \
MACRO(InitializeNumberFormat, InitializeNumberFormat, \
"InitializeNumberFormat") \
MACRO(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
@ -262,6 +263,7 @@
MACRO(lineNumber, lineNumber, "lineNumber") \
MACRO(literal, literal, "literal") \
MACRO(loc, loc, "loc") \
MACRO(Locale, Locale, "Locale") \
MACRO(locale, locale, "locale") \
MACRO(lookupGetter, lookupGetter, "__lookupGetter__") \
MACRO(lookupSetter, lookupSetter, "__lookupSetter__") \

View File

@ -95,6 +95,7 @@ class GlobalObject : public NativeObject {
DATE_TIME_FORMAT_PROTO,
PLURAL_RULES_PROTO,
RELATIVE_TIME_FORMAT_PROTO,
LOCALE_PROTO,
MODULE_PROTO,
IMPORT_ENTRY_PROTO,
EXPORT_ENTRY_PROTO,
@ -538,6 +539,11 @@ class GlobalObject : public NativeObject {
initIntlObject);
}
static JSObject* getOrCreateLocalePrototype(JSContext* cx,
Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, LOCALE_PROTO, initIntlObject);
}
static bool ensureModulePrototypesCreated(JSContext* cx,
Handle<GlobalObject*> global);
@ -840,6 +846,9 @@ class GlobalObject : public NativeObject {
// Implemented in builtin/intl/IntlObject.cpp.
static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
// Implemented in builtin/intl/Locale.cpp.
static bool addLocaleConstructor(JSContext* cx, HandleObject intl);
// Implemented in builtin/ModuleObject.cpp
static bool initModuleProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global);

View File

@ -24,6 +24,7 @@
#include "builtin/intl/Collator.h"
#include "builtin/intl/DateTimeFormat.h"
#include "builtin/intl/IntlObject.h"
#include "builtin/intl/Locale.h"
#include "builtin/intl/NumberFormat.h"
#include "builtin/intl/PluralRules.h"
#include "builtin/intl/RelativeTimeFormat.h"
@ -2508,6 +2509,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
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),
@ -2520,6 +2523,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("IsWrappedDateTimeFormat",
intrinsic_IsWrappedInstanceOfBuiltin<DateTimeFormatObject>, 1, 0),
JS_FN("IsWrappedLocale", intrinsic_IsWrappedInstanceOfBuiltin<LocaleObject>,
1, 0),
JS_FN("IsWrappedNumberFormat",
intrinsic_IsWrappedInstanceOfBuiltin<NumberFormatObject>, 1, 0),
@ -2527,6 +2532,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
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",