[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:
Frank Tang 2018-09-04 11:32:51 -07:00 committed by Commit Bot
parent 71f14bd0d3
commit 9c7ec98a90
22 changed files with 987 additions and 779 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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 */ \

View File

@ -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) {

View File

@ -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

View File

@ -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") \

View File

@ -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);
}
);

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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", &currency);
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

View File

@ -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,

View 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_

View 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, &currency_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,
&currency_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

View 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_

View File

@ -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) {

View File

@ -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) \

View 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);

View File

@ -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"),
}