Commit 9e984162 authored by Sathya Gunasekaran's avatar Sathya Gunasekaran Committed by Commit Bot

[Intl] Port ResolveLocale

- Add a new Intl::ResolveLocale method and uses it in all the intl objects.
- Fix CanonicalizeLocaleList to call out to HasProperty as per spec.
- Add calls to CanonicalizeLocaleList where it was previously missing.
- Change CanonicalizeLocaleListJS calls to CanonicalizeLocaleList now
  that we have migrated ResolveLocale.

Bug: v8:5751
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I4249d2045c1556f18d570b00f7c92cbc3fa52077
Reviewed-on: https://chromium-review.googlesource.com/c/1270255Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarFrank Tang <ftang@chromium.org>
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56660}
parent 92d6222f
......@@ -71,8 +71,6 @@ enum ContextLookupFlags {
V(ARRAY_FOR_EACH_ITERATOR_INDEX, JSFunction, array_for_each_iterator) \
V(ARRAY_KEYS_ITERATOR_INDEX, JSFunction, array_keys_iterator) \
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
V(CANONICALIZE_LOCALE_LIST_FUNCTION_INDEX, JSFunction, \
canonicalize_locale_list) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(ERROR_TO_STRING, JSFunction, error_to_string) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
......@@ -91,7 +89,6 @@ enum ContextLookupFlags {
V(REFERENCE_ERROR_FUNCTION_INDEX, JSFunction, reference_error_function) \
V(CACHED_OR_NEW_SERVICE_LOCALE_FUNCTION_INDEX, JSFunction, \
cached_or_new_service) \
V(RESOLVE_LOCALE_FUNCTION_INDEX, JSFunction, resolve_locale) \
V(SET_ADD_INDEX, JSFunction, set_add) \
V(SET_DELETE_INDEX, JSFunction, set_delete) \
V(SET_HAS_INDEX, JSFunction, set_has) \
......
......@@ -17,29 +17,16 @@
// -------------------------------------------------------------------
// Imports
var ArrayJoin;
var ArrayPush;
var GlobalDate = global.Date;
var GlobalIntl = global.Intl;
var GlobalIntlDateTimeFormat = GlobalIntl.DateTimeFormat;
var GlobalIntlNumberFormat = GlobalIntl.NumberFormat;
var GlobalIntlCollator = GlobalIntl.Collator;
var GlobalIntlPluralRules = GlobalIntl.PluralRules;
var GlobalIntlv8BreakIterator = GlobalIntl.v8BreakIterator;
var GlobalRegExp = global.RegExp;
var GlobalString = global.String;
var GlobalArray = global.Array;
var IntlFallbackSymbol = utils.ImportNow("intl_fallback_symbol");
var InternalArray = utils.InternalArray;
var MathMax = global.Math.max;
var ObjectHasOwnProperty = global.Object.prototype.hasOwnProperty;
var ObjectKeys = global.Object.keys;
var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
var StringSubstr = GlobalString.prototype.substr;
var StringSubstring = GlobalString.prototype.substring;
utils.Import(function(from) {
ArrayJoin = from.ArrayJoin;
ArrayPush = from.ArrayPush;
});
......@@ -60,239 +47,6 @@ var AVAILABLE_LOCALES = {
'segmenter': UNDEFINED,
};
/**
* Unicode extension regular expression.
*/
var UNICODE_EXTENSION_RE = UNDEFINED;
function GetUnicodeExtensionRE() {
if (IS_UNDEFINED(UNDEFINED)) {
UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g');
}
return UNICODE_EXTENSION_RE;
}
/**
* Matches any Unicode extension.
*/
var ANY_EXTENSION_RE = UNDEFINED;
function GetAnyExtensionRE() {
if (IS_UNDEFINED(ANY_EXTENSION_RE)) {
ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g');
}
return ANY_EXTENSION_RE;
}
/**
* Matches valid service name.
*/
var SERVICE_RE = UNDEFINED;
function GetServiceRE() {
if (IS_UNDEFINED(SERVICE_RE)) {
SERVICE_RE =
new GlobalRegExp('^(' + %_Call(ArrayJoin, ObjectKeys(AVAILABLE_LOCALES), '|') + ')$');
}
return SERVICE_RE;
}
/**
* Returns a getOption function that extracts property value for given
* options object. If property is missing it returns defaultValue. If value
* is out of range for that property it throws RangeError.
*/
function getGetOption(options, caller) {
if (IS_UNDEFINED(options)) throw %make_error(kDefaultOptionsMissing, caller);
// Ecma 402 #sec-getoption
var getOption = function (property, type, values, fallback) {
// 1. Let value be ? Get(options, property).
var value = options[property];
// 2. If value is not undefined, then
if (!IS_UNDEFINED(value)) {
switch (type) {
// If type is "boolean", then let value be ToBoolean(value).
case 'boolean':
value = TO_BOOLEAN(value);
break;
// If type is "string", then let value be ToString(value).
case 'string':
value = TO_STRING(value);
break;
// Assert: type is "boolean" or "string".
default:
throw %make_error(kWrongValueType);
}
// d. If values is not undefined, then
// If values does not contain an element equal to value, throw a
// RangeError exception.
if (!IS_UNDEFINED(values) && %ArrayIndexOf(values, value, 0) === -1) {
throw %make_range_error(kValueOutOfRange, value, caller, property);
}
return value;
}
return fallback;
}
return getOption;
}
/**
* Ecma 402 9.2.5
* TODO(jshin): relevantExtensionKeys and localeData need to be taken into
* account per spec.
* Compares a BCP 47 language priority list requestedLocales against the locales
* in availableLocales and determines the best available language to meet the
* request. Two algorithms are available to match the locales: the Lookup
* algorithm described in RFC 4647 section 3.4, and an implementation dependent
* best-fit algorithm. Independent of the locale matching algorithm, options
* specified through Unicode locale extension sequences are negotiated
* separately, taking the caller's relevant extension keys and locale data as
* well as client-provided options into consideration. Returns an object with
* a locale property whose value is the language tag of the selected locale,
* and properties for each key in relevantExtensionKeys providing the selected
* value for that key.
*/
function resolveLocale(service, requestedLocales, options) {
requestedLocales = initializeLocaleList(requestedLocales);
var getOption = getGetOption(options, service);
var matcher = getOption('localeMatcher', 'string',
['lookup', 'best fit'], 'best fit');
var resolved;
if (matcher === 'lookup') {
resolved = lookupMatcher(service, requestedLocales);
} else {
resolved = bestFitMatcher(service, requestedLocales);
}
return resolved;
}
%InstallToContext([
"resolve_locale", resolveLocale
]);
/**
* Look up the longest non-empty prefix of |locale| that is an element of
* |availableLocales|. Returns undefined when the |locale| is completely
* unsupported by |availableLocales|.
*/
function bestAvailableLocale(availableLocales, locale) {
do {
if (!IS_UNDEFINED(availableLocales[locale])) {
return locale;
}
// Truncate locale if possible.
var pos = %StringLastIndexOf(locale, '-');
if (pos === -1) {
break;
}
locale = %_Call(StringSubstring, locale, 0, pos);
} while (true);
return UNDEFINED;
}
/**
* Try to match any mutation of |requestedLocale| against |availableLocales|.
*/
function attemptSingleLookup(availableLocales, requestedLocale) {
// Remove all extensions.
var noExtensionsLocale = %RegExpInternalReplace(
GetAnyExtensionRE(), requestedLocale, '');
var availableLocale = bestAvailableLocale(
availableLocales, requestedLocale);
if (!IS_UNDEFINED(availableLocale)) {
// Return the resolved locale and extension.
var extensionMatch = %regexp_internal_match(
GetUnicodeExtensionRE(), requestedLocale);
var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
return {
__proto__: null,
locale: availableLocale,
extension: extension,
localeWithExtension: availableLocale + extension,
};
}
return UNDEFINED;
}
/**
* Returns best matched supported locale and extension info using basic
* lookup algorithm.
*/
function lookupMatcher(service, requestedLocales) {
if (IS_NULL(%regexp_internal_match(GetServiceRE(), service))) {
throw %make_error(kWrongServiceType, service);
}
var availableLocales = getAvailableLocalesOf(service);
for (var i = 0; i < requestedLocales.length; ++i) {
var result = attemptSingleLookup(availableLocales, requestedLocales[i]);
if (!IS_UNDEFINED(result)) {
return result;
}
}
var defLocale = %GetDefaultICULocale();
// While ECMA-402 returns defLocale directly, we have to check if it is
// supported, as such support is not guaranteed.
var result = attemptSingleLookup(availableLocales, defLocale);
if (!IS_UNDEFINED(result)) {
return result;
}
// Didn't find a match, return default.
return {
__proto__: null,
locale: 'und',
extension: '',
localeWithExtension: 'und',
};
}
/**
* Returns best matched supported locale and extension info using
* implementation dependend algorithm.
*/
function bestFitMatcher(service, requestedLocales) {
// TODO(cira): implement better best fit algorithm.
return lookupMatcher(service, requestedLocales);
}
/**
* Given an array-like, outputs an Array with the numbered
* properties copied over and defined
* configurable: false, writable: false, enumerable: true.
* When |expandable| is true, the result array can be expanded.
*/
function freezeArray(input) {
var array = [];
var l = input.length;
for (var i = 0; i < l; i++) {
if (i in input) {
%object_define_property(array, i, {value: input[i],
configurable: false,
writable: false,
enumerable: true});
}
}
%object_define_property(array, 'length', {value: l, writable: false});
return array;
}
/* Make JS array[] out of InternalArray */
function makeArray(input) {
var array = [];
......@@ -300,37 +54,6 @@ function makeArray(input) {
return array;
}
/**
* Returns an Object that contains all of supported locales for a given
* service.
* In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
* that is supported. This is required by the spec.
*/
function getAvailableLocalesOf(service) {
// Cache these, they don't ever change per service.
if (!IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
return AVAILABLE_LOCALES[service];
}
var available = %AvailableLocalesOf(service);
for (var i in available) {
if (HAS_OWN_PROPERTY(available, i)) {
var parts = %regexp_internal_match(
/^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/, i);
if (!IS_NULL(parts)) {
// Build xx-ZZ. We don't care about the actual value,
// as long it's not undefined.
available[parts[1] + '-' + parts[3]] = null;
}
}
}
AVAILABLE_LOCALES[service] = available;
return available;
}
/**
* Returns an InternalArray where all locales are canonicalized and duplicates
* removed.
......@@ -365,18 +88,6 @@ function canonicalizeLocaleList(locales) {
return seen;
}
// TODO(ftang): remove the %InstallToContext once
// initializeLocaleList is available in C++
// https://bugs.chromium.org/p/v8/issues/detail?id=7987
%InstallToContext([
"canonicalize_locale_list", canonicalizeLocaleList
]);
function initializeLocaleList(locales) {
return freezeArray(canonicalizeLocaleList(locales));
}
// ECMA 402 section 8.2.1
DEFINE_METHOD(
GlobalIntl,
......
This diff is collapsed.
......@@ -82,34 +82,6 @@ class Intl {
static std::string DefaultLocale(Isolate* isolate);
// The ResolveLocale abstract operation compares a BCP 47 language
// priority list requestedLocales against the locales in
// availableLocales and determines the best available language to
// meet the request. availableLocales, requestedLocales, and
// relevantExtensionKeys must be provided as List values, options
// and localeData as Records.
//
// #ecma402/sec-partitiondatetimepattern
//
// Returns a JSObject with two properties:
// (1) locale
// (2) extension
//
// To access either, use JSObject::GetDataProperty.
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ResolveLocale(
Isolate* isolate, const char* service, Handle<Object> requestedLocales,
Handle<Object> options);
// This currently calls out to the JavaScript implementation of
// CanonicalizeLocaleList.
// Note: This is deprecated glue code, required only as long as ResolveLocale
// still calls a JS implementation. The C++ successor is the overloaded
// version below that returns a Maybe<std::vector<std::string>>.
//
// ecma402/#sec-canonicalizelocalelist
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CanonicalizeLocaleListJS(
Isolate* isolate, Handle<Object> locales);
// ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
// ecma402/#sec-getoption
//
......@@ -186,8 +158,9 @@ class Intl {
Isolate* isolate, icu::DecimalFormat* number_format,
Handle<JSReceiver> options, int mnfd_default, int mxfd_default);
icu::Locale static CreateICULocale(Isolate* isolate,
static icu::Locale CreateICULocale(Isolate* isolate,
Handle<String> bcp47_locale_str);
static icu::Locale CreateICULocale(const std::string& bcp47_locale);
// Helper funciton to convert a UnicodeString to a Handle<String>
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString(
......@@ -214,10 +187,6 @@ class Intl {
Handle<String> additional_property_name,
Handle<String> additional_property_value);
// A helper function to help handle Unicode Extensions in locale.
static std::map<std::string, std::string> LookupUnicodeExtensions(
const icu::Locale& icu_locale, const std::set<std::string>& relevant_keys);
// In ECMA 402 v1, Intl constructors supported a mode of operation
// where calling them with an existing object as a receiver would
// transform the receiver into the relevant Intl instance with all
......@@ -236,6 +205,19 @@ class Intl {
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CachedOrNewService(
Isolate* isolate, Handle<String> service, Handle<Object> locales,
Handle<Object> options, Handle<Object> internal_options);
enum MatcherOption { kBestFit, kLookup };
struct ResolvedLocale {
std::string locale;
icu::Locale icu_locale;
std::map<std::string, std::string> extensions;
};
static ResolvedLocale ResolveLocale(
Isolate* isolate, const std::set<std::string>& available_locales,
const std::vector<std::string>& requested_locales, MatcherOption options,
const std::set<std::string>& relevant_extension_keys);
};
} // namespace internal
......
......@@ -29,6 +29,13 @@ MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::Initialize(
Handle<Object> locales, Handle<Object> options_obj) {
Factory* factory = isolate->factory();
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, MaybeHandle<JSV8BreakIterator>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
Handle<JSReceiver> options;
if (options_obj->IsUndefined(isolate)) {
options = factory->NewJSObjectWithNullProto();
......@@ -40,15 +47,23 @@ MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::Initialize(
}
// Extract locale string
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "breakiterator", locales, options),
JSV8BreakIterator);
Handle<Object> locale_obj =
JSObject::GetDataProperty(r, factory->locale_string());
CHECK(locale_obj->IsString());
Handle<String> locale = Handle<String>::cast(locale_obj);
const std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.JSV8BreakIterator", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSV8BreakIterator>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kBreakIterator);
Intl::ResolvedLocale r = Intl::ResolveLocale(isolate, available_locales,
requested_locales, matcher, {});
// Extract type from options
std::unique_ptr<char[]> type_str = nullptr;
......@@ -63,8 +78,7 @@ MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::Initialize(
type_enum = getType(type_str.get());
}
// Construct icu_locale using the locale string
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
// Construct break_iterator using icu_locale and type
......@@ -103,8 +117,10 @@ MaybeHandle<JSV8BreakIterator> JSV8BreakIterator::Initialize(
Handle<Managed<icu::UnicodeString>> managed_unicode_string =
Managed<icu::UnicodeString>::FromRawPtr(isolate, 0, nullptr);
// Setting fields
break_iterator_holder->set_locale(*locale);
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
break_iterator_holder->set_locale(*locale_str);
break_iterator_holder->set_type(type_enum);
break_iterator_holder->set_break_iterator(*managed_break_iterator);
break_iterator_holder->set_unicode_string(*managed_unicode_string);
......
......@@ -222,10 +222,11 @@ MaybeHandle<JSCollator> JSCollator::Initialize(Isolate* isolate,
Handle<Object> locales,
Handle<Object> options_obj) {
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Handle<JSObject> requested_locales;
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales,
Intl::CanonicalizeLocaleListJS(isolate, locales),
JSCollator);
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSCollator>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 2. If options is undefined, then
if (options_obj->IsUndefined(isolate)) {
......@@ -258,13 +259,21 @@ MaybeHandle<JSCollator> JSCollator::Initialize(Isolate* isolate,
}
}
// TODO(gsathya): This is currently done as part of the
// Intl::ResolveLocale call below. Fix this once resolveLocale is
// changed to not do the lookup.
//
// 9. Let matcher be ? GetOption(options, "localeMatcher", "string",
// « "lookup", "best fit" », "best fit").
// 10. Set opt.[[localeMatcher]] to matcher.
values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher = Intl::GetStringOption(
isolate, options, "localeMatcher", values, "Intl.Collator", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSCollator>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 11. Let numeric be ? GetOption(options, "numeric", "boolean",
// undefined, undefined).
......@@ -296,48 +305,20 @@ MaybeHandle<JSCollator> JSCollator::Initialize(Isolate* isolate,
// 16. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
std::set<std::string> relevant_extension_keys{"co", "kn", "kf"};
// We don't pass the relevant_extension_keys to ResolveLocale here
// as per the spec.
//
// In ResolveLocale, the spec makes sure we only pick and use the
// relevant extension keys and ignore any other keys. Also, in
// ResolveLocale, the spec makes sure that if a given key has both a
// value in the options object and an unicode extension value, then
// we pick the value provided in the options object.
// For example: in the case of `new Intl.Collator('en-u-kn-true', {
// numeric: false })` the value `false` is used for the `numeric`
// key.
//
// Instead of performing all this validation in ResolveLocale, we
// just perform it inline below. In the future when we port
// ResolveLocale to C++, we can make all these validations generic
// and move it ResolveLocale.
//
// 17. Let r be ResolveLocale(%Collator%.[[AvailableLocales]],
// requestedLocales, opt, %Collator%.[[RelevantExtensionKeys]],
// localeData).
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kCollator);
Intl::ResolvedLocale r =
Intl::ResolveLocale(isolate, available_locales, requested_locales,
matcher, relevant_extension_keys);
// 18. Set collator.[[Locale]] to r.[[locale]].
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "collator", requested_locales, options),
JSCollator);
Handle<Object> locale_with_extension_obj = JSObject::GetDataProperty(
r, isolate->factory()->localeWithExtension_string());
// The locale_with_extension has to be a string. Either a user
// provided canonicalized string or the default locale.
CHECK(locale_with_extension_obj->IsString());
Handle<String> locale_with_extension =
Handle<String>::cast(locale_with_extension_obj);
icu::Locale icu_locale =
Intl::CreateICULocale(isolate, locale_with_extension);
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
std::map<std::string, std::string> extensions =
Intl::LookupUnicodeExtensions(icu_locale, relevant_extension_keys);
std::map<std::string, std::string> extensions = r.extensions;
// 19. Let collation be r.[[co]].
//
......
......@@ -720,7 +720,14 @@ enum FormatMatcherOption { kBestFit, kBasic };
// ecma402/#sec-initializedatetimeformat
MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
Isolate* isolate, Handle<JSDateTimeFormat> date_time_format,
Handle<Object> requested_locales, Handle<Object> input_options) {
Handle<Object> locales, Handle<Object> input_options) {
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSDateTimeFormat>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 2. Let options be ? ToDateTimeOptions(options, "any", "date").
Handle<JSObject> options;
ASSIGN_RETURN_ON_EXCEPTION(
......@@ -729,17 +736,22 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
isolate, input_options, RequiredOption::kAny, DefaultsOption::kDate),
JSDateTimeFormat);
// ResolveLocale currently get option of localeMatcher so we have to call
// ResolveLocale before "hour12" and "hourCycle".
// TODO(ftang): fix this once ResolveLocale is ported to C++
// 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
// localeData).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "dateformat", requested_locales, options),
JSDateTimeFormat);
// 4. Let matcher be ? GetOption(options, "localeMatcher", "string",
// « "lookup", "best fit" », "best fit").
// 5. Set opt.[[localeMatcher]] to matcher.
std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> locale_matcher_str = nullptr;
Intl::MatcherOption locale_matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_locale_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.DateTimeFormat", &locale_matcher_str);
MAYBE_RETURN(found_locale_matcher, MaybeHandle<JSDateTimeFormat>());
if (found_locale_matcher.FromJust()) {
DCHECK_NOT_NULL(locale_matcher_str.get());
if (strcmp(locale_matcher_str.get(), "lookup") == 0) {
locale_matcher = Intl::MatcherOption::kLookup;
}
}
// 6. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined,
// undefined).
......@@ -768,17 +780,18 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
// 9. Set opt.[[hc]] to hourCycle.
// TODO(ftang): change behavior based on hour_cycle.
Handle<Object> locale_with_extension_obj = JSObject::GetDataProperty(
r, isolate->factory()->localeWithExtension_string());
// The locale_with_extension has to be a string. Either a user
// provided canonicalized string or the default locale.
CHECK(locale_with_extension_obj->IsString());
Handle<String> locale_with_extension =
Handle<String>::cast(locale_with_extension_obj);
icu::Locale icu_locale =
Intl::CreateICULocale(isolate, locale_with_extension);
// 10. Let localeData be %DateTimeFormat%.[[LocaleData]].
// 11. Let r be ResolveLocale( %DateTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]],
// localeData).
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kDateFormat);
Intl::ResolvedLocale r = Intl::ResolveLocale(
isolate, available_locales, requested_locales, locale_matcher, {"nu"});
// TODO(ftang): Make sure that "nu" key doesn't have "native",
// "traditio" or "finance" values.
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
// 17. Let timeZone be ? Get(options, "timeZone").
......
......@@ -121,24 +121,33 @@ JSListFormat::Type get_type(const char* str) {
MaybeHandle<JSListFormat> JSListFormat::Initialize(
Isolate* isolate, Handle<JSListFormat> list_format_holder,
Handle<Object> input_locales, Handle<Object> input_options) {
Factory* factory = isolate->factory();
Handle<Object> locales, Handle<Object> input_options) {
list_format_holder->set_flags(0);
Handle<JSReceiver> options;
// 2. If options is undefined, then
// 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSListFormat>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 4. If options is undefined, then
if (input_options->IsUndefined(isolate)) {
// a. Let options be ObjectCreate(null).
// 4. a. Let options be ObjectCreate(null).
options = isolate->factory()->NewJSObjectWithNullProto();
// 3. Else
// 5. Else
} else {
// a. Let options be ? ToObject(options).
// 5. a. Let options be ? ToObject(options).
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, input_options),
JSListFormat);
}
// 5. Let t be GetOption(options, "type", "string", «"conjunction",
// Note: No need to create a record. It's not observable.
// 6. Let opt be a new Record.
// 7. Let t be GetOption(options, "type", "string", «"conjunction",
// "disjunction", "unit"», "conjunction").
std::unique_ptr<char[]> type_str = nullptr;
std::vector<const char*> type_values = {"conjunction", "disjunction", "unit"};
......@@ -150,10 +159,11 @@ MaybeHandle<JSListFormat> JSListFormat::Initialize(
DCHECK_NOT_NULL(type_str.get());
type_enum = get_type(type_str.get());
}
// 6. Set listFormat.[[Type]] to t.
// 8. Set listFormat.[[Type]] to t.
list_format_holder->set_type(type_enum);
// 7. Let s be ? GetOption(options, "style", "string",
// 9. Let s be ? GetOption(options, "style", "string",
// «"long", "short", "narrow"», "long").
std::unique_ptr<char[]> style_str = nullptr;
std::vector<const char*> style_values = {"long", "short", "narrow"};
......@@ -165,28 +175,39 @@ MaybeHandle<JSListFormat> JSListFormat::Initialize(
DCHECK_NOT_NULL(style_str.get());
style_enum = get_style(style_str.get());
}
// 15. Set listFormat.[[Style]] to s.
// 10. Set listFormat.[[Style]] to s.
list_format_holder->set_style(style_enum);
// 10. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]],
// TODO(ftang): There's no spec text for this yet.
// Tracking issue: https://github.com/tc39/proposal-intl-list-format/issues/24
const std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.ListFormat", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSListFormat>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 11. Let localeData be %ListFormat%.[[LocaleData]].
// 12. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]],
// requestedLocales, opt, undefined, localeData).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "listformat", input_locales, options),
JSListFormat);
Handle<Object> locale_obj =
JSObject::GetDataProperty(r, factory->locale_string());
Handle<String> locale;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, locale, Object::ToString(isolate, locale_obj), JSListFormat);
// 18. Set listFormat.[[Locale]] to the value of r.[[Locale]].
list_format_holder->set_locale(*locale);
std::unique_ptr<char[]> locale_name = locale->ToCString();
icu::Locale icu_locale(locale_name.get());
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kListFormatter);
Intl::ResolvedLocale r = Intl::ResolveLocale(isolate, available_locales,
requested_locales, matcher, {});
// 21. Set listFormat.[[Locale]] to r.[[Locale]].
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
list_format_holder->set_locale(*locale_str);
icu::Locale icu_locale = r.icu_locale;
UErrorCode status = U_ZERO_ERROR;
icu::ListFormatter* formatter = icu::ListFormatter::createInstance(
icu_locale, GetIcuStyleString(style_enum, type_enum), status);
......
......@@ -188,11 +188,13 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
// set the flags to 0 ASAP.
number_format->set_flags(0);
Factory* factory = isolate->factory();
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Handle<JSObject> requested_locales;
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales,
Intl::CanonicalizeLocaleListJS(isolate, locales),
JSNumberFormat);
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSNumberFormat>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 2. If options is undefined, then
if (options_obj->IsUndefined(isolate)) {
......@@ -211,43 +213,43 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
// 4. Let opt be a new Record.
//
// 5. Let matcher be ? GetOption(options, "localeMatcher", "string", «
// "lookup", "best fit" », "best fit").
//
// 6. Set opt.[[localeMatcher]] to matcher.
//
const std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.NumberFormat", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSNumberFormat>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 7. Let localeData be %NumberFormat%.[[LocaleData]].
//
// 8. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]],
// requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]],
// localeData).
//
// 9. Set numberFormat.[[Locale]] to r.[[locale]].
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "numberformat", requested_locales, options),
JSNumberFormat);
Handle<Object> locale_with_extension_obj = JSObject::GetDataProperty(
r, isolate->factory()->localeWithExtension_string());
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kNumberFormat);
std::set<std::string> relevant_extension_keys{"nu"};
Intl::ResolvedLocale r =
Intl::ResolveLocale(isolate, available_locales, requested_locales,
matcher, relevant_extension_keys);
// The locale_with_extension has to be a string. Either a user
// provided canonicalized string or the default locale.
CHECK(locale_with_extension_obj->IsString());
Handle<String> locale_with_extension =
Handle<String>::cast(locale_with_extension_obj);
// 9. Set numberFormat.[[Locale]] to r.[[locale]].
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
number_format->set_locale(*locale_str);
icu::Locale icu_locale =
Intl::CreateICULocale(isolate, locale_with_extension);
number_format->set_locale(*locale_with_extension);
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
std::set<std::string> relevant_extension_keys{"nu"};
std::map<std::string, std::string> extensions =
Intl::LookupUnicodeExtensions(icu_locale, relevant_extension_keys);
std::map<std::string, std::string> extensions = r.extensions;
// The list that is the value of the "nu" field of any locale field of
// [[LocaleData]] must not include the values "native", "traditio", or
......
......@@ -59,12 +59,9 @@ bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
}
void InitializeICUPluralRules(
Isolate* isolate, Handle<String> locale, const char* type,
Isolate* isolate, const icu::Locale& icu_locale, const char* type,
std::unique_ptr<icu::PluralRules>* plural_rules,
std::unique_ptr<icu::DecimalFormat>* number_format) {
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
DCHECK(!icu_locale.isBogus());
bool success = CreateICUPluralRules(isolate, icu_locale, type, plural_rules,
number_format);
if (!success) {
......@@ -89,12 +86,11 @@ MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
Isolate* isolate, Handle<JSPluralRules> plural_rules,
Handle<Object> locales, Handle<Object> options_obj) {
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
// TODO(jkummerow): Port ResolveLocale, then use the C++ version of
// CanonicalizeLocaleList here.
Handle<JSObject> requested_locales;
ASSIGN_RETURN_ON_EXCEPTION(isolate, requested_locales,
Intl::CanonicalizeLocaleListJS(isolate, locales),
JSPluralRules);
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSPluralRules>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 2. If options is undefined, then
if (options_obj->IsUndefined(isolate)) {
......@@ -112,17 +108,26 @@ MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
// At this point, options_obj can either be a JSObject or a JSProxy only.
Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
// TODO(gsathya): This is currently done as part of the
// Intl::ResolveLocale call below. Fix this once resolveLocale is
// changed to not do the lookup.
//
// 5. Let matcher be ? GetOption(options, "localeMatcher", "string",
// « "lookup", "best fit" », "best fit").
// 6. Set opt.[[localeMatcher]] to matcher.
std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.PluralRules", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSPluralRules>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 7. Let t be ? GetOption(options, "type", "string", « "cardinal",
// "ordinal" », "cardinal").
std::vector<const char*> values = {"cardinal", "ordinal"};
values = {"cardinal", "ordinal"};
std::unique_ptr<char[]> type_str = nullptr;
const char* type_cstr = "cardinal";
Maybe<bool> found = Intl::GetStringOption(isolate, options, "type", values,
......@@ -146,26 +151,25 @@ MaybeHandle<JSPluralRules> JSPluralRules::Initialize(
// 11. Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]],
// requestedLocales, opt, %PluralRules%.[[RelevantExtensionKeys]],
// localeData).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "pluralrules", requested_locales, options),
JSPluralRules);
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kPluralRules);
Intl::ResolvedLocale r = Intl::ResolveLocale(isolate, available_locales,
requested_locales, matcher, {});
Handle<String> locale_str = isolate->factory()->locale_string();
Handle<Object> locale_obj = JSObject::GetDataProperty(r, locale_str);
// 18. Set collator.[[Locale]] to r.[[locale]].
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
// The locale has to be a string. Either a user provided
// canonicalized string or the default locale.
CHECK(locale_obj->IsString());
Handle<String> locale = Handle<String>::cast(locale_obj);
std::map<std::string, std::string> extensions = r.extensions;
// 12. Set pluralRules.[[Locale]] to the value of r.[[locale]].
plural_rules->set_locale(*locale);
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
plural_rules->set_locale(*locale_str);
std::unique_ptr<icu::PluralRules> icu_plural_rules;
std::unique_ptr<icu::DecimalFormat> icu_decimal_format;
InitializeICUPluralRules(isolate, locale, type_cstr, &icu_plural_rules,
InitializeICUPluralRules(isolate, icu_locale, type_cstr, &icu_plural_rules,
&icu_decimal_format);
CHECK_NOT_NULL(icu_plural_rules.get());
CHECK_NOT_NULL(icu_decimal_format.get());
......
......@@ -56,43 +56,65 @@ JSRelativeTimeFormat::Numeric JSRelativeTimeFormat::getNumeric(
MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
Isolate* isolate, Handle<JSRelativeTimeFormat> relative_time_format_holder,
Handle<Object> input_locales, Handle<Object> input_options) {
Factory* factory = isolate->factory();
Handle<Object> locales, Handle<Object> input_options) {
relative_time_format_holder->set_flags(0);
// 4. If options is undefined, then
// 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSRelativeTimeFormat>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 2. If options is undefined, then
Handle<JSReceiver> options;
if (input_options->IsUndefined(isolate)) {
// a. Let options be ObjectCreate(null).
// 2. a. Let options be ObjectCreate(null).
options = isolate->factory()->NewJSObjectWithNullProto();
// 5. Else
// 3. Else
} else {
// a. Let options be ? ToObject(options).
// 3. a. Let options be ? ToObject(options).
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, input_options),
JSRelativeTimeFormat);
}
// 10. Let r be ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt,
// %RelativeTimeFormat%.[[RelevantExtensionKeys]],
// localeData).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(isolate, r,
Intl::ResolveLocale(isolate, "relativetimeformat",
input_locales, options),
JSRelativeTimeFormat);
Handle<Object> locale_obj =
JSObject::GetDataProperty(r, factory->locale_string());
Handle<String> locale;
ASSIGN_RETURN_ON_EXCEPTION(isolate, locale,
Object::ToString(isolate, locale_obj),
JSRelativeTimeFormat);
// 11. Let locale be r.[[Locale]].
// 12. Set relativeTimeFormat.[[Locale]] to locale.
relative_time_format_holder->set_locale(*locale);
// 14. Let s be ? GetOption(options, "style", "string",
// 4. Let opt be a new Record.
// 5. Let matcher be ? GetOption(options, "localeMatcher", "string", «
// "lookup", "best fit" », "best fit").
// 6. Set opt.[[localeMatcher]] to matcher.
const std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.RelativeTimeFormat", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSRelativeTimeFormat>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 7. Let localeData be %RelativeTimeFormat%.[[LocaleData]].
// 8. Let r be
// ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt,
// %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData).
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kRelativeDateTimeFormatter);
Intl::ResolvedLocale r = Intl::ResolveLocale(isolate, available_locales,
requested_locales, matcher, {});
// 9. Let locale be r.[[Locale]].
// 10. Set relativeTimeFormat.[[Locale]] to locale.
// 11. Let dataLocale be r.[[DataLocale]].
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
relative_time_format_holder->set_locale(*locale_str);
// 12. Let s be ? GetOption(options, "style", "string",
// «"long", "short", "narrow"», "long").
std::unique_ptr<char[]> style_str = nullptr;
std::vector<const char*> style_values = {"long", "short", "narrow"};
......@@ -106,10 +128,10 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
style_enum = getStyle(style_str.get());
}
// 15. Set relativeTimeFormat.[[Style]] to s.
// 13. Set relativeTimeFormat.[[Style]] to s.
relative_time_format_holder->set_style(style_enum);
// 16. Let numeric be ? GetOption(options, "numeric", "string",
// 14. Let numeric be ? GetOption(options, "numeric", "string",
// «"always", "auto"», "always").
std::unique_ptr<char[]> numeric_str = nullptr;
std::vector<const char*> numeric_values = {"always", "auto"};
......@@ -123,14 +145,13 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
numeric_enum = getNumeric(numeric_str.get());
}
// 17. Set relativeTimeFormat.[[Numeric]] to numeric.
// 15. Set relativeTimeFormat.[[Numeric]] to numeric.
relative_time_format_holder->set_numeric(numeric_enum);
std::unique_ptr<char[]> locale_name = locale->ToCString();
icu::Locale icu_locale(locale_name.get());
icu::Locale icu_locale = r.icu_locale;
UErrorCode status = U_ZERO_ERROR;
// 25. Let relativeTimeFormat.[[NumberFormat]] be
// 19. Let relativeTimeFormat.[[NumberFormat]] be
// ? Construct(%NumberFormat%, « nfLocale, nfOptions »).
icu::NumberFormat* number_format =
icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status);
......@@ -159,9 +180,10 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
Managed<icu::RelativeDateTimeFormatter>::FromRawPtr(isolate, 0,
icu_formatter);
// 30. Set relativeTimeFormat.[[InitializedRelativeTimeFormat]] to true.
// 21. Set relativeTimeFormat.[[InitializedRelativeTimeFormat]] to true.
relative_time_format_holder->set_icu_formatter(*managed_formatter);
// 31. Return relativeTimeFormat.
// 22. Return relativeTimeFormat.
return relative_time_format_holder;
}
......
......@@ -40,44 +40,55 @@ JSSegmenter::Granularity JSSegmenter::GetGranularity(const char* str) {
MaybeHandle<JSSegmenter> JSSegmenter::Initialize(
Isolate* isolate, Handle<JSSegmenter> segmenter_holder,
Handle<Object> input_locales, Handle<Object> input_options) {
Factory* factory = isolate->factory();
Handle<Object> locales, Handle<Object> input_options) {
segmenter_holder->set_flags(0);
// 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
Handle<JSObject> requested_locales;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, requested_locales,
Intl::CanonicalizeLocaleListJS(isolate, input_locales), JSSegmenter);
Maybe<std::vector<std::string>> maybe_requested_locales =
Intl::CanonicalizeLocaleList(isolate, locales);
MAYBE_RETURN(maybe_requested_locales, Handle<JSSegmenter>());
std::vector<std::string> requested_locales =
maybe_requested_locales.FromJust();
// 11. If options is undefined, then
Handle<JSReceiver> options;
if (input_options->IsUndefined(isolate)) {
// a. Let options be ObjectCreate(null).
// 11. a. Let options be ObjectCreate(null).
options = isolate->factory()->NewJSObjectWithNullProto();
// 12. Else
} else {
// a. Let options be ? ToObject(options).
// 23. a. Let options be ? ToObject(options).
ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
Object::ToObject(isolate, input_options),
JSSegmenter);
}
// 4. Let opt be a new Record.
// 5. Let matcher be ? GetOption(options, "localeMatcher", "string",
// « "lookup", "best fit" », "best fit").
// 6. Set opt.[[localeMatcher]] to matcher.
const std::vector<const char*> values = {"lookup", "best fit"};
std::unique_ptr<char[]> matcher_str = nullptr;
Intl::MatcherOption matcher = Intl::MatcherOption::kBestFit;
Maybe<bool> found_matcher =
Intl::GetStringOption(isolate, options, "localeMatcher", values,
"Intl.Segmenter", &matcher_str);
MAYBE_RETURN(found_matcher, MaybeHandle<JSSegmenter>());
if (found_matcher.FromJust()) {
DCHECK_NOT_NULL(matcher_str.get());
if (strcmp(matcher_str.get(), "lookup") == 0) {
matcher = Intl::MatcherOption::kLookup;
}
}
// 8. Set opt.[[lb]] to lineBreakStyle.
// Because currently we access localeMatcher inside ResolveLocale, we have to
// move ResolveLocale before get lineBreakStyle
// 9. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]],
// requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]]).
Handle<JSObject> r;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, r,
Intl::ResolveLocale(isolate, "segmenter", requested_locales, options),
JSSegmenter);
Handle<Object> locale_obj =
JSObject::GetDataProperty(r, factory->locale_string());
Handle<String> locale;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, locale, Object::ToString(isolate, locale_obj), JSSegmenter);
std::set<std::string> available_locales =
Intl::GetAvailableLocales(ICUService::kSegmenter);
Intl::ResolvedLocale r = Intl::ResolveLocale(isolate, available_locales,
requested_locales, matcher, {});
// 7. Let lineBreakStyle be ? GetOption(options, "lineBreakStyle", "string", «
// "strict", "normal", "loose" », "normal").
......@@ -95,7 +106,9 @@ MaybeHandle<JSSegmenter> JSSegmenter::Initialize(
}
// 10. Set segmenter.[[Locale]] to the value of r.[[Locale]].
segmenter_holder->set_locale(*locale);
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
segmenter_holder->set_locale(*locale_str);
// 13. Let granularity be ? GetOption(options, "granularity", "string", «
// "grapheme", "word", "sentence", "line" », "grapheme").
......@@ -124,7 +137,7 @@ MaybeHandle<JSSegmenter> JSSegmenter::Initialize(
segmenter_holder->set_line_break_style(LineBreakStyle::NOTSET);
}
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
icu::Locale icu_locale = r.icu_locale;
DCHECK(!icu_locale.isBogus());
UErrorCode status = U_ZERO_ERROR;
......
......@@ -550,9 +550,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=7669
'intl402/Intl/getCanonicalLocales/canonicalized-tags': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=8051
'intl402/Collator/unicode-ext-seq-in-private-tag': [FAIL],
# Tests assume that the sort order of "same elements" (comparator returns 0)
# is deterministic.
# https://crbug.com/v8/7808
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment