[intl] NumberFormat v3 Part 4 SelectRange

Implement the Intl.PluralRules.prototype.selectRange (start, end)
of the spec
See https://tc39.es/proposal-intl-numberformat-v3/out/pluralrules/diff.html


https://chromestatus.com/guide/edit/5707621009981440

Design Doc: https://docs.google.com/document/d/19jAogPBb6W4Samt8NWGZKu47iv0_KoQhBvLgQH3xvr8/edit

Bug: v8:10776
Change-Id: Ie9c56df7ce68199492281fdf2483c3d6f822cc9e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3504421
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79495}
This commit is contained in:
Frank Tang 2022-03-04 21:45:04 -08:00 committed by V8 LUCI CQ
parent d782fd1da9
commit 09de56b06e
9 changed files with 137 additions and 21 deletions

View File

@ -1789,6 +1789,8 @@ namespace internal {
CPP(PluralRulesPrototypeResolvedOptions) \
/* ecma402 #sec-intl.pluralrules.prototype.select */ \
CPP(PluralRulesPrototypeSelect) \
/* ecma402 #sec-intl.pluralrules.prototype.selectrange */ \
CPP(PluralRulesPrototypeSelectRange) \
/* ecma402 #sec-intl.pluralrules.supportedlocalesof */ \
CPP(PluralRulesSupportedLocalesOf) \
/* ecma402 #sec-intl.RelativeTimeFormat.constructor */ \

View File

@ -987,6 +987,64 @@ BUILTIN(PluralRulesPrototypeSelect) {
isolate, plural_rules, number_double));
}
BUILTIN(PluralRulesPrototypeSelectRange) {
HandleScope scope(isolate);
// 1. Let pr be the this value.
// 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).
CHECK_RECEIVER(JSPluralRules, plural_rules,
"Intl.PluralRules.prototype.selectRange");
// 3. If start is undefined or end is undefined, throw a TypeError exception.
Handle<Object> start = args.atOrUndefined(isolate, 1);
Handle<Object> end = args.atOrUndefined(isolate, 2);
if (start->IsUndefined()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalid,
isolate->factory()->startRange_string(), start));
}
if (end->IsUndefined()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalid,
isolate->factory()->endRange_string(), end));
}
// 4. Let x be ? ToNumber(start).
Handle<Object> x;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
Object::ToNumber(isolate, start));
// 5. Let y be ? ToNumber(end).
Handle<Object> y;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, y,
Object::ToNumber(isolate, end));
// 6. Return ! ResolvePluralRange(pr, x, y).
// Inside ResolvePluralRange
// 5. If x is NaN or y is NaN, throw a RangeError exception.
if (x->IsNaN()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalid,
isolate->factory()->startRange_string(), x));
}
if (y->IsNaN()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalid,
isolate->factory()->endRange_string(), y));
}
// 6. If x > y, throw a RangeError exception.
double x_double = x->Number();
double y_double = y->Number();
if (x_double > y_double) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalid, x, y));
}
RETURN_RESULT_OR_FAILURE(
isolate, JSPluralRules::ResolvePluralRange(isolate, plural_rules,
x_double, y_double));
}
BUILTIN(PluralRulesSupportedLocalesOf) {
HandleScope scope(isolate);
Handle<Object> locales = args.atOrUndefined(isolate, 1);

View File

@ -5351,6 +5351,7 @@ void Genesis::InitializeGlobal_harmony_intl_number_format_v3() {
factory()->InternalizeUtf8String("Intl"))
.ToHandleChecked());
{
Handle<JSFunction> number_format_constructor = Handle<JSFunction>::cast(
JSReceiver::GetProperty(
isolate(), Handle<JSReceiver>(JSReceiver::cast(*intl), isolate()),
@ -5366,6 +5367,20 @@ void Genesis::InitializeGlobal_harmony_intl_number_format_v3() {
Builtin::kNumberFormatPrototypeFormatRangeToParts, 2,
false);
}
{
Handle<JSFunction> plural_rules_constructor = Handle<JSFunction>::cast(
JSReceiver::GetProperty(
isolate(), Handle<JSReceiver>(JSReceiver::cast(*intl), isolate()),
factory()->InternalizeUtf8String("PluralRules"))
.ToHandleChecked());
Handle<JSObject> prototype(
JSObject::cast(plural_rules_constructor->prototype()), isolate());
SimpleInstallFunction(isolate(), prototype, "selectRange",
Builtin::kPluralRulesPrototypeSelectRange, 2, false);
}
}
#endif // V8_INTL_SUPPORT

View File

@ -28,6 +28,9 @@ ACCESSORS(JSPluralRules, icu_plural_rules, Managed<icu::PluralRules>,
ACCESSORS(JSPluralRules, icu_number_formatter,
Managed<icu::number::LocalizedNumberFormatter>,
kIcuNumberFormatterOffset)
ACCESSORS(JSPluralRules, icu_number_range_formatter,
Managed<icu::number::LocalizedNumberRangeFormatter>,
kIcuNumberRangeFormatterOffset)
inline void JSPluralRules::set_type(Type type) {
DCHECK_LE(type, TypeBit::kMax);

View File

@ -16,6 +16,7 @@
#include "src/objects/option-utils.h"
#include "unicode/locid.h"
#include "unicode/numberformatter.h"
#include "unicode/numberrangeformatter.h"
#include "unicode/plurrule.h"
#include "unicode/unumberformatter.h"
@ -145,6 +146,10 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
icu::number::LocalizedNumberFormatter icu_number_formatter =
settings.locale(icu_locale);
icu::number::LocalizedNumberRangeFormatter icu_number_range_formatter =
icu::number::UnlocalizedNumberRangeFormatter()
.numberFormatterBoth(settings)
.locale(icu_locale);
Handle<Managed<icu::PluralRules>> managed_plural_rules =
Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
@ -155,6 +160,12 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
Managed<icu::number::LocalizedNumberFormatter>::FromRawPtr(
isolate, 0,
new icu::number::LocalizedNumberFormatter(icu_number_formatter));
Handle<Managed<icu::number::LocalizedNumberRangeFormatter>>
managed_number_range_formatter =
Managed<icu::number::LocalizedNumberRangeFormatter>::FromRawPtr(
isolate, 0,
new icu::number::LocalizedNumberRangeFormatter(
icu_number_range_formatter));
// Now all properties are ready, so we can allocate the result object.
Handle<JSPluralRules> plural_rules = Handle<JSPluralRules>::cast(
@ -170,6 +181,7 @@ MaybeHandle<JSPluralRules> JSPluralRules::New(Isolate* isolate, Handle<Map> map,
plural_rules->set_icu_plural_rules(*managed_plural_rules);
plural_rules->set_icu_number_formatter(*managed_number_formatter);
plural_rules->set_icu_number_range_formatter(*managed_number_range_formatter);
// 13. Return pluralRules.
return plural_rules;
@ -196,6 +208,26 @@ MaybeHandle<String> JSPluralRules::ResolvePlural(
return Intl::ToString(isolate, result);
}
MaybeHandle<String> JSPluralRules::ResolvePluralRange(
Isolate* isolate, Handle<JSPluralRules> plural_rules, double x, double y) {
icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules().raw();
DCHECK_NOT_NULL(icu_plural_rules);
icu::number::LocalizedNumberRangeFormatter* fmt =
plural_rules->icu_number_range_formatter().raw();
DCHECK_NOT_NULL(fmt);
UErrorCode status = U_ZERO_ERROR;
icu::number::FormattedNumberRange formatted = fmt->formatFormattableRange(
icu::Formattable(x), icu::Formattable(y), status);
DCHECK(U_SUCCESS(status));
icu::UnicodeString result = icu_plural_rules->select(formatted, status);
DCHECK(U_SUCCESS(status));
return Intl::ToString(isolate, result);
}
namespace {
void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,

View File

@ -26,6 +26,7 @@ namespace U_ICU_NAMESPACE {
class PluralRules;
namespace number {
class LocalizedNumberFormatter;
class LocalizedNumberRangeFormatter;
} // namespace number
} // namespace U_ICU_NAMESPACE
@ -47,6 +48,9 @@ class JSPluralRules
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ResolvePlural(
Isolate* isolate, Handle<JSPluralRules> plural_rules, double number);
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ResolvePluralRange(
Isolate* isolate, Handle<JSPluralRules> plural_rules, double x, double y);
V8_EXPORT_PRIVATE static const std::set<std::string>& GetAvailableLocales();
// [[Type]] is one of the values "cardinal" or "ordinal",
@ -68,6 +72,8 @@ class JSPluralRules
DECL_ACCESSORS(icu_plural_rules, Managed<icu::PluralRules>)
DECL_ACCESSORS(icu_number_formatter,
Managed<icu::number::LocalizedNumberFormatter>)
DECL_ACCESSORS(icu_number_range_formatter,
Managed<icu::number::LocalizedNumberRangeFormatter>)
TQ_OBJECT_CONSTRUCTORS(JSPluralRules)
};

View File

@ -15,4 +15,6 @@ extern class JSPluralRules extends JSObject {
icu_plural_rules: Foreign; // Managed<icu::PluralRules>
icu_number_formatter:
Foreign; // Managed<icu::number::LocalizedNumberFormatter>
icu_number_range_formatter:
Foreign; // Managed<icu::number::LocalizedNumberRangeFormatter>
}

View File

@ -0,0 +1,7 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-intl-number-format-v3
const pl = new Intl.PluralRules("sl");
assertEquals("few", pl.selectRange(102, 201));

View File

@ -2776,15 +2776,6 @@
'intl402/NumberFormat/prototype/formatRange/x-greater-than-y-throws': [FAIL],
'intl402/NumberFormat/prototype/formatRangeToParts/x-greater-than-y-throws': [FAIL],
# PluralRules.prototype.selectRange
'intl402/PluralRules/prototype/selectRange/default-en-us': [FAIL],
'intl402/PluralRules/prototype/selectRange/invoked-as-func': [FAIL],
'intl402/PluralRules/prototype/selectRange/length': [FAIL],
'intl402/PluralRules/prototype/selectRange/name': [FAIL],
'intl402/PluralRules/prototype/selectRange/prop-desc': [FAIL],
'intl402/PluralRules/prototype/selectRange/nan-arguments-throws': [FAIL],
'intl402/PluralRules/prototype/selectRange/x-greater-than-y-throws': [FAIL],
# String handling
'intl402/NumberFormat/prototype/format/format-rounding-increment-1000': [FAIL],
'intl402/NumberFormat/prototype/format/format-rounding-increment-100': [FAIL],