[Intl] Optimize Intl.PluralRules
Previously, Intl.PluralRules was mostly implemented in JavaScript. This patch moves most of the constructor and parts of other methods to C++. The size of the Intl.PluralRules object is reduced by not storing MinimumIntegerDigits, MinimumFractionDigits, MaximumFractionDigits, MinimumSignificantDigits, MaximumSignificantDigits. Instead these are looked up from icu::DecimalFormat as required. Another optimziation is that we don't create the result of resolvedOptions when the Intl.PluralRules object is constructed, but instead defer until this method is called. In the future, we may want to cache the result. This patch also cleans up several error handling paths that shouldn't happen with ICU and instead just crashes should it ever happen. Bug: v8:5751 Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: I84c5aa6c25c35fe2d336693dee1b36bf3dcd4a79 Reviewed-on: https://chromium-review.googlesource.com/1158701 Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Jungshik Shin <jshin@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#54917}
This commit is contained in:
parent
13ed3e38d1
commit
cdb4d913f6
6
BUILD.gn
6
BUILD.gn
@ -2168,6 +2168,9 @@ v8_source_set("v8_base") {
|
||||
"src/objects/js-locale-inl.h",
|
||||
"src/objects/js-locale.cc",
|
||||
"src/objects/js-locale.h",
|
||||
"src/objects/js-plural-rules-inl.h",
|
||||
"src/objects/js-plural-rules.cc",
|
||||
"src/objects/js-plural-rules.h",
|
||||
"src/objects/js-promise-inl.h",
|
||||
"src/objects/js-promise.h",
|
||||
"src/objects/js-proxy-inl.h",
|
||||
@ -2865,6 +2868,9 @@ v8_source_set("v8_base") {
|
||||
"src/objects/js-locale-inl.h",
|
||||
"src/objects/js-locale.cc",
|
||||
"src/objects/js-locale.h",
|
||||
"src/objects/js-plural-rules-inl.h",
|
||||
"src/objects/js-plural-rules.cc",
|
||||
"src/objects/js-plural-rules.h",
|
||||
"src/objects/js-relative-time-format-inl.h",
|
||||
"src/objects/js-relative-time-format.cc",
|
||||
"src/objects/js-relative-time-format.h",
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "src/objects/js-regexp-string-iterator.h"
|
||||
#include "src/objects/js-regexp.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
#include "src/objects/js-relative-time-format.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#include "src/objects/templates.h"
|
||||
@ -2968,10 +2969,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
|
||||
{
|
||||
Handle<JSFunction> plural_rules_constructor = InstallFunction(
|
||||
isolate_, intl, "PluralRules", JS_OBJECT_TYPE, PluralRules::kSize, 0,
|
||||
factory->the_hole_value(), Builtins::kIllegal);
|
||||
native_context()->set_intl_plural_rules_function(
|
||||
*plural_rules_constructor);
|
||||
isolate_, intl, "PluralRules", JS_INTL_PLURAL_RULES_TYPE,
|
||||
JSPluralRules::kSize, 0, factory->the_hole_value(),
|
||||
Builtins::kPluralRulesConstructor);
|
||||
plural_rules_constructor->shared()->DontAdaptArguments();
|
||||
InstallWithIntrinsicDefaultProto(
|
||||
isolate_, plural_rules_constructor,
|
||||
Context::INTL_PLURAL_RULES_FUNCTION_INDEX);
|
||||
|
||||
Handle<JSObject> prototype(
|
||||
JSObject::cast(plural_rules_constructor->prototype()), isolate_);
|
||||
|
@ -1360,6 +1360,8 @@ namespace internal {
|
||||
CPP(NumberFormatInternalFormatNumber) \
|
||||
/* ecma402 #sec-intl.numberformat.prototype.format */ \
|
||||
CPP(NumberFormatPrototypeFormatNumber) \
|
||||
/* ecma402 #sec-intl.pluralrules */ \
|
||||
CPP(PluralRulesConstructor) \
|
||||
/* ecma402 #sec-intl.RelativeTimeFormat.constructor */ \
|
||||
CPP(RelativeTimeFormatConstructor) \
|
||||
/* ecma402 #sec-intl.RelativeTimeFormat.prototype.resolvedOptions */ \
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/js-list-format-inl.h"
|
||||
#include "src/objects/js-locale-inl.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/js-relative-time-format-inl.h"
|
||||
|
||||
#include "unicode/datefmt.h"
|
||||
@ -1095,5 +1096,40 @@ BUILTIN(StringPrototypeToLocaleUpperCase) {
|
||||
args.atOrUndefined(isolate, 1)));
|
||||
}
|
||||
|
||||
BUILTIN(PluralRulesConstructor) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
|
||||
isolate->factory()->NewStringFromStaticChars(
|
||||
"Intl.PluralRules")));
|
||||
}
|
||||
|
||||
// [[Construct]]
|
||||
Handle<JSFunction> target = args.target();
|
||||
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
|
||||
|
||||
Handle<Object> locales = args.atOrUndefined(isolate, 1);
|
||||
Handle<Object> options = args.atOrUndefined(isolate, 2);
|
||||
|
||||
// 2. Let pluralRules be ? OrdinaryCreateFromConstructor(newTarget,
|
||||
// "%PluralRulesPrototype%", « [[InitializedPluralRules]],
|
||||
// [[Locale]], [[Type]], [[MinimumIntegerDigits]],
|
||||
// [[MinimumFractionDigits]], [[MaximumFractionDigits]],
|
||||
// [[MinimumSignificantDigits]], [[MaximumSignificantDigits]] »).
|
||||
Handle<JSObject> plural_rules_obj;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, plural_rules_obj,
|
||||
JSObject::New(target, new_target));
|
||||
Handle<JSPluralRules> plural_rules =
|
||||
Handle<JSPluralRules>::cast(plural_rules_obj);
|
||||
|
||||
// 3. Return ? InitializePluralRules(pluralRules, locales, options).
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, JSPluralRules::InitializePluralRules(isolate, plural_rules,
|
||||
locales, options));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -209,6 +209,7 @@ Type::bitset BitsetType::Lub(HeapObjectType const& type) {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
|
@ -78,6 +78,8 @@ 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) \
|
||||
|
@ -760,6 +760,14 @@ 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));
|
||||
}
|
||||
@ -949,84 +957,10 @@ function compare(collator, x, y) {
|
||||
|
||||
AddBoundMethod(GlobalIntlCollator, 'compare', compare, 2, COLLATOR_TYPE, false);
|
||||
|
||||
function PluralRulesConstructor() {
|
||||
if (IS_UNDEFINED(new.target)) {
|
||||
throw %make_type_error(kConstructorNotFunction, "PluralRules");
|
||||
}
|
||||
|
||||
var locales = arguments[0];
|
||||
var options = arguments[1];
|
||||
|
||||
if (IS_UNDEFINED(options)) {
|
||||
options = {__proto__: null};
|
||||
}
|
||||
|
||||
var getOption = getGetOption(options, 'pluralrules');
|
||||
|
||||
var locale = resolveLocale('pluralrules', locales, options);
|
||||
|
||||
var internalOptions = {__proto__: null};
|
||||
%DefineWEProperty(internalOptions, 'type', getOption(
|
||||
'type', 'string', ['cardinal', 'ordinal'], 'cardinal'));
|
||||
|
||||
SetNumberFormatDigitOptions(internalOptions, options, 0, 3);
|
||||
|
||||
var requestedLocale = locale.locale;
|
||||
var resolved = %object_define_properties({__proto__: null}, {
|
||||
type: {value: internalOptions.type, writable: true},
|
||||
locale: {writable: true},
|
||||
maximumFractionDigits: {writable: true},
|
||||
minimumFractionDigits: {writable: true},
|
||||
minimumIntegerDigits: {writable: true},
|
||||
requestedLocale: {value: requestedLocale, writable: true},
|
||||
});
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'minimumSignificantDigits')) {
|
||||
%DefineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'maximumSignificantDigits')) {
|
||||
%DefineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
%DefineWEProperty(resolved, 'pluralCategories', []);
|
||||
var pluralRules = %CreatePluralRules(requestedLocale, internalOptions,
|
||||
resolved);
|
||||
|
||||
%MarkAsInitializedIntlObjectOfType(pluralRules, PLURAL_RULES_TYPE);
|
||||
pluralRules[resolvedSymbol] = resolved;
|
||||
|
||||
return pluralRules;
|
||||
}
|
||||
%SetCode(GlobalIntlPluralRules, PluralRulesConstructor);
|
||||
|
||||
DEFINE_METHOD(
|
||||
GlobalIntlPluralRules.prototype,
|
||||
resolvedOptions() {
|
||||
if (!%IsInitializedIntlObjectOfType(this, PLURAL_RULES_TYPE)) {
|
||||
throw %make_type_error(kIncompatibleMethodReceiver,
|
||||
'Intl.PluralRules.prototype.resolvedOptions',
|
||||
this);
|
||||
}
|
||||
|
||||
var result = {
|
||||
locale: this[resolvedSymbol].locale,
|
||||
type: this[resolvedSymbol].type,
|
||||
minimumIntegerDigits: this[resolvedSymbol].minimumIntegerDigits,
|
||||
minimumFractionDigits: this[resolvedSymbol].minimumFractionDigits,
|
||||
maximumFractionDigits: this[resolvedSymbol].maximumFractionDigits,
|
||||
};
|
||||
|
||||
if (HAS_OWN_PROPERTY(this[resolvedSymbol], 'minimumSignificantDigits')) {
|
||||
defineWECProperty(result, 'minimumSignificantDigits',
|
||||
this[resolvedSymbol].minimumSignificantDigits);
|
||||
}
|
||||
|
||||
if (HAS_OWN_PROPERTY(this[resolvedSymbol], 'maximumSignificantDigits')) {
|
||||
defineWECProperty(result, 'maximumSignificantDigits',
|
||||
this[resolvedSymbol].maximumSignificantDigits);
|
||||
}
|
||||
|
||||
defineWECProperty(result, 'pluralCategories',
|
||||
%_Call(ArraySlice, this[resolvedSymbol].pluralCategories));
|
||||
return result;
|
||||
return %PluralRulesResolvedOptions(this);
|
||||
}
|
||||
);
|
||||
|
||||
@ -1040,12 +974,6 @@ DEFINE_METHOD(
|
||||
DEFINE_METHOD(
|
||||
GlobalIntlPluralRules.prototype,
|
||||
select(value) {
|
||||
if (!%IsInitializedIntlObjectOfType(this, PLURAL_RULES_TYPE)) {
|
||||
throw %make_type_error(kIncompatibleMethodReceiver,
|
||||
'Intl.PluralRules.prototype.select',
|
||||
this);
|
||||
}
|
||||
|
||||
return %PluralRulesSelect(this, TO_NUMBER(value) + 0);
|
||||
}
|
||||
);
|
||||
|
@ -728,6 +728,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
case WASM_GLOBAL_TYPE:
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "src/objects/js-regexp-inl.h"
|
||||
#include "src/objects/js-regexp-string-iterator-inl.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/js-relative-time-format-inl.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#include "src/objects/maybe-object.h"
|
||||
@ -358,6 +359,9 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
JSLocale::cast(this)->JSLocaleVerify(isolate);
|
||||
break;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
JSPluralRules::cast(this)->JSPluralRulesVerify(isolate);
|
||||
break;
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
JSRelativeTimeFormat::cast(this)->JSRelativeTimeFormatVerify(isolate);
|
||||
break;
|
||||
@ -1865,12 +1869,14 @@ void InterpreterData::InterpreterDataVerify(Isolate* isolate) {
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
void JSListFormat::JSListFormatVerify(Isolate* isolate) {
|
||||
JSObjectVerify(isolate);
|
||||
VerifyObjectField(isolate, kLocaleOffset);
|
||||
VerifyObjectField(isolate, kFormatterOffset);
|
||||
VerifyObjectField(isolate, kFlagsOffset);
|
||||
}
|
||||
|
||||
void JSLocale::JSLocaleVerify(Isolate* isolate) {
|
||||
JSObjectVerify(isolate);
|
||||
VerifyObjectField(isolate, kLanguageOffset);
|
||||
VerifyObjectField(isolate, kScriptOffset);
|
||||
VerifyObjectField(isolate, kRegionOffset);
|
||||
@ -1885,7 +1891,17 @@ void JSLocale::JSLocaleVerify(Isolate* isolate) {
|
||||
VerifyObjectField(isolate, kNumberingSystemOffset);
|
||||
}
|
||||
|
||||
void JSPluralRules::JSPluralRulesVerify(Isolate* isolate) {
|
||||
CHECK(IsJSPluralRules());
|
||||
JSObjectVerify(isolate);
|
||||
VerifyObjectField(isolate, kLocaleOffset);
|
||||
VerifyObjectField(isolate, kTypeOffset);
|
||||
VerifyObjectField(isolate, kICUPluralRulesOffset);
|
||||
VerifyObjectField(isolate, kICUDecimalFormatOffset);
|
||||
}
|
||||
|
||||
void JSRelativeTimeFormat::JSRelativeTimeFormatVerify(Isolate* isolate) {
|
||||
JSObjectVerify(isolate);
|
||||
VerifyObjectField(isolate, kLocaleOffset);
|
||||
VerifyObjectField(isolate, kFormatterOffset);
|
||||
VerifyObjectField(isolate, kFlagsOffset);
|
||||
|
@ -219,6 +219,7 @@ namespace internal {
|
||||
INSTANCE_TYPE_LIST_BEFORE_INTL(V) \
|
||||
V(JS_INTL_LIST_FORMAT_TYPE) \
|
||||
V(JS_INTL_LOCALE_TYPE) \
|
||||
V(JS_INTL_PLURAL_RULES_TYPE) \
|
||||
V(JS_INTL_RELATIVE_TIME_FORMAT_TYPE) \
|
||||
INSTANCE_TYPE_LIST_AFTER_INTL(V)
|
||||
#else
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "src/objects/js-regexp-inl.h"
|
||||
#include "src/objects/js-regexp-string-iterator-inl.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/js-relative-time-format-inl.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#include "src/objects/literal-objects-inl.h"
|
||||
@ -310,6 +311,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
JSLocale::cast(this)->JSLocalePrint(os);
|
||||
break;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
JSPluralRules::cast(this)->JSPluralRulesPrint(os);
|
||||
break;
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
JSRelativeTimeFormat::cast(this)->JSRelativeTimeFormatPrint(os);
|
||||
break;
|
||||
@ -1976,6 +1980,16 @@ void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void JSPluralRules::JSPluralRulesPrint(std::ostream& os) { // NOLINT
|
||||
HeapObject::PrintHeader(os, "JSPluralRules");
|
||||
JSObjectPrint(os);
|
||||
os << "\n - locale: " << Brief(locale());
|
||||
os << "\n - type: " << Brief(type());
|
||||
os << "\n - icu plural rules: " << Brief(icu_plural_rules());
|
||||
os << "\n - icu decimal format: " << Brief(icu_decimal_format());
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void JSRelativeTimeFormat::JSRelativeTimeFormatPrint(
|
||||
std::ostream& os) { // NOLINT
|
||||
JSObjectPrintHeader(os, this, "JSRelativeTimeFormat");
|
||||
|
@ -68,6 +68,7 @@
|
||||
#include "src/objects/js-regexp-inl.h"
|
||||
#include "src/objects/js-regexp-string-iterator.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
#include "src/objects/js-relative-time-format.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#include "src/objects/literal-objects-inl.h"
|
||||
@ -1422,6 +1423,8 @@ int JSObject::GetHeaderSize(InstanceType type,
|
||||
return JSListFormat::kSize;
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
return JSLocale::kSize;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
return JSPluralRules::kSize;
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
return JSRelativeTimeFormat::kSize;
|
||||
#endif // V8_INTL_SUPPORT
|
||||
@ -3170,6 +3173,7 @@ VisitorId Map::GetVisitorId(Map* map) {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
case WASM_GLOBAL_TYPE:
|
||||
@ -13211,6 +13215,9 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
|
||||
case JS_DATE_TYPE:
|
||||
case JS_FUNCTION_TYPE:
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
#endif
|
||||
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
|
||||
case JS_MAP_TYPE:
|
||||
case JS_MESSAGE_OBJECT_TYPE:
|
||||
|
@ -77,6 +77,7 @@
|
||||
// - JSModuleNamespace
|
||||
// - JSListFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - JSLocale // If V8_INTL_SUPPORT enabled.
|
||||
// - JSPluralRules // If V8_INTL_SUPPORT enabled.
|
||||
// - JSRelativeTimeFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - WasmGlobalObject
|
||||
// - WasmInstanceObject
|
||||
@ -585,6 +586,7 @@ enum InstanceType : uint16_t {
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
JS_INTL_LIST_FORMAT_TYPE,
|
||||
JS_INTL_LOCALE_TYPE,
|
||||
JS_INTL_PLURAL_RULES_TYPE,
|
||||
JS_INTL_RELATIVE_TIME_FORMAT_TYPE,
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
@ -701,6 +703,7 @@ class JSGlobalProxy;
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
class JSListFormat;
|
||||
class JSLocale;
|
||||
class JSPluralRules;
|
||||
class JSRelativeTimeFormat;
|
||||
#endif // V8_INTL_SUPPORT
|
||||
class JSPromise;
|
||||
@ -912,6 +915,7 @@ class ZoneForwardList;
|
||||
HEAP_OBJECT_ORDINARY_TYPE_LIST_BASE(V) \
|
||||
V(JSListFormat) \
|
||||
V(JSLocale) \
|
||||
V(JSPluralRules) \
|
||||
V(JSRelativeTimeFormat)
|
||||
#else
|
||||
#define HEAP_OBJECT_ORDINARY_TYPE_LIST(V) HEAP_OBJECT_ORDINARY_TYPE_LIST_BASE(V)
|
||||
@ -1028,10 +1032,11 @@ class ZoneForwardList;
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
|
||||
#define INSTANCE_TYPE_CHECKERS_SINGLE(V) \
|
||||
INSTANCE_TYPE_CHECKERS_SINGLE_BASE(V) \
|
||||
V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \
|
||||
V(JSLocale, JS_INTL_LOCALE_TYPE) \
|
||||
#define INSTANCE_TYPE_CHECKERS_SINGLE(V) \
|
||||
INSTANCE_TYPE_CHECKERS_SINGLE_BASE(V) \
|
||||
V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \
|
||||
V(JSLocale, JS_INTL_LOCALE_TYPE) \
|
||||
V(JSPluralRules, JS_INTL_PLURAL_RULES_TYPE) \
|
||||
V(JSRelativeTimeFormat, JS_INTL_RELATIVE_TIME_FORMAT_TYPE)
|
||||
|
||||
#else
|
||||
|
@ -94,34 +94,6 @@ bool ExtractBooleanSetting(Isolate* isolate, Handle<JSObject> options,
|
||||
return false;
|
||||
}
|
||||
|
||||
icu::Locale CreateICULocale(Isolate* isolate, Handle<String> bcp47_locale_str) {
|
||||
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
||||
v8::String::Utf8Value bcp47_locale(v8_isolate,
|
||||
v8::Utils::ToLocal(bcp47_locale_str));
|
||||
CHECK_NOT_NULL(*bcp47_locale);
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
// Convert BCP47 into ICU locale format.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char icu_result[ULOC_FULLNAME_CAPACITY];
|
||||
int icu_length = 0;
|
||||
|
||||
// bcp47_locale_str should be a canonicalized language tag, which
|
||||
// means this shouldn't fail.
|
||||
uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
|
||||
&icu_length, &status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
CHECK_LT(0, icu_length);
|
||||
|
||||
icu::Locale icu_locale(icu_result);
|
||||
if (icu_locale.isBogus()) {
|
||||
FATAL("Failed to create ICU locale, are ICU data files missing?");
|
||||
}
|
||||
|
||||
return icu_locale;
|
||||
}
|
||||
|
||||
icu::SimpleDateFormat* CreateICUDateFormat(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options) {
|
||||
@ -707,89 +679,6 @@ void SetResolvedCollatorSettings(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options, icu::PluralRules** pl,
|
||||
icu::DecimalFormat** nf) {
|
||||
// Make formatter from options. Numbering system is added
|
||||
// to the locale as Unicode extension (if it was specified at all).
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
UPluralType type = UPLURAL_TYPE_CARDINAL;
|
||||
|
||||
icu::UnicodeString type_string;
|
||||
if (ExtractStringSetting(isolate, options, "type", &type_string)) {
|
||||
if (type_string == UNICODE_STRING_SIMPLE("ordinal")) {
|
||||
type = UPLURAL_TYPE_ORDINAL;
|
||||
} else {
|
||||
CHECK(type_string == UNICODE_STRING_SIMPLE("cardinal"));
|
||||
}
|
||||
}
|
||||
|
||||
icu::PluralRules* plural_rules =
|
||||
icu::PluralRules::forLocale(icu_locale, type, status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
delete plural_rules;
|
||||
return false;
|
||||
}
|
||||
|
||||
icu::DecimalFormat* number_format = static_cast<icu::DecimalFormat*>(
|
||||
icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status));
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
delete plural_rules;
|
||||
delete number_format;
|
||||
return false;
|
||||
}
|
||||
|
||||
*pl = plural_rules;
|
||||
*nf = number_format;
|
||||
|
||||
SetNumericSettings(isolate, number_format, options);
|
||||
|
||||
// Set rounding mode.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetResolvedPluralRulesSettings(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
icu::PluralRules* plural_rules,
|
||||
icu::DecimalFormat* number_format,
|
||||
Handle<JSObject> resolved) {
|
||||
SetResolvedNumericSettings(isolate, icu_locale, number_format, resolved);
|
||||
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
Handle<JSObject> pluralCategories = Handle<JSObject>::cast(
|
||||
JSObject::GetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("pluralCategories"))
|
||||
.ToHandleChecked());
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::unique_ptr<icu::StringEnumeration> categories(
|
||||
plural_rules->getKeywords(status));
|
||||
if (U_FAILURE(status)) return false;
|
||||
|
||||
if (U_FAILURE(status)) return false;
|
||||
|
||||
for (int32_t i = 0;; i++) {
|
||||
const icu::UnicodeString* category = categories->snext(status);
|
||||
if (U_FAILURE(status)) return false;
|
||||
if (category == nullptr) return true;
|
||||
|
||||
std::string keyword;
|
||||
Handle<String> value = factory->NewStringFromAsciiChecked(
|
||||
category->toUTF8String(keyword).data());
|
||||
|
||||
LookupIterator it(isolate, pluralCategories, i, LookupIterator::OWN);
|
||||
JSObject::DefineOwnPropertyIgnoreAttributes(&it, value,
|
||||
PropertyAttributes::NONE)
|
||||
.ToHandleChecked();
|
||||
}
|
||||
}
|
||||
|
||||
icu::BreakIterator* CreateICUBreakIterator(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options) {
|
||||
@ -848,11 +737,40 @@ void SetResolvedBreakIteratorSettings(Isolate* isolate,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
icu::Locale Intl::CreateICULocale(Isolate* isolate,
|
||||
Handle<String> bcp47_locale_str) {
|
||||
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
||||
v8::String::Utf8Value bcp47_locale(v8_isolate,
|
||||
v8::Utils::ToLocal(bcp47_locale_str));
|
||||
CHECK_NOT_NULL(*bcp47_locale);
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
// Convert BCP47 into ICU locale format.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char icu_result[ULOC_FULLNAME_CAPACITY];
|
||||
int icu_length = 0;
|
||||
|
||||
// bcp47_locale_str should be a canonicalized language tag, which
|
||||
// means this shouldn't fail.
|
||||
uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
|
||||
&icu_length, &status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
CHECK_LT(0, icu_length);
|
||||
|
||||
icu::Locale icu_locale(icu_result);
|
||||
if (icu_locale.isBogus()) {
|
||||
FATAL("Failed to create ICU locale, are ICU data files missing?");
|
||||
}
|
||||
|
||||
return icu_locale;
|
||||
}
|
||||
|
||||
// static
|
||||
icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
icu::Locale icu_locale = CreateICULocale(isolate, locale);
|
||||
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
icu::SimpleDateFormat* date_format =
|
||||
@ -889,7 +807,7 @@ void DateFormat::DeleteDateFormat(const v8::WeakCallbackInfo<void>& data) {
|
||||
icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
icu::Locale icu_locale = CreateICULocale(isolate, locale);
|
||||
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
icu::DecimalFormat* number_format =
|
||||
@ -929,7 +847,7 @@ icu::Collator* Collator::InitializeCollator(Isolate* isolate,
|
||||
Handle<String> locale,
|
||||
Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
icu::Locale icu_locale = CreateICULocale(isolate, locale);
|
||||
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
|
||||
@ -957,56 +875,10 @@ icu::Collator* Collator::UnpackCollator(Handle<JSObject> obj) {
|
||||
return Managed<icu::Collator>::cast(obj->GetEmbedderField(0))->raw();
|
||||
}
|
||||
|
||||
void PluralRules::InitializePluralRules(Isolate* isolate, Handle<String> locale,
|
||||
Handle<JSObject> options,
|
||||
Handle<JSObject> resolved,
|
||||
icu::PluralRules** plural_rules,
|
||||
icu::DecimalFormat** number_format) {
|
||||
icu::Locale icu_locale = CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
bool success = CreateICUPluralRules(isolate, icu_locale, options,
|
||||
plural_rules, number_format);
|
||||
if (!success) {
|
||||
// Remove extensions and try again.
|
||||
icu::Locale no_extension_locale(icu_locale.getBaseName());
|
||||
success = CreateICUPluralRules(isolate, no_extension_locale, options,
|
||||
plural_rules, number_format);
|
||||
|
||||
if (!success) {
|
||||
FATAL("Failed to create ICU PluralRules, are ICU data files missing?");
|
||||
}
|
||||
|
||||
// Set resolved settings (pattern, numbering system).
|
||||
success = SetResolvedPluralRulesSettings(
|
||||
isolate, no_extension_locale, *plural_rules, *number_format, resolved);
|
||||
} else {
|
||||
success = SetResolvedPluralRulesSettings(isolate, icu_locale, *plural_rules,
|
||||
*number_format, resolved);
|
||||
}
|
||||
|
||||
CHECK_NOT_NULL(*plural_rules);
|
||||
CHECK_NOT_NULL(*number_format);
|
||||
}
|
||||
|
||||
icu::PluralRules* PluralRules::UnpackPluralRules(Handle<JSObject> obj) {
|
||||
return reinterpret_cast<icu::PluralRules*>(obj->GetEmbedderField(0));
|
||||
}
|
||||
|
||||
icu::DecimalFormat* PluralRules::UnpackNumberFormat(Handle<JSObject> obj) {
|
||||
return reinterpret_cast<icu::DecimalFormat*>(obj->GetEmbedderField(1));
|
||||
}
|
||||
|
||||
void PluralRules::DeletePluralRules(const v8::WeakCallbackInfo<void>& data) {
|
||||
delete reinterpret_cast<icu::PluralRules*>(data.GetInternalField(0));
|
||||
delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(1));
|
||||
GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
|
||||
}
|
||||
|
||||
icu::BreakIterator* V8BreakIterator::InitializeBreakIterator(
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
icu::Locale icu_locale = CreateICULocale(isolate, locale);
|
||||
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
icu::BreakIterator* break_iterator =
|
||||
@ -1471,8 +1343,7 @@ MaybeHandle<JSObject> Intl::ResolveLocale(Isolate* isolate, const char* service,
|
||||
Handle<JSFunction> resolve_locale_function = isolate->resolve_locale();
|
||||
|
||||
Handle<Object> result;
|
||||
Handle<Object> undefined_value(ReadOnlyRoots(isolate).undefined_value(),
|
||||
isolate);
|
||||
Handle<Object> undefined_value = isolate->factory()->undefined_value();
|
||||
Handle<Object> args[] = {service_str, requestedLocales, options};
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
@ -1483,6 +1354,23 @@ MaybeHandle<JSObject> Intl::ResolveLocale(Isolate* isolate, const char* service,
|
||||
return Handle<JSObject>::cast(result);
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> Intl::CanonicalizeLocaleList(Isolate* isolate,
|
||||
Handle<Object> locales) {
|
||||
Handle<JSFunction> canonicalize_locale_list_function =
|
||||
isolate->canonicalize_locale_list();
|
||||
|
||||
Handle<Object> result;
|
||||
Handle<Object> undefined_value = isolate->factory()->undefined_value();
|
||||
Handle<Object> args[] = {locales};
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
Execution::Call(isolate, canonicalize_locale_list_function,
|
||||
undefined_value, arraysize(args), args),
|
||||
JSObject);
|
||||
|
||||
return Handle<JSObject>::cast(result);
|
||||
}
|
||||
|
||||
Maybe<bool> Intl::GetStringOption(Isolate* isolate, Handle<JSReceiver> options,
|
||||
const char* property,
|
||||
std::vector<const char*> values,
|
||||
@ -2045,45 +1933,149 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate,
|
||||
}
|
||||
|
||||
// ecma402/#sec-defaultnumberoption
|
||||
MaybeHandle<Smi> Intl::DefaultNumberOption(Isolate* isolate,
|
||||
Handle<Object> value, int min,
|
||||
int max, int fallback,
|
||||
Handle<String> property) {
|
||||
Maybe<int> Intl::DefaultNumberOption(Isolate* isolate, Handle<Object> value,
|
||||
int min, int max, int fallback,
|
||||
Handle<String> property) {
|
||||
// 2. Else, return fallback.
|
||||
if (value->IsUndefined()) {
|
||||
return Handle<Smi>(Smi::FromInt(fallback), isolate);
|
||||
}
|
||||
if (value->IsUndefined()) return Just(fallback);
|
||||
|
||||
// 1. If value is not undefined, then
|
||||
// a. Let value be ? ToNumber(value).
|
||||
Handle<Object> value_num;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, value_num,
|
||||
Object::ToNumber(isolate, value), Smi);
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, value_num, Object::ToNumber(isolate, value), Nothing<int>());
|
||||
DCHECK(value_num->IsNumber());
|
||||
|
||||
// b. If value is NaN or less than minimum or greater than maximum, throw a
|
||||
// RangeError exception.
|
||||
if (value_num->IsNaN() || value_num->Number() < min ||
|
||||
value_num->Number() > max) {
|
||||
THROW_NEW_ERROR(
|
||||
THROW_NEW_ERROR_RETURN_VALUE(
|
||||
isolate,
|
||||
NewRangeError(MessageTemplate::kPropertyValueOutOfRange, property),
|
||||
Smi);
|
||||
Nothing<int>());
|
||||
}
|
||||
|
||||
// The max and min arguments are integers and the above check makes
|
||||
// sure that we are within the integer range making this double to
|
||||
// int conversion safe.
|
||||
//
|
||||
// c. Return floor(value).
|
||||
return Handle<Smi>(Smi::FromInt(floor(value_num->Number())), isolate);
|
||||
return Just(FastD2I(floor(value_num->Number())));
|
||||
}
|
||||
|
||||
// ecma402/#sec-getnumberoption
|
||||
MaybeHandle<Smi> Intl::GetNumberOption(Isolate* isolate,
|
||||
Handle<JSReceiver> options,
|
||||
Handle<String> property, int min,
|
||||
int max, int fallback) {
|
||||
Handle<Object> value;
|
||||
Maybe<int> Intl::GetNumberOption(Isolate* isolate, Handle<JSReceiver> options,
|
||||
Handle<String> property, int min, int max,
|
||||
int fallback) {
|
||||
// 1. Let value be ? Get(options, property).
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, value, JSReceiver::GetProperty(isolate, options, property), Smi);
|
||||
Handle<Object> value;
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, value, JSReceiver::GetProperty(isolate, options, property),
|
||||
Nothing<int>());
|
||||
|
||||
// Return ? DefaultNumberOption(value, minimum, maximum, fallback).
|
||||
return DefaultNumberOption(isolate, value, min, max, fallback, property);
|
||||
}
|
||||
|
||||
Maybe<int> Intl::GetNumberOption(Isolate* isolate, Handle<JSReceiver> options,
|
||||
const char* property, int min, int max,
|
||||
int fallback) {
|
||||
Handle<String> property_str =
|
||||
isolate->factory()->NewStringFromAsciiChecked(property);
|
||||
return GetNumberOption(isolate, options, property_str, min, max, fallback);
|
||||
}
|
||||
|
||||
Maybe<bool> Intl::SetNumberFormatDigitOptions(Isolate* isolate,
|
||||
icu::DecimalFormat* number_format,
|
||||
Handle<JSReceiver> options,
|
||||
int mnfd_default,
|
||||
int mxfd_default) {
|
||||
CHECK_NOT_NULL(number_format);
|
||||
|
||||
// 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21,
|
||||
// 1).
|
||||
int mnid;
|
||||
if (!GetNumberOption(isolate, options, "minimumIntegerDigits", 1, 21, 1)
|
||||
.To(&mnid)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
// 6. Let mnfd be ? GetNumberOption(options, "minimumFractionDigits", 0, 20,
|
||||
// mnfdDefault).
|
||||
int mnfd;
|
||||
if (!GetNumberOption(isolate, options, "minimumFractionDigits", 0, 20,
|
||||
mnfd_default)
|
||||
.To(&mnfd)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
// 7. Let mxfdActualDefault be max( mnfd, mxfdDefault ).
|
||||
int mxfd_actual_default = std::max(mnfd, mxfd_default);
|
||||
|
||||
// 8. Let mxfd be ? GetNumberOption(options,
|
||||
// "maximumFractionDigits", mnfd, 20, mxfdActualDefault).
|
||||
int mxfd;
|
||||
if (!GetNumberOption(isolate, options, "maximumFractionDigits", mnfd, 20,
|
||||
mxfd_actual_default)
|
||||
.To(&mxfd)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
// 9. Let mnsd be ? Get(options, "minimumSignificantDigits").
|
||||
Handle<Object> mnsd_obj;
|
||||
Handle<String> mnsd_str =
|
||||
isolate->factory()->NewStringFromStaticChars("minimumSignificantDigits");
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, mnsd_obj, JSReceiver::GetProperty(isolate, options, mnsd_str),
|
||||
Nothing<bool>());
|
||||
|
||||
// 10. Let mxsd be ? Get(options, "maximumSignificantDigits").
|
||||
Handle<Object> mxsd_obj;
|
||||
Handle<String> mxsd_str =
|
||||
isolate->factory()->NewStringFromStaticChars("maximumSignificantDigits");
|
||||
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
||||
isolate, mxsd_obj, JSReceiver::GetProperty(isolate, options, mxsd_str),
|
||||
Nothing<bool>());
|
||||
|
||||
// 11. Set intlObj.[[MinimumIntegerDigits]] to mnid.
|
||||
number_format->setMinimumIntegerDigits(mnid);
|
||||
|
||||
// 12. Set intlObj.[[MinimumFractionDigits]] to mnfd.
|
||||
number_format->setMinimumFractionDigits(mnfd);
|
||||
|
||||
// 13. Set intlObj.[[MaximumFractionDigits]] to mxfd.
|
||||
number_format->setMaximumFractionDigits(mxfd);
|
||||
|
||||
bool significant_digits_used = false;
|
||||
// 14. If mnsd is not undefined or mxsd is not undefined, then
|
||||
if (!mnsd_obj->IsUndefined(isolate) || !mxsd_obj->IsUndefined(isolate)) {
|
||||
// 14. a. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1).
|
||||
int mnsd;
|
||||
if (!DefaultNumberOption(isolate, mnsd_obj, 1, 21, 1, mnsd_str).To(&mnsd)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
// 14. b. Let mxsd be ? DefaultNumberOption(mxsd, mnsd, 21, 21).
|
||||
int mxsd;
|
||||
if (!DefaultNumberOption(isolate, mxsd_obj, mnsd, 21, 21, mxsd_str)
|
||||
.To(&mxsd)) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
|
||||
significant_digits_used = true;
|
||||
|
||||
// 14. c. Set intlObj.[[MinimumSignificantDigits]] to mnsd.
|
||||
number_format->setMinimumSignificantDigits(mnsd);
|
||||
|
||||
// 14. d. Set intlObj.[[MaximumSignificantDigits]] to mxsd.
|
||||
number_format->setMaximumSignificantDigits(mxsd);
|
||||
}
|
||||
|
||||
number_format->setSignificantDigitsUsed(significant_digits_used);
|
||||
number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "src/contexts.h"
|
||||
#include "src/intl.h"
|
||||
#include "src/objects.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/uversion.h"
|
||||
|
||||
namespace U_ICU_NAMESPACE {
|
||||
@ -136,41 +137,6 @@ class Collator {
|
||||
Collator();
|
||||
};
|
||||
|
||||
class PluralRules {
|
||||
public:
|
||||
// Create a PluralRules and DecimalFormat for the specificied locale and
|
||||
// options. Returns false on an ICU failure.
|
||||
static void InitializePluralRules(Isolate* isolate, Handle<String> locale,
|
||||
Handle<JSObject> options,
|
||||
Handle<JSObject> resolved,
|
||||
icu::PluralRules** plural_rules,
|
||||
icu::DecimalFormat** decimal_format);
|
||||
|
||||
// Unpacks PluralRules object from corresponding JavaScript object.
|
||||
static icu::PluralRules* UnpackPluralRules(Handle<JSObject> obj);
|
||||
|
||||
// Unpacks NumberFormat object from corresponding JavaScript PluralRUles
|
||||
// object.
|
||||
static icu::DecimalFormat* UnpackNumberFormat(Handle<JSObject> obj);
|
||||
|
||||
// Release memory we allocated for the Collator once the JS object that holds
|
||||
// the pointer gets garbage collected.
|
||||
static void DeletePluralRules(const v8::WeakCallbackInfo<void>& data);
|
||||
|
||||
// Layout description.
|
||||
static const int kPluralRules = JSObject::kHeaderSize;
|
||||
// Values are formatted with this NumberFormat and then parsed as a Number
|
||||
// to round them based on the options passed into the PluralRules objct.
|
||||
// TODO(littledan): If a future version of ICU supports the rounding
|
||||
// built-in to PluralRules, switch to that, see this bug:
|
||||
// http://bugs.icu-project.org/trac/ticket/12763
|
||||
static const int kNumberFormat = kPluralRules + kPointerSize;
|
||||
static const int kSize = kNumberFormat + kPointerSize;
|
||||
|
||||
private:
|
||||
PluralRules();
|
||||
};
|
||||
|
||||
class V8BreakIterator {
|
||||
public:
|
||||
// Create a BreakIterator for the specificied locale and options. Returns the
|
||||
@ -266,6 +232,13 @@ class Intl {
|
||||
Isolate* isolate, const char* service, Handle<Object> requestedLocales,
|
||||
Handle<Object> options);
|
||||
|
||||
// This currently calls out to the JavaScript implementation of
|
||||
// CanonicalizeLocaleList.
|
||||
//
|
||||
// ecma402/#sec-canonicalizelocalelist
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CanonicalizeLocaleList(
|
||||
Isolate* isolate, Handle<Object> locales);
|
||||
|
||||
// ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
|
||||
// ecma402/#sec-getoption
|
||||
//
|
||||
@ -337,14 +310,25 @@ class Intl {
|
||||
Handle<Object> options);
|
||||
|
||||
// ecma402/#sec-defaultnumberoption
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<Smi> DefaultNumberOption(
|
||||
V8_WARN_UNUSED_RESULT static Maybe<int> DefaultNumberOption(
|
||||
Isolate* isolate, Handle<Object> value, int min, int max, int fallback,
|
||||
Handle<String> property);
|
||||
|
||||
// ecma402/#sec-getnumberoption
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<Smi> GetNumberOption(
|
||||
V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption(
|
||||
Isolate* isolate, Handle<JSReceiver> options, Handle<String> property,
|
||||
int min, int max, int fallback);
|
||||
V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption(
|
||||
Isolate* isolate, Handle<JSReceiver> options, const char* property,
|
||||
int min, int max, int fallback);
|
||||
|
||||
// ecma402/#sec-setnfdigitoptions
|
||||
V8_WARN_UNUSED_RESULT static Maybe<bool> SetNumberFormatDigitOptions(
|
||||
Isolate* isolate, icu::DecimalFormat* number_format,
|
||||
Handle<JSReceiver> options, int mnfd_default, int mxfd_default);
|
||||
|
||||
icu::Locale static CreateICULocale(Isolate* isolate,
|
||||
Handle<String> bcp47_locale_str);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
36
src/objects/js-plural-rules-inl.h
Normal file
36
src/objects/js-plural-rules-inl.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
#ifndef V8_INTL_SUPPORT
|
||||
#error Internationalization is expected to be enabled.
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#ifndef V8_OBJECTS_JS_PLURAL_RULES_INL_H_
|
||||
#define V8_OBJECTS_JS_PLURAL_RULES_INL_H_
|
||||
|
||||
#include "src/api-inl.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
ACCESSORS(JSPluralRules, locale, String, kLocaleOffset)
|
||||
ACCESSORS(JSPluralRules, type, String, kTypeOffset)
|
||||
ACCESSORS(JSPluralRules, icu_plural_rules, Managed<icu::PluralRules>,
|
||||
kICUPluralRulesOffset)
|
||||
ACCESSORS(JSPluralRules, icu_decimal_format, Managed<icu::DecimalFormat>,
|
||||
kICUDecimalFormatOffset)
|
||||
|
||||
CAST_ACCESSOR(JSPluralRules);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#include "src/objects/object-macros-undef.h"
|
||||
|
||||
#endif // V8_OBJECTS_JS_PLURAL_RULES_INL_H_
|
324
src/objects/js-plural-rules.cc
Normal file
324
src/objects/js-plural-rules.cc
Normal file
@ -0,0 +1,324 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
#ifndef V8_INTL_SUPPORT
|
||||
#error Internationalization is expected to be enabled.
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/numfmt.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/strenum.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
bool CreateICUPluralRules(Isolate* isolate, const icu::Locale& icu_locale,
|
||||
const char* type_string,
|
||||
std::unique_ptr<icu::PluralRules>* pl,
|
||||
std::unique_ptr<icu::DecimalFormat>* nf) {
|
||||
// Make formatter from options. Numbering system is added
|
||||
// to the locale as Unicode extension (if it was specified at all).
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
UPluralType type = UPLURAL_TYPE_CARDINAL;
|
||||
if (strcmp(type_string, "ordinal") == 0) {
|
||||
type = UPLURAL_TYPE_ORDINAL;
|
||||
} else {
|
||||
CHECK_EQ(0, strcmp(type_string, "cardinal"));
|
||||
}
|
||||
|
||||
std::unique_ptr<icu::PluralRules> plural_rules(
|
||||
icu::PluralRules::forLocale(icu_locale, type, status));
|
||||
if (U_FAILURE(status)) {
|
||||
return false;
|
||||
}
|
||||
CHECK_NOT_NULL(plural_rules.get());
|
||||
|
||||
std::unique_ptr<icu::DecimalFormat> number_format(
|
||||
static_cast<icu::DecimalFormat*>(
|
||||
icu::NumberFormat::createInstance(icu_locale, UNUM_DECIMAL, status)));
|
||||
if (U_FAILURE(status)) {
|
||||
return false;
|
||||
}
|
||||
CHECK_NOT_NULL(number_format.get());
|
||||
|
||||
*pl = std::move(plural_rules);
|
||||
*nf = std::move(number_format);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitializeICUPluralRules(
|
||||
Isolate* isolate, Handle<String> 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) {
|
||||
// Remove extensions and try again.
|
||||
icu::Locale no_extension_locale(icu_locale.getBaseName());
|
||||
success = CreateICUPluralRules(isolate, no_extension_locale, type,
|
||||
plural_rules, number_format);
|
||||
|
||||
if (!success) {
|
||||
FATAL("Failed to create ICU PluralRules, are ICU data files missing?");
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_NOT_NULL((*plural_rules).get());
|
||||
CHECK_NOT_NULL((*number_format).get());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
MaybeHandle<JSPluralRules> JSPluralRules::InitializePluralRules(
|
||||
Isolate* isolate, Handle<JSPluralRules> plural_rules,
|
||||
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::CanonicalizeLocaleList(isolate, locales),
|
||||
JSPluralRules);
|
||||
|
||||
// 2. If options is undefined, then
|
||||
if (options_obj->IsUndefined(isolate)) {
|
||||
// 2. a. Let options be ObjectCreate(null).
|
||||
options_obj = isolate->factory()->NewJSObjectWithNullProto();
|
||||
} else {
|
||||
// 3. Else
|
||||
// 3. a. Let options be ? ToObject(options).
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, options_obj,
|
||||
Object::ToObject(isolate, options_obj, "Intl.PluralRules"),
|
||||
JSPluralRules);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// 7. Let t be ? GetOption(options, "type", "string", « "cardinal",
|
||||
// "ordinal" », "cardinal").
|
||||
std::vector<const char*> values = {"cardinal", "ordinal"};
|
||||
std::unique_ptr<char[]> type_str = nullptr;
|
||||
const char* type_cstr = "cardinal";
|
||||
Maybe<bool> found = Intl::GetStringOption(isolate, options, "type", values,
|
||||
"Intl.PluralRules", &type_str);
|
||||
MAYBE_RETURN(found, MaybeHandle<JSPluralRules>());
|
||||
if (found.FromJust()) {
|
||||
type_cstr = type_str.get();
|
||||
}
|
||||
|
||||
// 8. Set pluralRules.[[Type]] to t.
|
||||
Handle<String> type =
|
||||
isolate->factory()->NewStringFromAsciiChecked(type_cstr);
|
||||
plural_rules->set_type(*type);
|
||||
|
||||
// Note: The spec says we should do ResolveLocale after performing
|
||||
// SetNumberFormatDigitOptions but we need the locale to create all
|
||||
// the ICU data structures.
|
||||
//
|
||||
// This isn't observable so we aren't violating the spec.
|
||||
|
||||
// 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);
|
||||
|
||||
Handle<String> locale_str = isolate->factory()->locale_string();
|
||||
Handle<Object> locale_obj = JSObject::GetDataProperty(r, locale_str);
|
||||
|
||||
// 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);
|
||||
|
||||
// 12. Set pluralRules.[[Locale]] to the value of r.[[locale]].
|
||||
plural_rules->set_locale(*locale);
|
||||
|
||||
std::unique_ptr<icu::PluralRules> icu_plural_rules;
|
||||
std::unique_ptr<icu::DecimalFormat> icu_decimal_format;
|
||||
InitializeICUPluralRules(isolate, locale, type_cstr, &icu_plural_rules,
|
||||
&icu_decimal_format);
|
||||
CHECK_NOT_NULL(icu_plural_rules.get());
|
||||
CHECK_NOT_NULL(icu_decimal_format.get());
|
||||
|
||||
// 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3).
|
||||
Maybe<bool> done = Intl::SetNumberFormatDigitOptions(
|
||||
isolate, icu_decimal_format.get(), options, 0, 3);
|
||||
MAYBE_RETURN(done, MaybeHandle<JSPluralRules>());
|
||||
|
||||
Handle<Managed<icu::PluralRules>> managed_plural_rules =
|
||||
Managed<icu::PluralRules>::FromUniquePtr(isolate, 0,
|
||||
std::move(icu_plural_rules));
|
||||
plural_rules->set_icu_plural_rules(*managed_plural_rules);
|
||||
|
||||
Handle<Managed<icu::DecimalFormat>> managed_decimal_format =
|
||||
Managed<icu::DecimalFormat>::FromUniquePtr(isolate, 0,
|
||||
std::move(icu_decimal_format));
|
||||
plural_rules->set_icu_decimal_format(*managed_decimal_format);
|
||||
|
||||
// 13. Return pluralRules.
|
||||
return plural_rules;
|
||||
}
|
||||
|
||||
MaybeHandle<String> JSPluralRules::ResolvePlural(
|
||||
Isolate* isolate, Handle<JSPluralRules> plural_rules,
|
||||
Handle<Object> number) {
|
||||
icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
|
||||
CHECK_NOT_NULL(icu_plural_rules);
|
||||
|
||||
icu::DecimalFormat* icu_decimal_format =
|
||||
plural_rules->icu_decimal_format()->raw();
|
||||
CHECK_NOT_NULL(icu_decimal_format);
|
||||
|
||||
// Currently, PluralRules doesn't implement all the options for rounding that
|
||||
// the Intl spec provides; format and parse the number to round to the
|
||||
// appropriate amount, then apply PluralRules.
|
||||
//
|
||||
// TODO(littledan): If a future ICU version supports an extended API to avoid
|
||||
// this step, then switch to that API. Bug thread:
|
||||
// http://bugs.icu-project.org/trac/ticket/12763
|
||||
icu::UnicodeString rounded_string;
|
||||
icu_decimal_format->format(number->Number(), rounded_string);
|
||||
|
||||
icu::Formattable formattable;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu_decimal_format->parse(rounded_string, formattable, status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
|
||||
double rounded = formattable.getDouble(status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
|
||||
icu::UnicodeString result = icu_plural_rules->select(rounded);
|
||||
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
|
||||
Handle<Object> value, const char* key) {
|
||||
Handle<String> key_str = isolate->factory()->NewStringFromAsciiChecked(key);
|
||||
|
||||
// This is a brand new JSObject that shouldn't already have the same
|
||||
// key so this shouldn't fail.
|
||||
CHECK(JSReceiver::CreateDataProperty(isolate, options, key_str, value,
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
void CreateDataPropertyForOptions(Isolate* isolate, Handle<JSObject> options,
|
||||
int value, const char* key) {
|
||||
Handle<Smi> value_smi(Smi::FromInt(value), isolate);
|
||||
CreateDataPropertyForOptions(isolate, options, value_smi, key);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<JSObject> JSPluralRules::ResolvedOptions(
|
||||
Isolate* isolate, Handle<JSPluralRules> plural_rules) {
|
||||
Handle<JSObject> options =
|
||||
isolate->factory()->NewJSObject(isolate->object_function());
|
||||
|
||||
Handle<String> locale_value(plural_rules->locale(), isolate);
|
||||
CreateDataPropertyForOptions(isolate, options, locale_value, "locale");
|
||||
|
||||
Handle<String> type_value(plural_rules->type(), isolate);
|
||||
CreateDataPropertyForOptions(isolate, options, type_value, "type");
|
||||
|
||||
icu::DecimalFormat* icu_decimal_format =
|
||||
plural_rules->icu_decimal_format()->raw();
|
||||
CHECK_NOT_NULL(icu_decimal_format);
|
||||
|
||||
// This is a safe upcast as icu::DecimalFormat inherits from
|
||||
// icu::NumberFormat.
|
||||
icu::NumberFormat* icu_number_format =
|
||||
static_cast<icu::NumberFormat*>(icu_decimal_format);
|
||||
|
||||
int min_int_digits = icu_number_format->getMinimumIntegerDigits();
|
||||
CreateDataPropertyForOptions(isolate, options, min_int_digits,
|
||||
"minimumIntegerDigits");
|
||||
|
||||
int min_fraction_digits = icu_number_format->getMinimumFractionDigits();
|
||||
CreateDataPropertyForOptions(isolate, options, min_fraction_digits,
|
||||
"minimumFractionDigits");
|
||||
|
||||
int max_fraction_digits = icu_number_format->getMaximumFractionDigits();
|
||||
CreateDataPropertyForOptions(isolate, options, max_fraction_digits,
|
||||
"maximumFractionDigits");
|
||||
|
||||
if (icu_decimal_format->areSignificantDigitsUsed()) {
|
||||
int min_significant_digits =
|
||||
icu_decimal_format->getMinimumSignificantDigits();
|
||||
CreateDataPropertyForOptions(isolate, options, min_significant_digits,
|
||||
"minimumSignificantDigits");
|
||||
|
||||
int max_significant_digits =
|
||||
icu_decimal_format->getMaximumSignificantDigits();
|
||||
CreateDataPropertyForOptions(isolate, options, max_significant_digits,
|
||||
"maximumSignificantDigits");
|
||||
}
|
||||
|
||||
// 6. Let pluralCategories be a List of Strings representing the
|
||||
// possible results of PluralRuleSelect for the selected locale pr.
|
||||
icu::PluralRules* icu_plural_rules = plural_rules->icu_plural_rules()->raw();
|
||||
CHECK_NOT_NULL(icu_plural_rules);
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::unique_ptr<icu::StringEnumeration> categories(
|
||||
icu_plural_rules->getKeywords(status));
|
||||
CHECK(U_SUCCESS(status));
|
||||
int32_t count = categories->count(status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
|
||||
Handle<FixedArray> plural_categories =
|
||||
isolate->factory()->NewFixedArray(count);
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
const icu::UnicodeString* category = categories->snext(status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
if (category == nullptr) break;
|
||||
|
||||
std::string keyword;
|
||||
Handle<String> value = isolate->factory()->NewStringFromAsciiChecked(
|
||||
category->toUTF8String(keyword).data());
|
||||
plural_categories->set(i, *value);
|
||||
}
|
||||
|
||||
// 7. Perform ! CreateDataProperty(options, "pluralCategories",
|
||||
// CreateArrayFromList(pluralCategories)).
|
||||
Handle<JSArray> plural_categories_value =
|
||||
isolate->factory()->NewJSArrayWithElements(plural_categories);
|
||||
CreateDataPropertyForOptions(isolate, options, plural_categories_value,
|
||||
"pluralCategories");
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
69
src/objects/js-plural-rules.h
Normal file
69
src/objects/js-plural-rules.h
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
#ifndef V8_INTL_SUPPORT
|
||||
#error Internationalization is expected to be enabled.
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
||||
#ifndef V8_OBJECTS_JS_PLURAL_RULES_H_
|
||||
#define V8_OBJECTS_JS_PLURAL_RULES_H_
|
||||
|
||||
#include "src/heap/factory.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/managed.h"
|
||||
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class JSPluralRules : public JSObject {
|
||||
public:
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<JSPluralRules> InitializePluralRules(
|
||||
Isolate* isolate, Handle<JSPluralRules> plural_rules,
|
||||
Handle<Object> locales, Handle<Object> options);
|
||||
|
||||
static Handle<JSObject> ResolvedOptions(Isolate* isolate,
|
||||
Handle<JSPluralRules> plural_rules);
|
||||
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<String> ResolvePlural(
|
||||
Isolate* isolate, Handle<JSPluralRules> plural_rules,
|
||||
Handle<Object> number);
|
||||
|
||||
DECL_CAST(JSPluralRules)
|
||||
DECL_PRINTER(JSPluralRules)
|
||||
DECL_VERIFIER(JSPluralRules)
|
||||
|
||||
// Layout description.
|
||||
#define JS_PLURAL_RULES_FIELDS(V) \
|
||||
V(kLocaleOffset, kPointerSize) \
|
||||
/* In the future, this can be an enum, \
|
||||
and not a string. */ \
|
||||
V(kTypeOffset, kPointerSize) \
|
||||
V(kICUPluralRulesOffset, kPointerSize) \
|
||||
V(kICUDecimalFormatOffset, kPointerSize) \
|
||||
/* Total size. */ \
|
||||
V(kSize, 0)
|
||||
|
||||
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_PLURAL_RULES_FIELDS)
|
||||
#undef JS_PLURAL_RULES_FIELDS
|
||||
|
||||
DECL_ACCESSORS(locale, String)
|
||||
DECL_ACCESSORS(type, String)
|
||||
DECL_ACCESSORS(icu_plural_rules, Managed<icu::PluralRules>)
|
||||
DECL_ACCESSORS(icu_decimal_format, Managed<icu::DecimalFormat>)
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSPluralRules);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#include "src/objects/object-macros-undef.h"
|
||||
|
||||
#endif // V8_OBJECTS_JS_PLURAL_RULES_H_
|
@ -20,6 +20,7 @@
|
||||
#include "src/messages.h"
|
||||
#include "src/objects/intl-objects-inl.h"
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/managed.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
#include "src/utils.h"
|
||||
@ -60,9 +61,13 @@ RUNTIME_FUNCTION(Runtime_GetNumberOption) {
|
||||
CONVERT_SMI_ARG_CHECKED(min, 2);
|
||||
CONVERT_SMI_ARG_CHECKED(max, 3);
|
||||
CONVERT_SMI_ARG_CHECKED(fallback, 4);
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate,
|
||||
Intl::GetNumberOption(isolate, options, property, min, max, fallback));
|
||||
|
||||
Maybe<int> num =
|
||||
Intl::GetNumberOption(isolate, options, property, min, max, fallback);
|
||||
if (num.IsNothing()) {
|
||||
return ReadOnlyRoots(isolate).exception();
|
||||
}
|
||||
return Smi::FromInt(num.FromJust());
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefaultNumberOption) {
|
||||
@ -73,9 +78,13 @@ RUNTIME_FUNCTION(Runtime_DefaultNumberOption) {
|
||||
CONVERT_SMI_ARG_CHECKED(max, 2);
|
||||
CONVERT_SMI_ARG_CHECKED(fallback, 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, property, 4);
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate,
|
||||
Intl::DefaultNumberOption(isolate, value, min, max, fallback, property));
|
||||
|
||||
Maybe<int> num =
|
||||
Intl::DefaultNumberOption(isolate, value, min, max, fallback, property);
|
||||
if (num.IsNothing()) {
|
||||
return ReadOnlyRoots(isolate).exception();
|
||||
}
|
||||
return Smi::FromInt(num.FromJust());
|
||||
}
|
||||
|
||||
// ECMA 402 6.2.3
|
||||
@ -275,80 +284,52 @@ RUNTIME_FUNCTION(Runtime_InternalCompare) {
|
||||
return *Intl::InternalCompare(isolate, collator_holder, string1, string2);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CreatePluralRules) {
|
||||
RUNTIME_FUNCTION(Runtime_PluralRulesResolvedOptions) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
DCHECK_EQ(3, args.length());
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, plural_rules_obj, 0);
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
|
||||
// 3. If pr does not have an [[InitializedPluralRules]] internal
|
||||
// slot, throw a TypeError exception.
|
||||
if (!plural_rules_obj->IsJSPluralRules()) {
|
||||
Handle<String> method_str = isolate->factory()->NewStringFromStaticChars(
|
||||
"Intl.PluralRules.prototype.resolvedOptions");
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
method_str, plural_rules_obj));
|
||||
}
|
||||
|
||||
Handle<JSFunction> constructor(
|
||||
isolate->native_context()->intl_plural_rules_function(), isolate);
|
||||
Handle<JSPluralRules> plural_rules =
|
||||
Handle<JSPluralRules>::cast(plural_rules_obj);
|
||||
|
||||
Handle<JSObject> local_object;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, local_object,
|
||||
JSObject::New(constructor, constructor));
|
||||
|
||||
// Set pluralRules as internal field of the resulting JS object.
|
||||
icu::PluralRules* plural_rules;
|
||||
icu::DecimalFormat* decimal_format;
|
||||
PluralRules::InitializePluralRules(isolate, locale, options, resolved,
|
||||
&plural_rules, &decimal_format);
|
||||
CHECK_NOT_NULL(plural_rules);
|
||||
CHECK_NOT_NULL(decimal_format);
|
||||
|
||||
local_object->SetEmbedderField(0, reinterpret_cast<Smi*>(plural_rules));
|
||||
local_object->SetEmbedderField(1, reinterpret_cast<Smi*>(decimal_format));
|
||||
|
||||
Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
|
||||
GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
|
||||
PluralRules::DeletePluralRules,
|
||||
WeakCallbackType::kInternalFields);
|
||||
return *local_object;
|
||||
return *JSPluralRules::ResolvedOptions(isolate, plural_rules);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_PluralRulesSelect) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
DCHECK_EQ(2, args.length());
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, plural_rules_holder, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, plural_rules_obj, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
|
||||
|
||||
icu::PluralRules* plural_rules =
|
||||
PluralRules::UnpackPluralRules(plural_rules_holder);
|
||||
CHECK_NOT_NULL(plural_rules);
|
||||
// 3. If pr does not have an [[InitializedPluralRules]] internal
|
||||
// slot, throw a TypeError exception.
|
||||
if (!plural_rules_obj->IsJSPluralRules()) {
|
||||
Handle<String> method_str = isolate->factory()->NewStringFromStaticChars(
|
||||
"Intl.PluralRules.prototype.select");
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
method_str, plural_rules_obj));
|
||||
}
|
||||
|
||||
icu::DecimalFormat* number_format =
|
||||
PluralRules::UnpackNumberFormat(plural_rules_holder);
|
||||
CHECK_NOT_NULL(number_format);
|
||||
Handle<JSPluralRules> plural_rules =
|
||||
Handle<JSPluralRules>::cast(plural_rules_obj);
|
||||
|
||||
// Currently, PluralRules doesn't implement all the options for rounding that
|
||||
// the Intl spec provides; format and parse the number to round to the
|
||||
// appropriate amount, then apply PluralRules.
|
||||
//
|
||||
// TODO(littledan): If a future ICU version supports an extended API to avoid
|
||||
// this step, then switch to that API. Bug thread:
|
||||
// http://bugs.icu-project.org/trac/ticket/12763
|
||||
icu::UnicodeString rounded_string;
|
||||
number_format->format(number->Number(), rounded_string);
|
||||
// 4. Return ? ResolvePlural(pr, n).
|
||||
|
||||
icu::Formattable formattable;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
number_format->parse(rounded_string, formattable, status);
|
||||
if (!U_SUCCESS(status)) return isolate->ThrowIllegalOperation();
|
||||
|
||||
double rounded = formattable.getDouble(status);
|
||||
if (!U_SUCCESS(status)) return isolate->ThrowIllegalOperation();
|
||||
|
||||
icu::UnicodeString result = plural_rules->select(rounded);
|
||||
return *isolate->factory()
|
||||
->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(result.getBuffer()),
|
||||
result.length()))
|
||||
.ToHandleChecked();
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, JSPluralRules::ResolvePlural(isolate, plural_rules, number));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CreateBreakIterator) {
|
||||
|
@ -214,7 +214,6 @@ namespace internal {
|
||||
F(CreateDateTimeFormat, 3, 1) \
|
||||
F(CreateNumberFormat, 3, 1) \
|
||||
F(DefineWEProperty, 3, 1) \
|
||||
F(CreatePluralRules, 3, 1) \
|
||||
F(CurrencyDigits, 1, 1) \
|
||||
F(DateCacheVersion, 0, 1) \
|
||||
F(DefaultNumberOption, 5, 1) \
|
||||
@ -226,6 +225,7 @@ namespace internal {
|
||||
F(IsInitializedIntlObjectOfType, 2, 1) \
|
||||
F(IsWellFormedCurrencyCode, 1, 1) \
|
||||
F(MarkAsInitializedIntlObjectOfType, 2, 1) \
|
||||
F(PluralRulesResolvedOptions, 1, 1) \
|
||||
F(PluralRulesSelect, 2, 1) \
|
||||
F(StringToLowerCaseIntl, 1, 1) \
|
||||
F(StringToUpperCaseIntl, 1, 1)
|
||||
|
@ -161,14 +161,15 @@ INSTANCE_TYPES = {
|
||||
1083: "JS_DATA_VIEW_TYPE",
|
||||
1084: "JS_INTL_LIST_FORMAT_TYPE",
|
||||
1085: "JS_INTL_LOCALE_TYPE",
|
||||
1086: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
|
||||
1087: "WASM_GLOBAL_TYPE",
|
||||
1088: "WASM_INSTANCE_TYPE",
|
||||
1089: "WASM_MEMORY_TYPE",
|
||||
1090: "WASM_MODULE_TYPE",
|
||||
1091: "WASM_TABLE_TYPE",
|
||||
1092: "JS_BOUND_FUNCTION_TYPE",
|
||||
1093: "JS_FUNCTION_TYPE",
|
||||
1086: "JS_INTL_PLURAL_RULES_TYPE",
|
||||
1087: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
|
||||
1088: "WASM_GLOBAL_TYPE",
|
||||
1089: "WASM_INSTANCE_TYPE",
|
||||
1090: "WASM_MEMORY_TYPE",
|
||||
1091: "WASM_MODULE_TYPE",
|
||||
1092: "WASM_TABLE_TYPE",
|
||||
1093: "JS_BOUND_FUNCTION_TYPE",
|
||||
1094: "JS_FUNCTION_TYPE",
|
||||
}
|
||||
|
||||
# List of known V8 maps.
|
||||
|
Loading…
Reference in New Issue
Block a user