[Intl] Move NumberFormat to JSNumberFormat
Bug: v8:7979 TBR: benedikt@chromium.org Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng;luci.chromium.try:linux_chromium_rel_ng Change-Id: I9d5181c15b56de3bc5288d11bd83e55157c7a610 Reviewed-on: https://chromium-review.googlesource.com/1168518 Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Cr-Commit-Position: refs/heads/master@{#55621}
This commit is contained in:
parent
71f14bd0d3
commit
9c7ec98a90
6
BUILD.gn
6
BUILD.gn
@ -2191,6 +2191,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-number-format-inl.h",
|
||||
"src/objects/js-number-format.cc",
|
||||
"src/objects/js-number-format.h",
|
||||
"src/objects/js-plural-rules-inl.h",
|
||||
"src/objects/js-plural-rules.cc",
|
||||
"src/objects/js-plural-rules.h",
|
||||
@ -2902,6 +2905,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-number-format-inl.h",
|
||||
"src/objects/js-number-format.cc",
|
||||
"src/objects/js-number-format.h",
|
||||
"src/objects/js-plural-rules-inl.h",
|
||||
"src/objects/js-plural-rules.cc",
|
||||
"src/objects/js-plural-rules.h",
|
||||
|
@ -24,16 +24,19 @@
|
||||
#include "src/objects/hash-table-inl.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/js-collator.h"
|
||||
#include "src/objects/js-list-format.h"
|
||||
#include "src/objects/js-locale.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#include "src/objects/js-array-buffer-inl.h"
|
||||
#include "src/objects/js-array-inl.h"
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-collator.h"
|
||||
#include "src/objects/js-list-format.h"
|
||||
#include "src/objects/js-locale.h"
|
||||
#include "src/objects/js-number-format.h"
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#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"
|
||||
@ -2925,10 +2928,14 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
|
||||
{
|
||||
Handle<JSFunction> number_format_constructor = InstallFunction(
|
||||
isolate_, intl, "NumberFormat", JS_OBJECT_TYPE, NumberFormat::kSize,
|
||||
0, factory->the_hole_value(), Builtins::kIllegal);
|
||||
native_context()->set_intl_number_format_function(
|
||||
*number_format_constructor);
|
||||
isolate_, intl, "NumberFormat", JS_INTL_NUMBER_FORMAT_TYPE,
|
||||
JSNumberFormat::kSize, 0, factory->the_hole_value(),
|
||||
Builtins::kNumberFormatConstructor);
|
||||
number_format_constructor->shared()->set_length(0);
|
||||
number_format_constructor->shared()->DontAdaptArguments();
|
||||
InstallWithIntrinsicDefaultProto(
|
||||
isolate_, number_format_constructor,
|
||||
Context::INTL_NUMBER_FORMAT_FUNCTION_INDEX);
|
||||
|
||||
SimpleInstallFunction(
|
||||
isolate(), number_format_constructor, "supportedLocalesOf",
|
||||
|
@ -1336,6 +1336,8 @@ namespace internal {
|
||||
CPP(StringPrototypeToUpperCaseIntl) \
|
||||
/* ES #sec-string.prototype.normalize */ \
|
||||
CPP(StringPrototypeNormalizeIntl) \
|
||||
/* ecma402 #sec-intl.numberformat */ \
|
||||
CPP(NumberFormatConstructor) \
|
||||
/* ecma402 #sec-intl.numberformat.prototype.formattoparts */ \
|
||||
CPP(NumberFormatPrototypeFormatToParts) \
|
||||
/* ecma402 #sec-intl.datetimeformat.prototype.formattoparts */ \
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include "src/objects/js-collator-inl.h"
|
||||
#include "src/objects/js-list-format-inl.h"
|
||||
#include "src/objects/js-locale-inl.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/js-relative-time-format-inl.h"
|
||||
#include "src/property-descriptor.h"
|
||||
|
||||
#include "unicode/datefmt.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
@ -475,16 +477,7 @@ BUILTIN(NumberFormatSupportedLocalesOf) {
|
||||
BUILTIN(NumberFormatPrototypeFormatToParts) {
|
||||
const char* const method = "Intl.NumberFormat.prototype.formatToParts";
|
||||
HandleScope handle_scope(isolate);
|
||||
CHECK_RECEIVER(JSObject, number_format_holder, method);
|
||||
|
||||
if (!Intl::IsObjectOfType(isolate, number_format_holder,
|
||||
Intl::Type::kNumberFormat)) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate,
|
||||
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
isolate->factory()->NewStringFromAsciiChecked(method),
|
||||
number_format_holder));
|
||||
}
|
||||
CHECK_RECEIVER(JSNumberFormat, number_format, method);
|
||||
|
||||
Handle<Object> x;
|
||||
if (args.length() >= 2) {
|
||||
@ -494,12 +487,12 @@ BUILTIN(NumberFormatPrototypeFormatToParts) {
|
||||
x = isolate->factory()->nan_value();
|
||||
}
|
||||
|
||||
icu::DecimalFormat* number_format =
|
||||
NumberFormat::UnpackNumberFormat(number_format_holder);
|
||||
CHECK_NOT_NULL(number_format);
|
||||
icu::NumberFormat* icu_number_format =
|
||||
number_format->icu_number_format()->raw();
|
||||
CHECK_NOT_NULL(icu_number_format);
|
||||
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, FormatNumberToParts(isolate, number_format, x->Number()));
|
||||
isolate, FormatNumberToParts(isolate, icu_number_format, x->Number()));
|
||||
}
|
||||
|
||||
BUILTIN(DateTimeFormatSupportedLocalesOf) {
|
||||
@ -570,6 +563,91 @@ Handle<JSFunction> CreateBoundFunction(Isolate* isolate,
|
||||
return new_bound_function;
|
||||
}
|
||||
} // namespace
|
||||
BUILTIN(NumberFormatConstructor) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<JSReceiver> new_target;
|
||||
// 1. If NewTarget is undefined, let newTarget be the active
|
||||
// function object, else let newTarget be NewTarget.
|
||||
if (args.new_target()->IsUndefined(isolate)) {
|
||||
new_target = args.target();
|
||||
} else {
|
||||
new_target = Handle<JSReceiver>::cast(args.new_target());
|
||||
}
|
||||
|
||||
// [[Construct]]
|
||||
Handle<JSFunction> target = args.target();
|
||||
|
||||
Handle<Object> locales = args.atOrUndefined(isolate, 1);
|
||||
Handle<Object> options = args.atOrUndefined(isolate, 2);
|
||||
|
||||
// 2. Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget,
|
||||
// "%NumberFormatPrototype%", « [[InitializedNumberFormat]], [[Locale]],
|
||||
// [[NumberingSystem]], [[Style]], [[Currency]], [[CurrencyDisplay]],
|
||||
// [[MinimumIntegerDigits]], [[MinimumFractionDigits]],
|
||||
// [[MaximumFractionDigits]], [[MinimumSignificantDigits]],
|
||||
// [[MaximumSignificantDigits]], [[UseGrouping]], [[PositivePattern]],
|
||||
// [[NegativePattern]], [[BoundFormat]] »).
|
||||
|
||||
Handle<JSObject> number_format_obj;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_format_obj,
|
||||
JSObject::New(target, new_target));
|
||||
Handle<JSNumberFormat> number_format =
|
||||
Handle<JSNumberFormat>::cast(number_format_obj);
|
||||
number_format->set_flags(0);
|
||||
|
||||
// 3. Perform ? InitializeNumberFormat(numberFormat, locales, options).
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, number_format,
|
||||
JSNumberFormat::InitializeNumberFormat(isolate, number_format, locales,
|
||||
options));
|
||||
// 4. Let this be the this value.
|
||||
Handle<Object> receiver = args.receiver();
|
||||
|
||||
// 5. If NewTarget is undefined and ? InstanceofOperator(this, %NumberFormat%)
|
||||
// is true, then
|
||||
//
|
||||
// Look up the intrinsic value that has been stored on the context.
|
||||
Handle<Object> number_format_constructor =
|
||||
isolate->intl_number_format_function();
|
||||
|
||||
// Call the instanceof function
|
||||
Handle<Object> is_instance_of_obj;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, is_instance_of_obj,
|
||||
Object::InstanceOf(isolate, receiver, number_format_constructor));
|
||||
|
||||
// Get the boolean value of the result
|
||||
bool is_instance_of = is_instance_of_obj->BooleanValue(isolate);
|
||||
|
||||
if (args.new_target()->IsUndefined(isolate) && is_instance_of) {
|
||||
if (!receiver->IsJSReceiver()) {
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
isolate->factory()->NewStringFromStaticChars(
|
||||
"Intl.NumberFormat"),
|
||||
receiver));
|
||||
}
|
||||
Handle<JSReceiver> rec = Handle<JSReceiver>::cast(receiver);
|
||||
// a. Perform ? DefinePropertyOrThrow(this,
|
||||
// %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: numberFormat,
|
||||
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
|
||||
PropertyDescriptor desc;
|
||||
desc.set_value(number_format);
|
||||
desc.set_writable(false);
|
||||
desc.set_enumerable(false);
|
||||
desc.set_configurable(false);
|
||||
Maybe<bool> success = JSReceiver::DefineOwnProperty(
|
||||
isolate, rec, isolate->factory()->intl_fallback_symbol(), &desc,
|
||||
kThrowOnError);
|
||||
MAYBE_RETURN(success, ReadOnlyRoots(isolate).exception());
|
||||
CHECK(success.FromJust());
|
||||
// b. b. Return this.
|
||||
return *receiver;
|
||||
}
|
||||
|
||||
// 6. Return numberFormat.
|
||||
return *number_format;
|
||||
}
|
||||
|
||||
BUILTIN(NumberFormatPrototypeFormatNumber) {
|
||||
const char* const method = "get Intl.NumberFormat.prototype.format";
|
||||
@ -577,20 +655,14 @@ BUILTIN(NumberFormatPrototypeFormatNumber) {
|
||||
|
||||
// 1. Let nf be the this value.
|
||||
// 2. If Type(nf) is not Object, throw a TypeError exception.
|
||||
CHECK_RECEIVER(JSReceiver, receiver, method);
|
||||
CHECK_RECEIVER(JSObject, format_holder, method);
|
||||
|
||||
// 3. Let nf be ? UnwrapNumberFormat(nf).
|
||||
Handle<JSObject> number_format_holder;
|
||||
Handle<JSNumberFormat> nf;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, number_format_holder,
|
||||
NumberFormat::Unwrap(isolate, receiver, method));
|
||||
isolate, nf, JSNumberFormat::UnwrapNumberFormat(isolate, format_holder));
|
||||
|
||||
DCHECK(Intl::IsObjectOfType(isolate, number_format_holder,
|
||||
Intl::Type::kNumberFormat));
|
||||
|
||||
Handle<Object> bound_format = Handle<Object>(
|
||||
number_format_holder->GetEmbedderField(NumberFormat::kBoundFormatIndex),
|
||||
isolate);
|
||||
Handle<Object> bound_format = Handle<Object>(nf->bound_format(), isolate);
|
||||
|
||||
// 4. If nf.[[BoundFormat]] is undefined, then
|
||||
if (!bound_format->IsUndefined(isolate)) {
|
||||
@ -599,13 +671,11 @@ BUILTIN(NumberFormatPrototypeFormatNumber) {
|
||||
return *bound_format;
|
||||
}
|
||||
|
||||
Handle<JSFunction> new_bound_format_function =
|
||||
CreateBoundFunction(isolate, number_format_holder,
|
||||
Builtins::kNumberFormatInternalFormatNumber, 1);
|
||||
Handle<JSFunction> new_bound_format_function = CreateBoundFunction(
|
||||
isolate, format_holder, Builtins::kNumberFormatInternalFormatNumber, 1);
|
||||
|
||||
// 4. c. Set nf.[[BoundFormat]] to F.
|
||||
number_format_holder->SetEmbedderField(NumberFormat::kBoundFormatIndex,
|
||||
*new_bound_format_function);
|
||||
nf->set_bound_format(*new_bound_format_function);
|
||||
|
||||
// 5. Return nf.[[BoundFormat]].
|
||||
return *new_bound_format_function;
|
||||
@ -617,15 +687,12 @@ BUILTIN(NumberFormatInternalFormatNumber) {
|
||||
Handle<Context> context = Handle<Context>(isolate->context(), isolate);
|
||||
|
||||
// 1. Let nf be F.[[NumberFormat]].
|
||||
Handle<JSObject> number_format_holder = Handle<JSObject>(
|
||||
JSObject::cast(context->get(
|
||||
static_cast<int>(Intl::BoundFunctionContextSlot::kBoundFunction))),
|
||||
isolate);
|
||||
|
||||
// 2. Assert: Type(nf) is Object and nf has an
|
||||
// [[InitializedNumberFormat]] internal slot.
|
||||
DCHECK(Intl::IsObjectOfType(isolate, number_format_holder,
|
||||
Intl::Type::kNumberFormat));
|
||||
Handle<JSNumberFormat> number_format = Handle<JSNumberFormat>(
|
||||
JSNumberFormat::cast(
|
||||
context->get(JSNumberFormat::ContextSlot::kNumberFormat)),
|
||||
isolate);
|
||||
|
||||
// 3. If value is not provided, let value be undefined.
|
||||
Handle<Object> value = args.atOrUndefined(isolate, 1);
|
||||
@ -642,8 +709,8 @@ BUILTIN(NumberFormatInternalFormatNumber) {
|
||||
|
||||
double number = number_obj->Number();
|
||||
// Return FormatNumber(nf, x).
|
||||
RETURN_RESULT_OR_FAILURE(isolate, NumberFormat::FormatNumber(
|
||||
isolate, number_format_holder, number));
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, JSNumberFormat::FormatNumber(isolate, number_format, number));
|
||||
}
|
||||
|
||||
BUILTIN(DateTimeFormatPrototypeFormat) {
|
||||
|
@ -211,6 +211,7 @@ Type::bitset BitsetType::Lub(HeapObjectType const& type) {
|
||||
case JS_INTL_DATE_TIME_FORMAT_TYPE:
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
@ -41,6 +41,7 @@
|
||||
V(cell_value_string, "%cell_value") \
|
||||
V(char_at_string, "CharAt") \
|
||||
V(closure_string, "(closure)") \
|
||||
V(code_string, "code") \
|
||||
V(collation_string, "collation") \
|
||||
V(column_string, "column") \
|
||||
V(CompileError_string, "CompileError") \
|
||||
@ -50,6 +51,7 @@
|
||||
V(conjunction_string, "conjunction") \
|
||||
V(create_string, "create") \
|
||||
V(currency_string, "currency") \
|
||||
V(currencyDisplay_string, "currencyDisplay") \
|
||||
V(Date_string, "Date") \
|
||||
V(date_to_string, "[object Date]") \
|
||||
V(day_string, "day") \
|
||||
@ -128,9 +130,14 @@
|
||||
V(long_string, "long") \
|
||||
V(Map_string, "Map") \
|
||||
V(MapIterator_string, "Map Iterator") \
|
||||
V(maximumFractionDigits_string, "maximumFractionDigits") \
|
||||
V(maximumSignificantDigits_string, "maximumSignificantDigits") \
|
||||
V(message_string, "message") \
|
||||
V(minus_Infinity_string, "-Infinity") \
|
||||
V(minus_zero_string, "-0") \
|
||||
V(minimumFractionDigits_string, "minimumFractionDigits") \
|
||||
V(minimumIntegerDigits_string, "minimumIntegerDigits") \
|
||||
V(minimumSignificantDigits_string, "minimumSignificantDigits") \
|
||||
V(minusSign_string, "minusSign") \
|
||||
V(minute_string, "minute") \
|
||||
V(Module_string, "Module") \
|
||||
@ -153,6 +160,7 @@
|
||||
V(Number_string, "Number") \
|
||||
V(number_string, "number") \
|
||||
V(number_to_string, "[object Number]") \
|
||||
V(numberingSystem_string, "numberingSystem") \
|
||||
V(numeric_string, "numeric") \
|
||||
V(Object_string, "Object") \
|
||||
V(object_string, "object") \
|
||||
@ -160,6 +168,7 @@
|
||||
V(ok, "ok") \
|
||||
V(one_string, "1") \
|
||||
V(ownKeys_string, "ownKeys") \
|
||||
V(percent_string, "percent") \
|
||||
V(percentSign_string, "percentSign") \
|
||||
V(plusSign_string, "plusSign") \
|
||||
V(position_string, "position") \
|
||||
@ -231,6 +240,7 @@
|
||||
V(usage_string, "usage") \
|
||||
V(use_asm_string, "use asm") \
|
||||
V(use_strict_string, "use strict") \
|
||||
V(useGrouping_string, "useGrouping") \
|
||||
V(value_string, "value") \
|
||||
V(valueOf_string, "valueOf") \
|
||||
V(values_string, "values") \
|
||||
|
177
src/js/intl.js
177
src/js/intl.js
@ -584,188 +584,13 @@ DEFINE_METHOD(
|
||||
}
|
||||
);
|
||||
|
||||
// ECMA 402 #sec-setnfdigitoptions
|
||||
// SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault )
|
||||
function SetNumberFormatDigitOptions(internalOptions, options,
|
||||
mnfdDefault, mxfdDefault) {
|
||||
// Digit ranges.
|
||||
var mnid = %GetNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
|
||||
%DefineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
|
||||
|
||||
var mnfd = %GetNumberOption(options, 'minimumFractionDigits', 0, 20,
|
||||
mnfdDefault);
|
||||
%DefineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
|
||||
|
||||
var mxfdActualDefault = MathMax(mnfd, mxfdDefault);
|
||||
|
||||
var mxfd = %GetNumberOption(options, 'maximumFractionDigits', mnfd, 20,
|
||||
mxfdActualDefault);
|
||||
|
||||
%DefineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
|
||||
|
||||
var mnsd = options['minimumSignificantDigits'];
|
||||
var mxsd = options['maximumSignificantDigits'];
|
||||
if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
|
||||
mnsd = %DefaultNumberOption(mnsd, 1, 21, 1, 'minimumSignificantDigits');
|
||||
%DefineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
|
||||
|
||||
mxsd = %DefaultNumberOption(mxsd, mnsd, 21, 21, 'maximumSignificantDigits');
|
||||
%DefineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the given object so it's a valid NumberFormat instance.
|
||||
* Useful for subclassing.
|
||||
*/
|
||||
function CreateNumberFormat(locales, options) {
|
||||
if (IS_UNDEFINED(options)) {
|
||||
options = {__proto__: null};
|
||||
} else {
|
||||
options = TO_OBJECT(options);
|
||||
}
|
||||
|
||||
var getOption = getGetOption(options, 'numberformat');
|
||||
|
||||
var locale = resolveLocale('numberformat', locales, options);
|
||||
|
||||
var internalOptions = {__proto__: null};
|
||||
%DefineWEProperty(internalOptions, 'style', getOption(
|
||||
'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
|
||||
|
||||
var currency = getOption('currency', 'string');
|
||||
if (!IS_UNDEFINED(currency) && !%IsWellFormedCurrencyCode(currency)) {
|
||||
throw %make_range_error(kInvalidCurrencyCode, currency);
|
||||
}
|
||||
|
||||
if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) {
|
||||
throw %make_type_error(kCurrencyCode);
|
||||
}
|
||||
|
||||
var mnfdDefault, mxfdDefault;
|
||||
|
||||
var currencyDisplay = getOption(
|
||||
'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
|
||||
if (internalOptions.style === 'currency') {
|
||||
%DefineWEProperty(internalOptions, 'currency', %StringToUpperCaseIntl(currency));
|
||||
%DefineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
|
||||
|
||||
mnfdDefault = mxfdDefault = %CurrencyDigits(internalOptions.currency);
|
||||
} else {
|
||||
mnfdDefault = 0;
|
||||
mxfdDefault = internalOptions.style === 'percent' ? 0 : 3;
|
||||
}
|
||||
|
||||
SetNumberFormatDigitOptions(internalOptions, options, mnfdDefault,
|
||||
mxfdDefault);
|
||||
|
||||
// Grouping.
|
||||
%DefineWEProperty(internalOptions, 'useGrouping', getOption(
|
||||
'useGrouping', 'boolean', UNDEFINED, true));
|
||||
|
||||
// ICU prefers options to be passed using -u- extension key/values for
|
||||
// number format, so we need to build that.
|
||||
var extensionMap = %ParseExtension(locale.extension);
|
||||
|
||||
/**
|
||||
* Map of Unicode extensions to option properties, and their values and types,
|
||||
* for a number format.
|
||||
*/
|
||||
var NUMBER_FORMAT_KEY_MAP = {
|
||||
__proto__: null,
|
||||
'nu': {__proto__: null, 'property': UNDEFINED, 'type': 'string'}
|
||||
};
|
||||
|
||||
var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
|
||||
getOption, internalOptions);
|
||||
|
||||
var requestedLocale = locale.locale + extension;
|
||||
var resolved = %object_define_properties({__proto__: null}, {
|
||||
currency: {writable: true},
|
||||
currencyDisplay: {writable: true},
|
||||
locale: {writable: true},
|
||||
maximumFractionDigits: {writable: true},
|
||||
minimumFractionDigits: {writable: true},
|
||||
minimumIntegerDigits: {writable: true},
|
||||
numberingSystem: {writable: true},
|
||||
requestedLocale: {value: requestedLocale, writable: true},
|
||||
style: {value: internalOptions.style, writable: true},
|
||||
useGrouping: {writable: true}
|
||||
});
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'minimumSignificantDigits')) {
|
||||
%DefineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
if (HAS_OWN_PROPERTY(internalOptions, 'maximumSignificantDigits')) {
|
||||
%DefineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
|
||||
}
|
||||
var numberFormat = %CreateNumberFormat(requestedLocale, internalOptions,
|
||||
resolved);
|
||||
|
||||
if (internalOptions.style === 'currency') {
|
||||
%object_define_property(resolved, 'currencyDisplay',
|
||||
{value: currencyDisplay, writable: true});
|
||||
}
|
||||
|
||||
%MarkAsInitializedIntlObjectOfType(numberFormat, NUMBER_FORMAT_TYPE);
|
||||
numberFormat[resolvedSymbol] = resolved;
|
||||
|
||||
return numberFormat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs Intl.NumberFormat object given optional locales and options
|
||||
* parameters.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function NumberFormatConstructor() {
|
||||
return IntlConstruct(this, GlobalIntlNumberFormat, CreateNumberFormat,
|
||||
new.target, arguments, true);
|
||||
}
|
||||
%SetCode(GlobalIntlNumberFormat, NumberFormatConstructor);
|
||||
|
||||
|
||||
/**
|
||||
* NumberFormat resolvedOptions method.
|
||||
*/
|
||||
DEFINE_METHOD(
|
||||
GlobalIntlNumberFormat.prototype,
|
||||
resolvedOptions() {
|
||||
var methodName = 'resolvedOptions';
|
||||
if(!IS_RECEIVER(this)) {
|
||||
throw %make_type_error(kIncompatibleMethodReceiver, methodName, this);
|
||||
}
|
||||
var format = %IntlUnwrapReceiver(this, NUMBER_FORMAT_TYPE,
|
||||
GlobalIntlNumberFormat,
|
||||
methodName, true);
|
||||
var result = {
|
||||
locale: format[resolvedSymbol].locale,
|
||||
numberingSystem: format[resolvedSymbol].numberingSystem,
|
||||
style: format[resolvedSymbol].style,
|
||||
useGrouping: format[resolvedSymbol].useGrouping,
|
||||
minimumIntegerDigits: format[resolvedSymbol].minimumIntegerDigits,
|
||||
minimumFractionDigits: format[resolvedSymbol].minimumFractionDigits,
|
||||
maximumFractionDigits: format[resolvedSymbol].maximumFractionDigits,
|
||||
};
|
||||
|
||||
if (result.style === 'currency') {
|
||||
defineWECProperty(result, 'currency', format[resolvedSymbol].currency);
|
||||
defineWECProperty(result, 'currencyDisplay',
|
||||
format[resolvedSymbol].currencyDisplay);
|
||||
}
|
||||
|
||||
if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'minimumSignificantDigits')) {
|
||||
defineWECProperty(result, 'minimumSignificantDigits',
|
||||
format[resolvedSymbol].minimumSignificantDigits);
|
||||
}
|
||||
|
||||
if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'maximumSignificantDigits')) {
|
||||
defineWECProperty(result, 'maximumSignificantDigits',
|
||||
format[resolvedSymbol].maximumSignificantDigits);
|
||||
}
|
||||
|
||||
return result;
|
||||
return %NumberFormatResolvedOptions(this);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -730,6 +730,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
|
||||
case JS_INTL_DATE_TIME_FORMAT_TYPE:
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
|
@ -30,11 +30,12 @@
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-list-format-inl.h"
|
||||
#include "src/objects/js-locale-inl.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#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"
|
||||
@ -371,6 +372,9 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
JSLocale::cast(this)->JSLocaleVerify(isolate);
|
||||
break;
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
JSNumberFormat::cast(this)->JSNumberFormatVerify(isolate);
|
||||
break;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
JSPluralRules::cast(this)->JSPluralRulesVerify(isolate);
|
||||
break;
|
||||
@ -1911,6 +1915,15 @@ void JSLocale::JSLocaleVerify(Isolate* isolate) {
|
||||
VerifyObjectField(isolate, kNumberingSystemOffset);
|
||||
}
|
||||
|
||||
void JSNumberFormat::JSNumberFormatVerify(Isolate* isolate) {
|
||||
CHECK(IsJSNumberFormat());
|
||||
JSObjectVerify(isolate);
|
||||
VerifyObjectField(isolate, kLocaleOffset);
|
||||
VerifyObjectField(isolate, kIcuNumberFormatOffset);
|
||||
VerifyObjectField(isolate, kBoundFormatOffset);
|
||||
VerifyObjectField(isolate, kFlagsOffset);
|
||||
}
|
||||
|
||||
void JSPluralRules::JSPluralRulesVerify(Isolate* isolate) {
|
||||
CHECK(IsJSPluralRules());
|
||||
JSObjectVerify(isolate);
|
||||
|
@ -220,6 +220,7 @@ namespace internal {
|
||||
V(JS_INTL_DATE_TIME_FORMAT_TYPE) \
|
||||
V(JS_INTL_LIST_FORMAT_TYPE) \
|
||||
V(JS_INTL_LOCALE_TYPE) \
|
||||
V(JS_INTL_NUMBER_FORMAT_TYPE) \
|
||||
V(JS_INTL_PLURAL_RULES_TYPE) \
|
||||
V(JS_INTL_RELATIVE_TIME_FORMAT_TYPE) \
|
||||
INSTANCE_TYPE_LIST_AFTER_INTL(V)
|
||||
|
@ -30,11 +30,12 @@
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-list-format-inl.h"
|
||||
#include "src/objects/js-locale-inl.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#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"
|
||||
@ -324,6 +325,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
JSLocale::cast(this)->JSLocalePrint(os);
|
||||
break;
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
JSNumberFormat::cast(this)->JSNumberFormatPrint(os);
|
||||
break;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
JSPluralRules::cast(this)->JSPluralRulesPrint(os);
|
||||
break;
|
||||
@ -1995,6 +1999,16 @@ void JSLocale::JSLocalePrint(std::ostream& os) { // NOLINT
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void JSNumberFormat::JSNumberFormatPrint(std::ostream& os) { // NOLINT
|
||||
JSObjectPrintHeader(os, this, "JSNumberFormat");
|
||||
os << "\n - locale: " << Brief(locale());
|
||||
os << "\n - icu_number_format: " << Brief(icu_number_format());
|
||||
os << "\n - bound_format: " << Brief(bound_format());
|
||||
os << "\n - style: " << StyleAsString();
|
||||
os << "\n - currency_display: " << CurrencyDisplayAsString();
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void JSPluralRules::JSPluralRulesPrint(std::ostream& os) { // NOLINT
|
||||
HeapObject::PrintHeader(os, "JSPluralRules");
|
||||
JSObjectPrint(os);
|
||||
|
@ -71,11 +71,12 @@
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#include "src/objects/js-list-format.h"
|
||||
#include "src/objects/js-locale.h"
|
||||
#include "src/objects/js-number-format.h"
|
||||
#include "src/objects/js-plural-rules.h"
|
||||
#endif // V8_INTL_SUPPORT
|
||||
#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"
|
||||
@ -1464,6 +1465,8 @@ int JSObject::GetHeaderSize(InstanceType type,
|
||||
return JSListFormat::kSize;
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
return JSLocale::kSize;
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
return JSNumberFormat::kSize;
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
return JSPluralRules::kSize;
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
@ -3207,6 +3210,7 @@ VisitorId Map::GetVisitorId(Map* map) {
|
||||
case JS_INTL_DATE_TIME_FORMAT_TYPE:
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_LOCALE_TYPE:
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif // V8_INTL_SUPPORT
|
||||
@ -13152,6 +13156,7 @@ bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
|
||||
case JS_INTL_COLLATOR_TYPE:
|
||||
case JS_INTL_DATE_TIME_FORMAT_TYPE:
|
||||
case JS_INTL_LIST_FORMAT_TYPE:
|
||||
case JS_INTL_NUMBER_FORMAT_TYPE:
|
||||
case JS_INTL_PLURAL_RULES_TYPE:
|
||||
case JS_INTL_RELATIVE_TIME_FORMAT_TYPE:
|
||||
#endif
|
||||
|
@ -79,6 +79,7 @@
|
||||
// - JSDateTimeFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - JSListFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - JSLocale // If V8_INTL_SUPPORT enabled.
|
||||
// - JSNumberFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - JSPluralRules // If V8_INTL_SUPPORT enabled.
|
||||
// - JSRelativeTimeFormat // If V8_INTL_SUPPORT enabled.
|
||||
// - WasmGlobalObject
|
||||
@ -587,6 +588,7 @@ enum InstanceType : uint16_t {
|
||||
JS_INTL_DATE_TIME_FORMAT_TYPE,
|
||||
JS_INTL_LIST_FORMAT_TYPE,
|
||||
JS_INTL_LOCALE_TYPE,
|
||||
JS_INTL_NUMBER_FORMAT_TYPE,
|
||||
JS_INTL_PLURAL_RULES_TYPE,
|
||||
JS_INTL_RELATIVE_TIME_FORMAT_TYPE,
|
||||
#endif // V8_INTL_SUPPORT
|
||||
@ -706,6 +708,7 @@ class JSCollator;
|
||||
class JSDateTimeFormat;
|
||||
class JSListFormat;
|
||||
class JSLocale;
|
||||
class JSNumberFormat;
|
||||
class JSPluralRules;
|
||||
class JSRelativeTimeFormat;
|
||||
#endif // V8_INTL_SUPPORT
|
||||
@ -918,6 +921,7 @@ class ZoneForwardList;
|
||||
V(JSDateTimeFormat) \
|
||||
V(JSListFormat) \
|
||||
V(JSLocale) \
|
||||
V(JSNumberFormat) \
|
||||
V(JSPluralRules) \
|
||||
V(JSRelativeTimeFormat)
|
||||
#else
|
||||
@ -1039,6 +1043,7 @@ class ZoneForwardList;
|
||||
V(JSDateTimeFormat, JS_INTL_DATE_TIME_FORMAT_TYPE) \
|
||||
V(JSListFormat, JS_INTL_LIST_FORMAT_TYPE) \
|
||||
V(JSLocale, JS_INTL_LOCALE_TYPE) \
|
||||
V(JSNumberFormat, JS_INTL_NUMBER_FORMAT_TYPE) \
|
||||
V(JSPluralRules, JS_INTL_PLURAL_RULES_TYPE) \
|
||||
V(JSRelativeTimeFormat, JS_INTL_RELATIVE_TIME_FORMAT_TYPE)
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/js-collator-inl.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "src/objects/managed.h"
|
||||
#include "src/objects/string.h"
|
||||
#include "src/property-descriptor.h"
|
||||
@ -58,8 +59,21 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
std::string Intl::GetNumberingSystem(const icu::Locale& icu_locale) {
|
||||
// Ugly hack. ICU doesn't expose numbering system in any way, so we have
|
||||
// to assume that for given locale NumberingSystem constructor produces the
|
||||
// same digits as NumberFormat/Calendar would.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::unique_ptr<icu::NumberingSystem> numbering_system(
|
||||
icu::NumberingSystem::createInstance(icu_locale, status));
|
||||
std::string value;
|
||||
if (U_SUCCESS(status)) {
|
||||
value = numbering_system->getName();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ExtractStringSetting(Isolate* isolate, Handle<JSObject> options,
|
||||
const char* key, icu::UnicodeString* setting) {
|
||||
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
||||
@ -75,29 +89,6 @@ bool ExtractStringSetting(Isolate* isolate, Handle<JSObject> options,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExtractIntegerSetting(Isolate* isolate, Handle<JSObject> options,
|
||||
const char* key, int32_t* value) {
|
||||
Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
|
||||
Handle<Object> object =
|
||||
JSReceiver::GetProperty(isolate, options, str).ToHandleChecked();
|
||||
if (object->IsNumber()) {
|
||||
return object->ToInt32(value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExtractBooleanSetting(Isolate* isolate, Handle<JSObject> options,
|
||||
const char* key, bool* value) {
|
||||
Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
|
||||
Handle<Object> object =
|
||||
JSReceiver::GetProperty(isolate, options, str).ToHandleChecked();
|
||||
if (object->IsBoolean()) {
|
||||
*value = object->BooleanValue(isolate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
icu::SimpleDateFormat* CreateICUDateFormat(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options) {
|
||||
@ -207,238 +198,6 @@ void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale,
|
||||
}
|
||||
}
|
||||
|
||||
void SetNumericSettings(Isolate* isolate, icu::DecimalFormat* number_format,
|
||||
Handle<JSObject> options) {
|
||||
int32_t digits;
|
||||
if (ExtractIntegerSetting(isolate, options, "minimumIntegerDigits",
|
||||
&digits)) {
|
||||
number_format->setMinimumIntegerDigits(digits);
|
||||
}
|
||||
|
||||
if (ExtractIntegerSetting(isolate, options, "minimumFractionDigits",
|
||||
&digits)) {
|
||||
number_format->setMinimumFractionDigits(digits);
|
||||
}
|
||||
|
||||
if (ExtractIntegerSetting(isolate, options, "maximumFractionDigits",
|
||||
&digits)) {
|
||||
number_format->setMaximumFractionDigits(digits);
|
||||
}
|
||||
|
||||
bool significant_digits_used = false;
|
||||
if (ExtractIntegerSetting(isolate, options, "minimumSignificantDigits",
|
||||
&digits)) {
|
||||
number_format->setMinimumSignificantDigits(digits);
|
||||
significant_digits_used = true;
|
||||
}
|
||||
|
||||
if (ExtractIntegerSetting(isolate, options, "maximumSignificantDigits",
|
||||
&digits)) {
|
||||
number_format->setMaximumSignificantDigits(digits);
|
||||
significant_digits_used = true;
|
||||
}
|
||||
|
||||
number_format->setSignificantDigitsUsed(significant_digits_used);
|
||||
|
||||
number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
|
||||
}
|
||||
|
||||
icu::DecimalFormat* CreateICUNumberFormat(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options) {
|
||||
// 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;
|
||||
icu::DecimalFormat* number_format = nullptr;
|
||||
icu::UnicodeString style;
|
||||
icu::UnicodeString currency;
|
||||
if (ExtractStringSetting(isolate, options, "style", &style)) {
|
||||
if (style == UNICODE_STRING_SIMPLE("currency")) {
|
||||
icu::UnicodeString display;
|
||||
ExtractStringSetting(isolate, options, "currency", ¤cy);
|
||||
ExtractStringSetting(isolate, options, "currencyDisplay", &display);
|
||||
|
||||
#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
|
||||
icu::NumberFormat::EStyles format_style;
|
||||
if (display == UNICODE_STRING_SIMPLE("code")) {
|
||||
format_style = icu::NumberFormat::kIsoCurrencyStyle;
|
||||
} else if (display == UNICODE_STRING_SIMPLE("name")) {
|
||||
format_style = icu::NumberFormat::kPluralCurrencyStyle;
|
||||
} else {
|
||||
format_style = icu::NumberFormat::kCurrencyStyle;
|
||||
}
|
||||
#else // ICU version is 4.8 or above (we ignore versions below 4.0).
|
||||
UNumberFormatStyle format_style;
|
||||
if (display == UNICODE_STRING_SIMPLE("code")) {
|
||||
format_style = UNUM_CURRENCY_ISO;
|
||||
} else if (display == UNICODE_STRING_SIMPLE("name")) {
|
||||
format_style = UNUM_CURRENCY_PLURAL;
|
||||
} else {
|
||||
format_style = UNUM_CURRENCY;
|
||||
}
|
||||
#endif
|
||||
|
||||
number_format = static_cast<icu::DecimalFormat*>(
|
||||
icu::NumberFormat::createInstance(icu_locale, format_style, status));
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
delete number_format;
|
||||
return nullptr;
|
||||
}
|
||||
} else if (style == UNICODE_STRING_SIMPLE("percent")) {
|
||||
number_format = static_cast<icu::DecimalFormat*>(
|
||||
icu::NumberFormat::createPercentInstance(icu_locale, status));
|
||||
if (U_FAILURE(status)) {
|
||||
delete number_format;
|
||||
return nullptr;
|
||||
}
|
||||
// Make sure 1.1% doesn't go into 2%.
|
||||
number_format->setMinimumFractionDigits(1);
|
||||
} else {
|
||||
// Make a decimal instance by default.
|
||||
number_format = static_cast<icu::DecimalFormat*>(
|
||||
icu::NumberFormat::createInstance(icu_locale, status));
|
||||
}
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
delete number_format;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set all options.
|
||||
if (!currency.isEmpty()) {
|
||||
number_format->setCurrency(currency.getBuffer(), status);
|
||||
}
|
||||
|
||||
SetNumericSettings(isolate, number_format, options);
|
||||
|
||||
bool grouping;
|
||||
if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
|
||||
number_format->setGroupingUsed(grouping);
|
||||
}
|
||||
|
||||
return number_format;
|
||||
}
|
||||
|
||||
void SetResolvedNumericSettings(Isolate* isolate, const icu::Locale& icu_locale,
|
||||
icu::DecimalFormat* number_format,
|
||||
Handle<JSObject> resolved) {
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("minimumIntegerDigits"),
|
||||
factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("minimumFractionDigits"),
|
||||
factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("maximumFractionDigits"),
|
||||
factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
|
||||
Handle<String> key =
|
||||
factory->NewStringFromStaticChars("minimumSignificantDigits");
|
||||
Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key);
|
||||
CHECK(maybe.IsJust());
|
||||
if (maybe.FromJust()) {
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("minimumSignificantDigits"),
|
||||
factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
}
|
||||
|
||||
key = factory->NewStringFromStaticChars("maximumSignificantDigits");
|
||||
maybe = JSReceiver::HasOwnProperty(resolved, key);
|
||||
CHECK(maybe.IsJust());
|
||||
if (maybe.FromJust()) {
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved,
|
||||
factory->NewStringFromStaticChars("maximumSignificantDigits"),
|
||||
factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
}
|
||||
|
||||
// Set the locale
|
||||
char result[ULOC_FULLNAME_CAPACITY];
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
|
||||
FALSE, &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved, factory->NewStringFromStaticChars("locale"),
|
||||
factory->NewStringFromAsciiChecked(result), LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
} else {
|
||||
// This would never happen, since we got the locale from ICU.
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved, factory->NewStringFromStaticChars("locale"),
|
||||
factory->NewStringFromStaticChars("und"), LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
}
|
||||
}
|
||||
|
||||
void SetResolvedNumberSettings(Isolate* isolate, const icu::Locale& icu_locale,
|
||||
icu::DecimalFormat* number_format,
|
||||
Handle<JSObject> resolved) {
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
// Set resolved currency code in options.currency if not empty.
|
||||
icu::UnicodeString currency(number_format->getCurrency());
|
||||
if (!currency.isEmpty()) {
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved, factory->NewStringFromStaticChars("currency"),
|
||||
factory
|
||||
->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(currency.getBuffer()),
|
||||
currency.length()))
|
||||
.ToHandleChecked(),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
}
|
||||
|
||||
// Ugly hack. ICU doesn't expose numbering system in any way, so we have
|
||||
// to assume that for given locale NumberingSystem constructor produces the
|
||||
// same digits as NumberFormat/Calendar would.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu::NumberingSystem* numbering_system =
|
||||
icu::NumberingSystem::createInstance(icu_locale, status);
|
||||
if (U_SUCCESS(status)) {
|
||||
const char* ns = numbering_system->getName();
|
||||
JSObject::SetProperty(
|
||||
isolate, resolved, factory->NewStringFromStaticChars("numberingSystem"),
|
||||
factory->NewStringFromAsciiChecked(ns), LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
} else {
|
||||
JSObject::SetProperty(isolate, resolved,
|
||||
factory->NewStringFromStaticChars("numberingSystem"),
|
||||
factory->undefined_value(), LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
}
|
||||
delete numbering_system;
|
||||
|
||||
JSObject::SetProperty(isolate, resolved,
|
||||
factory->NewStringFromStaticChars("useGrouping"),
|
||||
factory->ToBoolean(number_format->isGroupingUsed()),
|
||||
LanguageMode::kSloppy)
|
||||
.Assert();
|
||||
|
||||
SetResolvedNumericSettings(isolate, icu_locale, number_format, resolved);
|
||||
}
|
||||
|
||||
icu::BreakIterator* CreateICUBreakIterator(Isolate* isolate,
|
||||
const icu::Locale& icu_locale,
|
||||
Handle<JSObject> options) {
|
||||
@ -702,45 +461,6 @@ MaybeHandle<String> DateFormat::ToLocaleDateTime(
|
||||
return DateFormat::FormatDateTime(isolate, date_format, x);
|
||||
}
|
||||
|
||||
icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
icu::Locale icu_locale = Intl::CreateICULocale(isolate, locale);
|
||||
DCHECK(!icu_locale.isBogus());
|
||||
|
||||
icu::DecimalFormat* number_format =
|
||||
CreateICUNumberFormat(isolate, icu_locale, options);
|
||||
if (!number_format) {
|
||||
// Remove extensions and try again.
|
||||
icu::Locale no_extension_locale(icu_locale.getBaseName());
|
||||
number_format =
|
||||
CreateICUNumberFormat(isolate, no_extension_locale, options);
|
||||
|
||||
if (!number_format) {
|
||||
FATAL("Failed to create ICU number format, are ICU data files missing?");
|
||||
}
|
||||
|
||||
// Set resolved settings (pattern, numbering system).
|
||||
SetResolvedNumberSettings(isolate, no_extension_locale, number_format,
|
||||
resolved);
|
||||
} else {
|
||||
SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
|
||||
}
|
||||
|
||||
CHECK_NOT_NULL(number_format);
|
||||
return number_format;
|
||||
}
|
||||
|
||||
icu::DecimalFormat* NumberFormat::UnpackNumberFormat(Handle<JSObject> obj) {
|
||||
return reinterpret_cast<icu::DecimalFormat*>(
|
||||
obj->GetEmbedderField(NumberFormat::kDecimalFormatIndex));
|
||||
}
|
||||
|
||||
void NumberFormat::DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data) {
|
||||
delete reinterpret_cast<icu::DecimalFormat*>(data.GetInternalField(0));
|
||||
GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
|
||||
}
|
||||
|
||||
icu::BreakIterator* V8BreakIterator::InitializeBreakIterator(
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
@ -1202,7 +922,7 @@ MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate,
|
||||
JSObject);
|
||||
}
|
||||
|
||||
// Collator has been ported to use regular instance types. We
|
||||
// Collator and NumberFormat has been ported to use regular instance types. We
|
||||
// shouldn't be using Intl::IsObjectOfType anymore.
|
||||
if (type == Intl::Type::kCollator) {
|
||||
if (!receiver->IsJSCollator()) {
|
||||
@ -1213,9 +933,19 @@ MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate,
|
||||
JSObject);
|
||||
}
|
||||
return Handle<JSCollator>::cast(receiver);
|
||||
} else if (type == Intl::Type::kNumberFormat) {
|
||||
if (!receiver->IsJSNumberFormat()) {
|
||||
// 3. a. Throw a TypeError exception.
|
||||
THROW_NEW_ERROR(isolate,
|
||||
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
method_name, receiver),
|
||||
JSObject);
|
||||
}
|
||||
return Handle<JSNumberFormat>::cast(receiver);
|
||||
}
|
||||
|
||||
DCHECK_NE(type, Intl::Type::kCollator);
|
||||
DCHECK_NE(type, Intl::Type::kNumberFormat);
|
||||
// 3. If Type(new_receiver) is not Object or nf does not have an
|
||||
// [[Initialized...]] internal slot, then
|
||||
if (!Intl::IsObjectOfType(isolate, new_receiver, type)) {
|
||||
@ -1231,34 +961,7 @@ MaybeHandle<JSObject> Intl::UnwrapReceiver(Isolate* isolate,
|
||||
return Handle<JSObject>::cast(new_receiver);
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> NumberFormat::Unwrap(Isolate* isolate,
|
||||
Handle<JSReceiver> receiver,
|
||||
const char* method_name) {
|
||||
Handle<Context> native_context =
|
||||
Handle<Context>(isolate->context()->native_context(), isolate);
|
||||
Handle<JSFunction> constructor = Handle<JSFunction>(
|
||||
JSFunction::cast(native_context->intl_number_format_function()), isolate);
|
||||
Handle<String> method_name_str =
|
||||
isolate->factory()->NewStringFromAsciiChecked(method_name);
|
||||
|
||||
return Intl::UnwrapReceiver(isolate, receiver, constructor,
|
||||
Intl::Type::kNumberFormat, method_name_str, true);
|
||||
}
|
||||
|
||||
MaybeHandle<String> NumberFormat::FormatNumber(
|
||||
Isolate* isolate, Handle<JSObject> number_format_holder, double value) {
|
||||
icu::DecimalFormat* number_format =
|
||||
NumberFormat::UnpackNumberFormat(number_format_holder);
|
||||
CHECK_NOT_NULL(number_format);
|
||||
|
||||
icu::UnicodeString result;
|
||||
number_format->format(value, result);
|
||||
|
||||
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
|
||||
}
|
||||
|
||||
void Intl::DefineWEProperty(Isolate* isolate, Handle<JSObject> target,
|
||||
void Intl::DefineWEProperty(Isolate* isolate, Handle<JSReceiver> target,
|
||||
Handle<Name> key, Handle<Object> value) {
|
||||
PropertyDescriptor desc;
|
||||
desc.set_writable(true);
|
||||
@ -1797,48 +1500,6 @@ Maybe<std::vector<std::string>> Intl::CanonicalizeLocaleList(
|
||||
return Just(seen);
|
||||
}
|
||||
|
||||
// ecma-402/#sec-currencydigits
|
||||
Handle<Smi> Intl::CurrencyDigits(Isolate* isolate, Handle<String> currency) {
|
||||
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
||||
v8::String::Value currency_string(v8_isolate, v8::Utils::ToLocal(currency));
|
||||
CHECK_NOT_NULL(*currency_string);
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
|
||||
reinterpret_cast<const UChar*>(*currency_string), &status);
|
||||
// For missing currency codes, default to the most common, 2
|
||||
if (U_FAILURE(status)) fraction_digits = 2;
|
||||
return Handle<Smi>(Smi::FromInt(fraction_digits), isolate);
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> Intl::CreateNumberFormat(Isolate* isolate,
|
||||
Handle<String> locale,
|
||||
Handle<JSObject> options,
|
||||
Handle<JSObject> resolved) {
|
||||
Handle<JSFunction> constructor(
|
||||
isolate->native_context()->intl_number_format_function(), isolate);
|
||||
|
||||
Handle<JSObject> local_object;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, local_object,
|
||||
JSObject::New(constructor, constructor), JSObject);
|
||||
|
||||
// Set number formatter as embedder field of the resulting JS object.
|
||||
icu::DecimalFormat* number_format =
|
||||
NumberFormat::InitializeNumberFormat(isolate, locale, options, resolved);
|
||||
|
||||
CHECK_NOT_NULL(number_format);
|
||||
|
||||
local_object->SetEmbedderField(NumberFormat::kDecimalFormatIndex,
|
||||
reinterpret_cast<Smi*>(number_format));
|
||||
|
||||
Handle<Object> wrapper = isolate->global_handles()->Create(*local_object);
|
||||
GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
|
||||
NumberFormat::DeleteNumberFormat,
|
||||
WeakCallbackType::kInternalFields);
|
||||
return local_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses Unicode extension into key - value map.
|
||||
* Returns empty object if the extension string is invalid.
|
||||
@ -1883,34 +1544,6 @@ void Intl::ParseExtension(Isolate* isolate, const std::string& extension,
|
||||
if (!key.empty()) out.insert(std::pair<std::string, std::string>(key, value));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsAToZ(char ch) {
|
||||
return IsInRange(AsciiAlphaToLower(ch), 'a', 'z');
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Verifies that the input is a well-formed ISO 4217 currency code.
|
||||
// ecma402/#sec-currency-codes
|
||||
bool Intl::IsWellFormedCurrencyCode(Isolate* isolate, Handle<String> currency) {
|
||||
// 2. If the number of elements in normalized is not 3, return false.
|
||||
if (currency->length() != 3) return false;
|
||||
|
||||
currency = String::Flatten(isolate, currency);
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
String::FlatContent flat = currency->GetFlatContent();
|
||||
|
||||
// 1. Let normalized be the result of mapping currency to upper case as
|
||||
// described in 6.1. 3. If normalized contains any character that is not in
|
||||
// the range "A" to "Z" (U+0041 to U+005A), return false. 4. Return true.
|
||||
// Don't uppercase to test. It could convert invalid code into a valid one.
|
||||
// For example \u00DFP (Eszett+P) becomes SSP.
|
||||
return (IsAToZ(flat.Get(0)) && IsAToZ(flat.Get(1)) && IsAToZ(flat.Get(2)));
|
||||
}
|
||||
}
|
||||
|
||||
// ecma402 #sup-string.prototype.tolocalelowercase
|
||||
// ecma402 #sup-string.prototype.tolocaleuppercase
|
||||
MaybeHandle<String> Intl::StringLocaleConvertCase(Isolate* isolate,
|
||||
@ -2017,16 +1650,19 @@ MaybeHandle<String> Intl::NumberToLocaleString(Isolate* isolate,
|
||||
factory->NewStringFromStaticChars("numberformat"),
|
||||
locales, options, factory->undefined_value()),
|
||||
String);
|
||||
DCHECK(
|
||||
Intl::IsObjectOfType(isolate, number_format_holder, Intl::kNumberFormat));
|
||||
DCHECK(number_format_holder->IsJSNumberFormat());
|
||||
Handle<JSNumberFormat> number_format = Handle<JSNumberFormat>(
|
||||
JSNumberFormat::cast(*number_format_holder), isolate);
|
||||
|
||||
Handle<Object> number_obj;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, number_obj,
|
||||
Object::ToNumber(isolate, num), String);
|
||||
|
||||
// Spec treats -0 and +0 as 0.
|
||||
double number = number_obj->Number() + 0;
|
||||
|
||||
// Return FormatNumber(numberFormat, x).
|
||||
return NumberFormat::FormatNumber(isolate, number_format_holder, number);
|
||||
return JSNumberFormat::FormatNumber(isolate, number_format, number);
|
||||
}
|
||||
|
||||
// ecma402/#sec-defaultnumberoption
|
||||
|
@ -23,6 +23,7 @@ namespace U_ICU_NAMESPACE {
|
||||
class BreakIterator;
|
||||
class Collator;
|
||||
class DecimalFormat;
|
||||
class NumberFormat;
|
||||
class PluralRules;
|
||||
class SimpleDateFormat;
|
||||
class UnicodeString;
|
||||
@ -101,58 +102,6 @@ class DateFormat {
|
||||
DateFormat();
|
||||
};
|
||||
|
||||
class NumberFormat {
|
||||
public:
|
||||
// Create a formatter for the specificied locale and options. Returns the
|
||||
// resolved settings for the locale / options.
|
||||
static icu::DecimalFormat* InitializeNumberFormat(Isolate* isolate,
|
||||
Handle<String> locale,
|
||||
Handle<JSObject> options,
|
||||
Handle<JSObject> resolved);
|
||||
|
||||
// Unpacks number format object from corresponding JavaScript object.
|
||||
static icu::DecimalFormat* UnpackNumberFormat(Handle<JSObject> obj);
|
||||
|
||||
// Release memory we allocated for the NumberFormat once the JS object that
|
||||
// holds the pointer gets garbage collected.
|
||||
static void DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data);
|
||||
|
||||
// The UnwrapNumberFormat abstract operation gets the underlying
|
||||
// NumberFormat operation for various methods which implement
|
||||
// ECMA-402 v1 semantics for supporting initializing existing Intl
|
||||
// objects.
|
||||
//
|
||||
// ecma402/#sec-unwrapnumberformat
|
||||
static MaybeHandle<JSObject> Unwrap(Isolate* isolate,
|
||||
Handle<JSReceiver> receiver,
|
||||
const char* method_name);
|
||||
|
||||
// ecm402/#sec-formatnumber
|
||||
static MaybeHandle<String> FormatNumber(Isolate* isolate,
|
||||
Handle<JSObject> number_format_holder,
|
||||
double value);
|
||||
|
||||
// Layout description.
|
||||
#define NUMBER_FORMAT_FIELDS(V) \
|
||||
/* Pointer fields. */ \
|
||||
V(kDecimalFormat, kPointerSize) \
|
||||
V(kBoundFormat, kPointerSize) \
|
||||
V(kSize, 0)
|
||||
|
||||
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, NUMBER_FORMAT_FIELDS)
|
||||
#undef NUMBER_FORMAT_FIELDS
|
||||
|
||||
// TODO(gsathya): Remove this and use regular accessors once
|
||||
// NumberFormat is a sub class of JSObject.
|
||||
//
|
||||
// This needs to be consistent with the above LayoutDescription.
|
||||
static const int kDecimalFormatIndex = 0;
|
||||
static const int kBoundFormatIndex = 1;
|
||||
|
||||
private:
|
||||
NumberFormat();
|
||||
};
|
||||
|
||||
class V8BreakIterator {
|
||||
public:
|
||||
// Create a BreakIterator for the specificied locale and options. Returns the
|
||||
@ -241,6 +190,12 @@ class Intl {
|
||||
// pa_IN.
|
||||
static std::set<std::string> GetAvailableLocales(const IcuService& service);
|
||||
|
||||
// Get the name of the numbering system from locale.
|
||||
// ICU doesn't expose numbering system in any way, so we have to assume that
|
||||
// for given locale NumberingSystem constructor produces the same digits as
|
||||
// NumberFormat/Calendar would.
|
||||
static std::string GetNumberingSystem(const icu::Locale& icu_locale);
|
||||
|
||||
static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> AvailableLocalesOf(
|
||||
Isolate* isolate, Handle<String> service);
|
||||
|
||||
@ -251,7 +206,7 @@ class Intl {
|
||||
|
||||
static std::string DefaultLocale(Isolate* isolate);
|
||||
|
||||
static void DefineWEProperty(Isolate* isolate, Handle<JSObject> target,
|
||||
static void DefineWEProperty(Isolate* isolate, Handle<JSReceiver> target,
|
||||
Handle<Name> key, Handle<Object> value);
|
||||
|
||||
// If locale has a script tag then return true and the locale without the
|
||||
@ -345,10 +300,6 @@ class Intl {
|
||||
Isolate* isolate, Handle<Object> locales,
|
||||
bool only_return_one_result = false);
|
||||
|
||||
// ecma-402/#sec-currencydigits
|
||||
// The currency is expected to an all upper case string value.
|
||||
static Handle<Smi> CurrencyDigits(Isolate* isolate, Handle<String> currency);
|
||||
|
||||
// TODO(ftang): Remove this and use ICU to the conversion in the future
|
||||
static void ParseExtension(Isolate* isolate, const std::string& extension,
|
||||
std::map<std::string, std::string>& out);
|
||||
@ -357,10 +308,6 @@ class Intl {
|
||||
Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
|
||||
Handle<JSObject> resolved);
|
||||
|
||||
// ecma402/#sec-iswellformedcurrencycode
|
||||
static bool IsWellFormedCurrencyCode(Isolate* isolate,
|
||||
Handle<String> currency);
|
||||
|
||||
// For locale sensitive functions
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<String> StringLocaleConvertCase(
|
||||
Isolate* isolate, Handle<String> s, bool is_upper,
|
||||
|
58
src/objects/js-number-format-inl.h
Normal file
58
src/objects/js-number-format-inl.h
Normal file
@ -0,0 +1,58 @@
|
||||
// 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_NUMBER_FORMAT_INL_H_
|
||||
#define V8_OBJECTS_JS_NUMBER_FORMAT_INL_H_
|
||||
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/js-number-format.h"
|
||||
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
ACCESSORS(JSNumberFormat, locale, String, kLocaleOffset)
|
||||
ACCESSORS(JSNumberFormat, icu_number_format, Managed<icu::NumberFormat>,
|
||||
kIcuNumberFormatOffset)
|
||||
ACCESSORS(JSNumberFormat, bound_format, Object, kBoundFormatOffset)
|
||||
SMI_ACCESSORS(JSNumberFormat, flags, kFlagsOffset)
|
||||
|
||||
inline void JSNumberFormat::set_style(Style style) {
|
||||
DCHECK_LT(style, Style::COUNT);
|
||||
int hints = flags();
|
||||
hints = StyleBits::update(hints, style);
|
||||
set_flags(hints);
|
||||
}
|
||||
|
||||
inline JSNumberFormat::Style JSNumberFormat::style() const {
|
||||
return StyleBits::decode(flags());
|
||||
}
|
||||
|
||||
inline void JSNumberFormat::set_currency_display(
|
||||
CurrencyDisplay currency_display) {
|
||||
DCHECK_LT(currency_display, CurrencyDisplay::COUNT);
|
||||
int hints = flags();
|
||||
hints = CurrencyDisplayBits::update(hints, currency_display);
|
||||
set_flags(hints);
|
||||
}
|
||||
|
||||
inline JSNumberFormat::CurrencyDisplay JSNumberFormat::currency_display()
|
||||
const {
|
||||
return CurrencyDisplayBits::decode(flags());
|
||||
}
|
||||
|
||||
CAST_ACCESSOR(JSNumberFormat);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#include "src/objects/object-macros-undef.h"
|
||||
|
||||
#endif // V8_OBJECTS_JS_NUMBER_FORMAT_INL_H_
|
505
src/objects/js-number-format.cc
Normal file
505
src/objects/js-number-format.cc
Normal file
@ -0,0 +1,505 @@
|
||||
// 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-number-format.h"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/intl-objects.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/numfmt.h"
|
||||
#include "unicode/strenum.h"
|
||||
#include "unicode/ucurr.h"
|
||||
#include "unicode/uloc.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// ecma-402/#sec-currencydigits
|
||||
// The currency is expected to an all upper case string value.
|
||||
int CurrencyDigits(const icu::UnicodeString& currency) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
|
||||
(const UChar*)(currency.getBuffer()), &status);
|
||||
// For missing currency codes, default to the most common, 2
|
||||
return U_SUCCESS(status) ? fraction_digits : 2;
|
||||
}
|
||||
|
||||
bool IsAToZ(char ch) { return IsInRange(AsciiAlphaToLower(ch), 'a', 'z'); }
|
||||
|
||||
// ecma402/#sec-iswellformedcurrencycode
|
||||
bool IsWellFormedCurrencyCode(const std::string& currency) {
|
||||
// Verifies that the input is a well-formed ISO 4217 currency code.
|
||||
// ecma402/#sec-currency-codes
|
||||
// 2. If the number of elements in normalized is not 3, return false.
|
||||
if (currency.length() != 3) return false;
|
||||
// 1. Let normalized be the result of mapping currency to upper case as
|
||||
// described in 6.1.
|
||||
//
|
||||
// 3. If normalized contains any character that is not in
|
||||
// the range "A" to "Z" (U+0041 to U+005A), return false.
|
||||
//
|
||||
// 4. Return true.
|
||||
// Don't uppercase to test. It could convert invalid code into a valid one.
|
||||
// For example \u00DFP (Eszett+P) becomes SSP.
|
||||
return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2]));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// static
|
||||
Handle<JSObject> JSNumberFormat::ResolvedOptions(
|
||||
Isolate* isolate, Handle<JSNumberFormat> number_format_holder) {
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->style_string(),
|
||||
number_format_holder->StyleAsString(), kDontThrow)
|
||||
.FromJust());
|
||||
|
||||
icu::NumberFormat* number_format =
|
||||
UnpackIcuNumberFormat(isolate, number_format_holder);
|
||||
CHECK_NOT_NULL(number_format);
|
||||
icu::DecimalFormat* decimal_format =
|
||||
static_cast<icu::DecimalFormat*>(number_format);
|
||||
CHECK_NOT_NULL(decimal_format);
|
||||
|
||||
Handle<String> locale =
|
||||
Handle<String>(number_format_holder->locale(), isolate);
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->locale_string(), locale, kDontThrow)
|
||||
.FromJust());
|
||||
UErrorCode error = U_ZERO_ERROR;
|
||||
icu::Locale icu_locale = number_format->getLocale(ULOC_VALID_LOCALE, error);
|
||||
DCHECK(U_SUCCESS(error));
|
||||
|
||||
std::string numbering_system = Intl::GetNumberingSystem(icu_locale);
|
||||
if (!numbering_system.empty()) {
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->numberingSystem_string(),
|
||||
factory->NewStringFromAsciiChecked(numbering_system.c_str()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
if (number_format_holder->style() == Style::CURRENCY) {
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->currencyDisplay_string(),
|
||||
number_format_holder->CurrencyDisplayAsString(), kDontThrow)
|
||||
.FromJust());
|
||||
icu::UnicodeString currency(number_format->getCurrency());
|
||||
DCHECK(!currency.isEmpty());
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->currency_string(),
|
||||
factory
|
||||
->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(currency.getBuffer()),
|
||||
currency.length()))
|
||||
.ToHandleChecked(),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->minimumIntegerDigits_string(),
|
||||
factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
CHECK(
|
||||
JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->minimumFractionDigits_string(),
|
||||
factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
CHECK(
|
||||
JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->maximumFractionDigits_string(),
|
||||
factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
if (decimal_format->areSignificantDigitsUsed()) {
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->minimumSignificantDigits_string(),
|
||||
factory->NewNumberFromInt(
|
||||
decimal_format->getMinimumSignificantDigits()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->maximumSignificantDigits_string(),
|
||||
factory->NewNumberFromInt(
|
||||
decimal_format->getMaximumSignificantDigits()),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
}
|
||||
CHECK(JSReceiver::CreateDataProperty(
|
||||
isolate, options, factory->useGrouping_string(),
|
||||
factory->ToBoolean((number_format->isGroupingUsed() == TRUE)),
|
||||
kDontThrow)
|
||||
.FromJust());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
// ecma402/#sec-unwrapnumberformat
|
||||
MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat(
|
||||
Isolate* isolate, Handle<JSReceiver> format_holder) {
|
||||
// old code copy from NumberFormat::Unwrap that has no spec comment and
|
||||
// compiled but fail unit tests.
|
||||
Handle<Context> native_context =
|
||||
Handle<Context>(isolate->context()->native_context(), isolate);
|
||||
Handle<JSFunction> constructor = Handle<JSFunction>(
|
||||
JSFunction::cast(native_context->intl_number_format_function()), isolate);
|
||||
Handle<Object> object;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, object,
|
||||
Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
|
||||
format_holder->IsJSNumberFormat()),
|
||||
JSNumberFormat);
|
||||
// 4. If ... or nf does not have an [[InitializedNumberFormat]] internal slot,
|
||||
// then
|
||||
if (!object->IsJSNumberFormat()) {
|
||||
// a. Throw a TypeError exception.
|
||||
THROW_NEW_ERROR(isolate,
|
||||
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
isolate->factory()->NewStringFromAsciiChecked(
|
||||
"UnwrapNumberFormat")),
|
||||
JSNumberFormat);
|
||||
}
|
||||
// 5. Return nf.
|
||||
return Handle<JSNumberFormat>::cast(object);
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<JSNumberFormat> JSNumberFormat::InitializeNumberFormat(
|
||||
Isolate* isolate, Handle<JSNumberFormat> number_format,
|
||||
Handle<Object> locales, Handle<Object> options_obj) {
|
||||
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);
|
||||
|
||||
// 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.NumberFormat"),
|
||||
JSNumberFormat);
|
||||
}
|
||||
|
||||
// At this point, options_obj can either be a JSObject or a JSProxy only.
|
||||
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.
|
||||
//
|
||||
// 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<String> locale_with_extension_str =
|
||||
isolate->factory()->NewStringFromStaticChars("localeWithExtension");
|
||||
Handle<Object> locale_with_extension_obj =
|
||||
JSObject::GetDataProperty(r, locale_with_extension_str);
|
||||
|
||||
// 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);
|
||||
number_format->set_locale(*locale_with_extension);
|
||||
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);
|
||||
|
||||
// The list that is the value of the "nu" field of any locale field of
|
||||
// [[LocaleData]] must not include the values "native", "traditio", or
|
||||
// "finance".
|
||||
//
|
||||
// See https://tc39.github.io/ecma402/#sec-intl.numberformat-internal-slots
|
||||
if (extensions.find("nu") != extensions.end()) {
|
||||
const std::string value = extensions.at("nu");
|
||||
if (value == "native" || value == "traditio" || value == "finance") {
|
||||
// 10. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
icu_locale.setKeywordValue("nu", NULL, status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Let dataLocale be r.[[dataLocale]].
|
||||
//
|
||||
// 12. Let style be ? GetOption(options, "style", "string", « "decimal",
|
||||
// "percent", "currency" », "decimal").
|
||||
const char* service = "Intl.NumberFormat";
|
||||
std::unique_ptr<char[]> style_cstr;
|
||||
static std::vector<const char*> style_values = {"decimal", "percent",
|
||||
"currency"};
|
||||
Maybe<bool> found_style = Intl::GetStringOption(
|
||||
isolate, options, "style", style_values, service, &style_cstr);
|
||||
MAYBE_RETURN(found_style, MaybeHandle<JSNumberFormat>());
|
||||
Style style = Style::DECIMAL;
|
||||
if (found_style.FromJust()) {
|
||||
DCHECK_NOT_NULL(style_cstr.get());
|
||||
if (strcmp(style_cstr.get(), "percent") == 0) {
|
||||
style = Style::PERCENT;
|
||||
} else if (strcmp(style_cstr.get(), "currency") == 0) {
|
||||
style = Style::CURRENCY;
|
||||
}
|
||||
}
|
||||
|
||||
// 13. Set numberFormat.[[Style]] to style.
|
||||
number_format->set_style(style);
|
||||
|
||||
// 14. Let currency be ? GetOption(options, "currency", "string", undefined,
|
||||
// undefined).
|
||||
std::unique_ptr<char[]> currency_cstr;
|
||||
static std::vector<const char*> empty_values = {};
|
||||
Maybe<bool> found_currency = Intl::GetStringOption(
|
||||
isolate, options, "currency", empty_values, service, ¤cy_cstr);
|
||||
MAYBE_RETURN(found_currency, MaybeHandle<JSNumberFormat>());
|
||||
|
||||
std::string currency;
|
||||
// 15. If currency is not undefined, then
|
||||
if (found_currency.FromJust()) {
|
||||
DCHECK_NOT_NULL(currency_cstr.get());
|
||||
currency = currency_cstr.get();
|
||||
// 15. a. If the result of IsWellFormedCurrencyCode(currency) is false,
|
||||
// throw a RangeError exception.
|
||||
if (!IsWellFormedCurrencyCode(currency)) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
NewRangeError(MessageTemplate::kInvalidCurrencyCode,
|
||||
factory->NewStringFromAsciiChecked(currency.c_str())),
|
||||
JSNumberFormat);
|
||||
}
|
||||
}
|
||||
|
||||
// 16. If style is "currency" and currency is undefined, throw a TypeError
|
||||
// exception.
|
||||
if (style == Style::CURRENCY && !found_currency.FromJust()) {
|
||||
THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode),
|
||||
JSNumberFormat);
|
||||
}
|
||||
// 17. If style is "currency", then
|
||||
int c_digits = 0;
|
||||
icu::UnicodeString currency_ustr;
|
||||
if (style == Style::CURRENCY) {
|
||||
// a. Let currency be the result of converting currency to upper case as
|
||||
// specified in 6.1
|
||||
std::transform(currency.begin(), currency.end(), currency.begin(), toupper);
|
||||
// c. Let cDigits be CurrencyDigits(currency).
|
||||
currency_ustr = currency.c_str();
|
||||
c_digits = CurrencyDigits(currency_ustr);
|
||||
}
|
||||
|
||||
// 18. Let currencyDisplay be ? GetOption(options, "currencyDisplay",
|
||||
// "string", « "code", "symbol", "name" », "symbol").
|
||||
std::unique_ptr<char[]> currency_display_cstr;
|
||||
static std::vector<const char*> currency_display_values = {"code", "name",
|
||||
"symbol"};
|
||||
Maybe<bool> found_currency_display = Intl::GetStringOption(
|
||||
isolate, options, "currencyDisplay", currency_display_values, service,
|
||||
¤cy_display_cstr);
|
||||
MAYBE_RETURN(found_currency_display, MaybeHandle<JSNumberFormat>());
|
||||
CurrencyDisplay currency_display = CurrencyDisplay::SYMBOL;
|
||||
UNumberFormatStyle format_style = UNUM_CURRENCY;
|
||||
|
||||
if (found_currency_display.FromJust()) {
|
||||
DCHECK_NOT_NULL(currency_display_cstr.get());
|
||||
if (strcmp(currency_display_cstr.get(), "code") == 0) {
|
||||
currency_display = CurrencyDisplay::CODE;
|
||||
format_style = UNUM_CURRENCY_ISO;
|
||||
} else if (strcmp(currency_display_cstr.get(), "name") == 0) {
|
||||
currency_display = CurrencyDisplay::NAME;
|
||||
format_style = UNUM_CURRENCY_PLURAL;
|
||||
}
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::unique_ptr<icu::NumberFormat> icu_number_format;
|
||||
if (style == Style::DECIMAL) {
|
||||
icu_number_format.reset(
|
||||
icu::NumberFormat::createInstance(icu_locale, status));
|
||||
} else if (style == Style::PERCENT) {
|
||||
icu_number_format.reset(
|
||||
icu::NumberFormat::createPercentInstance(icu_locale, status));
|
||||
} else {
|
||||
DCHECK_EQ(style, Style::CURRENCY);
|
||||
icu_number_format.reset(
|
||||
icu::NumberFormat::createInstance(icu_locale, format_style, status));
|
||||
}
|
||||
|
||||
if (U_FAILURE(status) || icu_number_format.get() == nullptr) {
|
||||
status = U_ZERO_ERROR;
|
||||
// Remove extensions and try again.
|
||||
icu::Locale no_extension_locale(icu_locale.getBaseName());
|
||||
icu_number_format.reset(
|
||||
icu::NumberFormat::createInstance(no_extension_locale, status));
|
||||
|
||||
if (U_FAILURE(status) || icu_number_format.get() == nullptr) {
|
||||
FATAL("Failed to create ICU number_format, are ICU data files missing?");
|
||||
}
|
||||
}
|
||||
DCHECK(U_SUCCESS(status));
|
||||
CHECK_NOT_NULL(icu_number_format.get());
|
||||
if (style == Style::CURRENCY) {
|
||||
// 19. If style is "currency", set numberFormat.[[CurrencyDisplay]] to
|
||||
// currencyDisplay.
|
||||
number_format->set_currency_display(currency_display);
|
||||
|
||||
// 17.b. Set numberFormat.[[Currency]] to currency.
|
||||
if (!currency_ustr.isEmpty()) {
|
||||
status = U_ZERO_ERROR;
|
||||
icu_number_format->setCurrency(currency_ustr.getBuffer(), status);
|
||||
CHECK(U_SUCCESS(status));
|
||||
}
|
||||
}
|
||||
|
||||
// 20. If style is "currency", then
|
||||
int mnfd_default, mxfd_default;
|
||||
if (style == Style::CURRENCY) {
|
||||
// a. Let mnfdDefault be cDigits.
|
||||
// b. Let mxfdDefault be cDigits.
|
||||
mnfd_default = c_digits;
|
||||
mxfd_default = c_digits;
|
||||
} else {
|
||||
// 21. Else,
|
||||
// a. Let mnfdDefault be 0.
|
||||
mnfd_default = 0;
|
||||
// b. If style is "percent", then
|
||||
if (style == Style::PERCENT) {
|
||||
// i. Let mxfdDefault be 0.
|
||||
mxfd_default = 0;
|
||||
} else {
|
||||
// c. Else,
|
||||
// i. Let mxfdDefault be 3.
|
||||
mxfd_default = 3;
|
||||
}
|
||||
}
|
||||
// 22. Perform ? SetNumberFormatDigitOptions(numberFormat, options,
|
||||
// mnfdDefault, mxfdDefault).
|
||||
icu::DecimalFormat* icu_decimal_format =
|
||||
static_cast<icu::DecimalFormat*>(icu_number_format.get());
|
||||
Maybe<bool> maybe_set_number_for_digit_options =
|
||||
Intl::SetNumberFormatDigitOptions(isolate, icu_decimal_format, options,
|
||||
mnfd_default, mxfd_default);
|
||||
MAYBE_RETURN(maybe_set_number_for_digit_options, Handle<JSNumberFormat>());
|
||||
|
||||
// 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean",
|
||||
// undefined, true).
|
||||
bool use_grouping = true;
|
||||
Maybe<bool> found_use_grouping = Intl::GetBoolOption(
|
||||
isolate, options, "useGrouping", service, &use_grouping);
|
||||
MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>());
|
||||
// 24. Set numberFormat.[[UseGrouping]] to useGrouping.
|
||||
icu_number_format->setGroupingUsed(use_grouping ? TRUE : FALSE);
|
||||
|
||||
// 25. Let dataLocaleData be localeData.[[<dataLocale>]].
|
||||
//
|
||||
// 26. Let patterns be dataLocaleData.[[patterns]].
|
||||
//
|
||||
// 27. Assert: patterns is a record (see 11.3.3).
|
||||
//
|
||||
// 28. Let stylePatterns be patterns.[[<style>]].
|
||||
//
|
||||
// 29. Set numberFormat.[[PositivePattern]] to
|
||||
// stylePatterns.[[positivePattern]].
|
||||
//
|
||||
// 30. Set numberFormat.[[NegativePattern]] to
|
||||
// stylePatterns.[[negativePattern]].
|
||||
|
||||
Handle<Managed<icu::NumberFormat>> managed_number_format =
|
||||
Managed<icu::NumberFormat>::FromUniquePtr(isolate, 0,
|
||||
std::move(icu_number_format));
|
||||
number_format->set_icu_number_format(*managed_number_format);
|
||||
number_format->set_bound_format(*factory->undefined_value());
|
||||
|
||||
// 31. Return numberFormat.
|
||||
return number_format;
|
||||
}
|
||||
|
||||
icu::NumberFormat* JSNumberFormat::UnpackIcuNumberFormat(
|
||||
Isolate* isolate, Handle<JSNumberFormat> holder) {
|
||||
return Managed<icu::NumberFormat>::cast(holder->icu_number_format())->raw();
|
||||
}
|
||||
|
||||
Handle<String> JSNumberFormat::StyleAsString() const {
|
||||
switch (style()) {
|
||||
case Style::DECIMAL:
|
||||
return GetReadOnlyRoots().decimal_string_handle();
|
||||
case Style::PERCENT:
|
||||
return GetReadOnlyRoots().percent_string_handle();
|
||||
case Style::CURRENCY:
|
||||
return GetReadOnlyRoots().currency_string_handle();
|
||||
case Style::COUNT:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
Handle<String> JSNumberFormat::CurrencyDisplayAsString() const {
|
||||
switch (currency_display()) {
|
||||
case CurrencyDisplay::CODE:
|
||||
return GetReadOnlyRoots().code_string_handle();
|
||||
case CurrencyDisplay::SYMBOL:
|
||||
return GetReadOnlyRoots().symbol_string_handle();
|
||||
case CurrencyDisplay::NAME:
|
||||
return GetReadOnlyRoots().name_string_handle();
|
||||
case CurrencyDisplay::COUNT:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<String> JSNumberFormat::FormatNumber(
|
||||
Isolate* isolate, Handle<JSNumberFormat> number_format_holder,
|
||||
double number) {
|
||||
icu::NumberFormat* number_format =
|
||||
UnpackIcuNumberFormat(isolate, number_format_holder);
|
||||
CHECK_NOT_NULL(number_format);
|
||||
|
||||
icu::UnicodeString result;
|
||||
number_format->format(number, result);
|
||||
|
||||
return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
|
||||
reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
129
src/objects/js-number-format.h
Normal file
129
src/objects/js-number-format.h
Normal file
@ -0,0 +1,129 @@
|
||||
// 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_NUMBER_FORMAT_H_
|
||||
#define V8_OBJECTS_JS_NUMBER_FORMAT_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 JSNumberFormat : public JSObject {
|
||||
public:
|
||||
// ecma402/#sec-initializenumberformat
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat>
|
||||
InitializeNumberFormat(Isolate* isolate, Handle<JSNumberFormat> number_format,
|
||||
Handle<Object> locales, Handle<Object> options);
|
||||
|
||||
// ecma402/#sec-unwrapnumberformat
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<JSNumberFormat> UnwrapNumberFormat(
|
||||
Isolate* isolate, Handle<JSReceiver> format_holder);
|
||||
|
||||
// ecma402/#sec-intl.numberformat.prototype.resolvedoptions
|
||||
static Handle<JSObject> ResolvedOptions(Isolate* isolate,
|
||||
Handle<JSNumberFormat> number_format);
|
||||
|
||||
V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatNumber(
|
||||
Isolate* isolate, Handle<JSNumberFormat> number_format, double number);
|
||||
|
||||
// Unpacks format object from corresponding JavaScript object.
|
||||
static icu::NumberFormat* UnpackIcuNumberFormat(
|
||||
Isolate* isolate, Handle<JSNumberFormat> number_format_holder);
|
||||
|
||||
Handle<String> StyleAsString() const;
|
||||
Handle<String> CurrencyDisplayAsString() const;
|
||||
|
||||
DECL_CAST(JSNumberFormat)
|
||||
DECL_PRINTER(JSNumberFormat)
|
||||
DECL_VERIFIER(JSNumberFormat)
|
||||
|
||||
// [[Style]] is one of the values "decimal", "percent" or "currency",
|
||||
// identifying the style of the number format.
|
||||
enum class Style {
|
||||
DECIMAL,
|
||||
PERCENT,
|
||||
CURRENCY,
|
||||
|
||||
COUNT
|
||||
};
|
||||
inline void set_style(Style style);
|
||||
inline Style style() const;
|
||||
|
||||
// [[CurrencyDisplay]] is one of the values "code", "symbol" or "name",
|
||||
// identifying the display of the currency number format.
|
||||
enum class CurrencyDisplay {
|
||||
CODE,
|
||||
SYMBOL,
|
||||
NAME,
|
||||
|
||||
COUNT
|
||||
};
|
||||
inline void set_currency_display(CurrencyDisplay currency_display);
|
||||
inline CurrencyDisplay currency_display() const;
|
||||
|
||||
// Layout description.
|
||||
#define JS_NUMBER_FORMAT_FIELDS(V) \
|
||||
V(kLocaleOffset, kPointerSize) \
|
||||
V(kIcuNumberFormatOffset, kPointerSize) \
|
||||
V(kBoundFormatOffset, kPointerSize) \
|
||||
V(kFlagsOffset, kPointerSize) \
|
||||
/* Total size. */ \
|
||||
V(kSize, 0)
|
||||
|
||||
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_NUMBER_FORMAT_FIELDS)
|
||||
#undef JS_NUMBER_FORMAT_FIELDS
|
||||
|
||||
// Bit positions in |flags|.
|
||||
#define FLAGS_BIT_FIELDS(V, _) \
|
||||
V(StyleBits, Style, 2, _) \
|
||||
V(CurrencyDisplayBits, CurrencyDisplay, 2, _)
|
||||
|
||||
DEFINE_BIT_FIELDS(FLAGS_BIT_FIELDS)
|
||||
#undef FLAGS_BIT_FIELDS
|
||||
|
||||
STATIC_ASSERT(Style::DECIMAL <= StyleBits::kMax);
|
||||
STATIC_ASSERT(Style::PERCENT <= StyleBits::kMax);
|
||||
STATIC_ASSERT(Style::CURRENCY <= StyleBits::kMax);
|
||||
|
||||
STATIC_ASSERT(CurrencyDisplay::CODE <= CurrencyDisplayBits::kMax);
|
||||
STATIC_ASSERT(CurrencyDisplay::SYMBOL <= CurrencyDisplayBits::kMax);
|
||||
STATIC_ASSERT(CurrencyDisplay::NAME <= CurrencyDisplayBits::kMax);
|
||||
|
||||
DECL_ACCESSORS(locale, String)
|
||||
DECL_ACCESSORS(icu_number_format, Managed<icu::NumberFormat>)
|
||||
DECL_ACCESSORS(bound_format, Object)
|
||||
DECL_INT_ACCESSORS(flags)
|
||||
|
||||
// ContextSlot defines the context structure for the bound
|
||||
// NumberFormat.prototype.format function.
|
||||
enum ContextSlot {
|
||||
// The number format instance that the function holding this
|
||||
// context is bound to.
|
||||
kNumberFormat = Context::MIN_CONTEXT_SLOTS,
|
||||
|
||||
kLength
|
||||
};
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSNumberFormat);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#include "src/objects/object-macros-undef.h"
|
||||
|
||||
#endif // V8_OBJECTS_JS_NUMBER_FORMAT_H_
|
@ -25,6 +25,7 @@
|
||||
#include "src/objects/js-date-time-format-inl.h"
|
||||
#include "src/objects/js-list-format-inl.h"
|
||||
#include "src/objects/js-list-format.h"
|
||||
#include "src/objects/js-number-format-inl.h"
|
||||
#include "src/objects/js-plural-rules-inl.h"
|
||||
#include "src/objects/managed.h"
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
@ -77,40 +78,6 @@ RUNTIME_FUNCTION(Runtime_FormatListToParts) {
|
||||
isolate, JSListFormat::FormatListToParts(isolate, list_format, list));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetNumberOption) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(5, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, options, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, property, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(min, 2);
|
||||
CONVERT_SMI_ARG_CHECKED(max, 3);
|
||||
CONVERT_SMI_ARG_CHECKED(fallback, 4);
|
||||
|
||||
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) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(5, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
|
||||
CONVERT_SMI_ARG_CHECKED(min, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(max, 2);
|
||||
CONVERT_SMI_ARG_CHECKED(fallback, 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, property, 4);
|
||||
|
||||
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
|
||||
RUNTIME_FUNCTION(Runtime_CanonicalizeLanguageTag) {
|
||||
HandleScope scope(isolate);
|
||||
@ -143,14 +110,6 @@ RUNTIME_FUNCTION(Runtime_GetDefaultICULocale) {
|
||||
Intl::DefaultLocale(isolate).c_str());
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_IsWellFormedCurrencyCode) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);
|
||||
return *(isolate->factory()->ToBoolean(
|
||||
Intl::IsWellFormedCurrencyCode(isolate, currency)));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineWEProperty) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
@ -255,23 +214,31 @@ RUNTIME_FUNCTION(Runtime_DateTimeFormatResolvedOptions) {
|
||||
isolate, JSDateTimeFormat::ResolvedOptions(isolate, date_format_holder));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CreateNumberFormat) {
|
||||
RUNTIME_FUNCTION(Runtime_NumberFormatResolvedOptions) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
DCHECK_EQ(3, args.length());
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, Intl::CreateNumberFormat(isolate, locale, options, resolved));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CurrencyDigits) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, currency, 0);
|
||||
return *Intl::CurrencyDigits(isolate, currency);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, number_format_obj, 0);
|
||||
|
||||
// 2. If Type(nf) is not Object, throw a TypeError exception
|
||||
if (!number_format_obj->IsJSReceiver()) {
|
||||
Handle<String> method_str = isolate->factory()->NewStringFromStaticChars(
|
||||
"Intl.NumberFormat.prototype.resolvedOptions");
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
|
||||
method_str, number_format_obj));
|
||||
}
|
||||
|
||||
// 3. Let nf be ? UnwrapNumberFormat(nf).
|
||||
Handle<JSReceiver> format_holder =
|
||||
Handle<JSReceiver>::cast(number_format_obj);
|
||||
|
||||
Handle<JSNumberFormat> number_format;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, number_format,
|
||||
JSNumberFormat::UnwrapNumberFormat(isolate, format_holder));
|
||||
|
||||
return *JSNumberFormat::ResolvedOptions(isolate, number_format);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CollatorResolvedOptions) {
|
||||
|
@ -205,21 +205,17 @@ namespace internal {
|
||||
F(CollatorResolvedOptions, 1, 1) \
|
||||
F(CreateBreakIterator, 3, 1) \
|
||||
F(CreateDateTimeFormat, 3, 1) \
|
||||
F(CreateNumberFormat, 3, 1) \
|
||||
F(CurrencyDigits, 1, 1) \
|
||||
F(DateCacheVersion, 0, 1) \
|
||||
F(DateTimeFormatResolvedOptions, 1, 1) \
|
||||
F(DefaultNumberOption, 5, 1) \
|
||||
F(DefineWEProperty, 3, 1) \
|
||||
F(FormatList, 2, 1) \
|
||||
F(FormatListToParts, 2, 1) \
|
||||
F(GetDefaultICULocale, 0, 1) \
|
||||
F(GetNumberOption, 5, 1) \
|
||||
F(IntlUnwrapReceiver, 5, 1) \
|
||||
F(IsInitializedIntlObjectOfType, 2, 1) \
|
||||
F(IsWellFormedCurrencyCode, 1, 1) \
|
||||
F(MarkAsInitializedIntlObjectOfType, 2, 1) \
|
||||
F(ParseExtension, 1, 1) \
|
||||
F(NumberFormatResolvedOptions, 1, 1) \
|
||||
F(PluralRulesResolvedOptions, 1, 1) \
|
||||
F(PluralRulesSelect, 2, 1) \
|
||||
F(ToDateTimeOptions, 3, 1) \
|
||||
|
12
test/intl/number-format/wont-crash-by-1-or-false.js
Normal file
12
test/intl/number-format/wont-crash-by-1-or-false.js
Normal file
@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
// Make sure passing 1 or false to patched construtor won't cause crash
|
||||
|
||||
Object.defineProperty(Intl.NumberFormat, Symbol.hasInstance, { value: _ => true });
|
||||
assertThrows(() =>
|
||||
Intl.NumberFormat.call(1), TypeError);
|
||||
|
||||
assertThrows(() =>
|
||||
Intl.NumberFormat.call(false), TypeError);
|
@ -162,15 +162,16 @@ INSTANCE_TYPES = {
|
||||
1085: "JS_INTL_DATE_TIME_FORMAT_TYPE",
|
||||
1086: "JS_INTL_LIST_FORMAT_TYPE",
|
||||
1087: "JS_INTL_LOCALE_TYPE",
|
||||
1088: "JS_INTL_PLURAL_RULES_TYPE",
|
||||
1089: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
|
||||
1090: "WASM_GLOBAL_TYPE",
|
||||
1091: "WASM_INSTANCE_TYPE",
|
||||
1092: "WASM_MEMORY_TYPE",
|
||||
1093: "WASM_MODULE_TYPE",
|
||||
1094: "WASM_TABLE_TYPE",
|
||||
1095: "JS_BOUND_FUNCTION_TYPE",
|
||||
1096: "JS_FUNCTION_TYPE",
|
||||
1088: "JS_INTL_NUMBER_FORMAT_TYPE",
|
||||
1089: "JS_INTL_PLURAL_RULES_TYPE",
|
||||
1090: "JS_INTL_RELATIVE_TIME_FORMAT_TYPE",
|
||||
1091: "WASM_GLOBAL_TYPE",
|
||||
1092: "WASM_INSTANCE_TYPE",
|
||||
1093: "WASM_MEMORY_TYPE",
|
||||
1094: "WASM_MODULE_TYPE",
|
||||
1095: "WASM_TABLE_TYPE",
|
||||
1096: "JS_BOUND_FUNCTION_TYPE",
|
||||
1097: "JS_FUNCTION_TYPE",
|
||||
}
|
||||
|
||||
# List of known V8 maps.
|
||||
@ -285,32 +286,32 @@ KNOWN_MAPS = {
|
||||
("RO_SPACE", 0x04811): (173, "ArrayBoilerplateDescriptionMap"),
|
||||
("RO_SPACE", 0x04b01): (161, "InterceptorInfoMap"),
|
||||
("RO_SPACE", 0x04bf9): (169, "ScriptMap"),
|
||||
("RO_SPACE", 0x09ac1): (154, "AccessorInfoMap"),
|
||||
("RO_SPACE", 0x09b11): (153, "AccessCheckInfoMap"),
|
||||
("RO_SPACE", 0x09b61): (155, "AccessorPairMap"),
|
||||
("RO_SPACE", 0x09bb1): (156, "AliasedArgumentsEntryMap"),
|
||||
("RO_SPACE", 0x09c01): (157, "AllocationMementoMap"),
|
||||
("RO_SPACE", 0x09c51): (158, "AsyncGeneratorRequestMap"),
|
||||
("RO_SPACE", 0x09ca1): (159, "DebugInfoMap"),
|
||||
("RO_SPACE", 0x09cf1): (160, "FunctionTemplateInfoMap"),
|
||||
("RO_SPACE", 0x09d41): (162, "InterpreterDataMap"),
|
||||
("RO_SPACE", 0x09d91): (163, "ModuleInfoEntryMap"),
|
||||
("RO_SPACE", 0x09de1): (164, "ModuleMap"),
|
||||
("RO_SPACE", 0x09e31): (165, "ObjectTemplateInfoMap"),
|
||||
("RO_SPACE", 0x09e81): (166, "PromiseCapabilityMap"),
|
||||
("RO_SPACE", 0x09ed1): (167, "PromiseReactionMap"),
|
||||
("RO_SPACE", 0x09f21): (168, "PrototypeInfoMap"),
|
||||
("RO_SPACE", 0x09f71): (170, "StackFrameInfoMap"),
|
||||
("RO_SPACE", 0x09fc1): (172, "Tuple3Map"),
|
||||
("RO_SPACE", 0x0a011): (174, "WasmDebugInfoMap"),
|
||||
("RO_SPACE", 0x0a061): (175, "WasmExportedFunctionDataMap"),
|
||||
("RO_SPACE", 0x0a0b1): (176, "CallableTaskMap"),
|
||||
("RO_SPACE", 0x0a101): (177, "CallbackTaskMap"),
|
||||
("RO_SPACE", 0x0a151): (178, "PromiseFulfillReactionJobTaskMap"),
|
||||
("RO_SPACE", 0x0a1a1): (179, "PromiseRejectReactionJobTaskMap"),
|
||||
("RO_SPACE", 0x0a1f1): (180, "PromiseResolveThenableJobTaskMap"),
|
||||
("RO_SPACE", 0x0a241): (181, "AllocationSiteMap"),
|
||||
("RO_SPACE", 0x0a291): (181, "AllocationSiteMap"),
|
||||
("RO_SPACE", 0x09c69): (154, "AccessorInfoMap"),
|
||||
("RO_SPACE", 0x09cb9): (153, "AccessCheckInfoMap"),
|
||||
("RO_SPACE", 0x09d09): (155, "AccessorPairMap"),
|
||||
("RO_SPACE", 0x09d59): (156, "AliasedArgumentsEntryMap"),
|
||||
("RO_SPACE", 0x09da9): (157, "AllocationMementoMap"),
|
||||
("RO_SPACE", 0x09df9): (158, "AsyncGeneratorRequestMap"),
|
||||
("RO_SPACE", 0x09e49): (159, "DebugInfoMap"),
|
||||
("RO_SPACE", 0x09e99): (160, "FunctionTemplateInfoMap"),
|
||||
("RO_SPACE", 0x09ee9): (162, "InterpreterDataMap"),
|
||||
("RO_SPACE", 0x09f39): (163, "ModuleInfoEntryMap"),
|
||||
("RO_SPACE", 0x09f89): (164, "ModuleMap"),
|
||||
("RO_SPACE", 0x09fd9): (165, "ObjectTemplateInfoMap"),
|
||||
("RO_SPACE", 0x0a029): (166, "PromiseCapabilityMap"),
|
||||
("RO_SPACE", 0x0a079): (167, "PromiseReactionMap"),
|
||||
("RO_SPACE", 0x0a0c9): (168, "PrototypeInfoMap"),
|
||||
("RO_SPACE", 0x0a119): (170, "StackFrameInfoMap"),
|
||||
("RO_SPACE", 0x0a169): (172, "Tuple3Map"),
|
||||
("RO_SPACE", 0x0a1b9): (174, "WasmDebugInfoMap"),
|
||||
("RO_SPACE", 0x0a209): (175, "WasmExportedFunctionDataMap"),
|
||||
("RO_SPACE", 0x0a259): (176, "CallableTaskMap"),
|
||||
("RO_SPACE", 0x0a2a9): (177, "CallbackTaskMap"),
|
||||
("RO_SPACE", 0x0a2f9): (178, "PromiseFulfillReactionJobTaskMap"),
|
||||
("RO_SPACE", 0x0a349): (179, "PromiseRejectReactionJobTaskMap"),
|
||||
("RO_SPACE", 0x0a399): (180, "PromiseResolveThenableJobTaskMap"),
|
||||
("RO_SPACE", 0x0a3e9): (181, "AllocationSiteMap"),
|
||||
("RO_SPACE", 0x0a439): (181, "AllocationSiteMap"),
|
||||
("MAP_SPACE", 0x02201): (1057, "ExternalMap"),
|
||||
("MAP_SPACE", 0x02251): (1072, "JSMessageObjectMap"),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user