5 #ifndef V8_INTL_SUPPORT 6 #error Internationalization is expected to be enabled. 7 #endif // V8_INTL_SUPPORT 9 #include "src/objects/js-number-format.h" 14 #include "src/isolate.h" 15 #include "src/objects-inl.h" 16 #include "src/objects/intl-objects.h" 17 #include "src/objects/js-number-format-inl.h" 18 #include "unicode/decimfmt.h" 19 #include "unicode/locid.h" 20 #include "unicode/numfmt.h" 21 #include "unicode/uloc.h" 28 UNumberFormatStyle ToNumberFormatStyle(
29 JSNumberFormat::CurrencyDisplay currency_display) {
30 switch (currency_display) {
31 case JSNumberFormat::CurrencyDisplay::SYMBOL:
33 case JSNumberFormat::CurrencyDisplay::CODE:
34 return UNUM_CURRENCY_ISO;
35 case JSNumberFormat::CurrencyDisplay::NAME:
36 return UNUM_CURRENCY_PLURAL;
37 case JSNumberFormat::CurrencyDisplay::COUNT:
44 int CurrencyDigits(
const icu::UnicodeString& currency) {
45 UErrorCode status = U_ZERO_ERROR;
46 uint32_t fraction_digits = ucurr_getDefaultFractionDigits(
47 reinterpret_cast<const UChar*>(currency.getBuffer()), &status);
49 return U_SUCCESS(status) ? fraction_digits : 2;
52 bool IsAToZ(
char ch) {
return IsInRange(AsciiAlphaToLower(ch),
'a',
'z'); }
55 bool IsWellFormedCurrencyCode(
const std::string& currency) {
59 if (currency.length() != 3)
return false;
69 return (IsAToZ(currency[0]) && IsAToZ(currency[1]) && IsAToZ(currency[2]));
76 Handle<JSObject> JSNumberFormat::ResolvedOptions(
77 Isolate* isolate, Handle<JSNumberFormat> number_format_holder) {
78 Factory* factory = isolate->factory();
81 Handle<JSObject> options = factory->NewJSObject(isolate->object_function());
83 icu::NumberFormat* number_format =
84 number_format_holder->icu_number_format()->raw();
85 CHECK_NOT_NULL(number_format);
86 icu::DecimalFormat* decimal_format =
87 static_cast<icu::DecimalFormat*
>(number_format);
88 CHECK_NOT_NULL(decimal_format);
90 Handle<String> locale =
91 Handle<String>(number_format_holder->locale(), isolate);
93 std::unique_ptr<char[]> locale_str = locale->ToCString();
94 icu::Locale icu_locale = Intl::CreateICULocale(locale_str.get());
96 std::string numbering_system = Intl::GetNumberingSystem(icu_locale);
112 CHECK(JSReceiver::CreateDataProperty(
113 isolate, options, factory->locale_string(), locale, kDontThrow)
115 if (!numbering_system.empty()) {
116 CHECK(JSReceiver::CreateDataProperty(
117 isolate, options, factory->numberingSystem_string(),
118 factory->NewStringFromAsciiChecked(numbering_system.c_str()),
122 CHECK(JSReceiver::CreateDataProperty(
123 isolate, options, factory->style_string(),
124 number_format_holder->StyleAsString(), kDontThrow)
126 if (number_format_holder->style() == Style::CURRENCY) {
127 icu::UnicodeString currency(number_format->getCurrency());
128 DCHECK(!currency.isEmpty());
129 CHECK(JSReceiver::CreateDataProperty(
130 isolate, options, factory->currency_string(),
132 ->NewStringFromTwoByte(Vector<const uint16_t>(
133 reinterpret_cast<const uint16_t*>(currency.getBuffer()),
139 CHECK(JSReceiver::CreateDataProperty(
140 isolate, options, factory->currencyDisplay_string(),
141 number_format_holder->CurrencyDisplayAsString(), kDontThrow)
144 CHECK(JSReceiver::CreateDataProperty(
145 isolate, options, factory->minimumIntegerDigits_string(),
146 factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
150 JSReceiver::CreateDataProperty(
151 isolate, options, factory->minimumFractionDigits_string(),
152 factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
156 JSReceiver::CreateDataProperty(
157 isolate, options, factory->maximumFractionDigits_string(),
158 factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
161 if (decimal_format->areSignificantDigitsUsed()) {
162 CHECK(JSReceiver::CreateDataProperty(
163 isolate, options, factory->minimumSignificantDigits_string(),
164 factory->NewNumberFromInt(
165 decimal_format->getMinimumSignificantDigits()),
168 CHECK(JSReceiver::CreateDataProperty(
169 isolate, options, factory->maximumSignificantDigits_string(),
170 factory->NewNumberFromInt(
171 decimal_format->getMaximumSignificantDigits()),
175 CHECK(JSReceiver::CreateDataProperty(
176 isolate, options, factory->useGrouping_string(),
177 factory->ToBoolean((number_format->isGroupingUsed() == TRUE)),
184 MaybeHandle<JSNumberFormat> JSNumberFormat::UnwrapNumberFormat(
185 Isolate* isolate, Handle<JSReceiver> format_holder) {
188 Handle<Context> native_context =
189 Handle<Context>(isolate->context()->native_context(), isolate);
190 Handle<JSFunction> constructor = Handle<JSFunction>(
191 JSFunction::cast(native_context->intl_number_format_function()), isolate);
192 Handle<Object> object;
193 ASSIGN_RETURN_ON_EXCEPTION(
195 Intl::LegacyUnwrapReceiver(isolate, format_holder, constructor,
196 format_holder->IsJSNumberFormat()),
200 if (!object->IsJSNumberFormat()) {
202 THROW_NEW_ERROR(isolate,
203 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
204 isolate->factory()->NewStringFromAsciiChecked(
205 "UnwrapNumberFormat")),
209 return Handle<JSNumberFormat>::cast(
object);
213 MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
214 Isolate* isolate, Handle<JSNumberFormat> number_format,
215 Handle<Object> locales, Handle<Object> options_obj) {
217 number_format->set_flags(0);
218 Factory* factory = isolate->factory();
221 Maybe<std::vector<std::string>> maybe_requested_locales =
222 Intl::CanonicalizeLocaleList(isolate, locales);
223 MAYBE_RETURN(maybe_requested_locales, Handle<JSNumberFormat>());
224 std::vector<std::string> requested_locales =
225 maybe_requested_locales.FromJust();
228 if (options_obj->IsUndefined(isolate)) {
230 options_obj = isolate->factory()->NewJSObjectWithNullProto();
234 ASSIGN_RETURN_ON_EXCEPTION(
235 isolate, options_obj,
236 Object::ToObject(isolate, options_obj,
"Intl.NumberFormat"),
241 Handle<JSReceiver> options = Handle<JSReceiver>::cast(options_obj);
247 Maybe<Intl::MatcherOption> maybe_locale_matcher =
248 Intl::GetLocaleMatcher(isolate, options,
"Intl.NumberFormat");
249 MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSNumberFormat>());
250 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
256 std::set<std::string> relevant_extension_keys{
"nu"};
257 Intl::ResolvedLocale r =
258 Intl::ResolveLocale(isolate, JSNumberFormat::GetAvailableLocales(),
259 requested_locales, matcher, relevant_extension_keys);
262 Handle<String> locale_str =
263 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
264 number_format->set_locale(*locale_str);
266 icu::Locale icu_locale = r.icu_locale;
267 DCHECK(!icu_locale.isBogus());
269 std::map<std::string, std::string> extensions = r.extensions;
276 if (extensions.find(
"nu") != extensions.end()) {
277 const std::string value = extensions.at(
"nu");
278 if (value ==
"native" || value ==
"traditio" || value ==
"finance") {
280 UErrorCode status = U_ZERO_ERROR;
281 icu_locale.setKeywordValue(
"nu",
nullptr, status);
282 CHECK(U_SUCCESS(status));
290 const char* service =
"Intl.NumberFormat";
291 Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
292 isolate, options,
"style", service, {
"decimal",
"percent",
"currency"},
293 {Style::DECIMAL, Style::PERCENT, Style::CURRENCY}, Style::DECIMAL);
294 MAYBE_RETURN(maybe_style, MaybeHandle<JSNumberFormat>());
295 Style style = maybe_style.FromJust();
298 number_format->set_style(style);
302 std::unique_ptr<char[]> currency_cstr;
303 const std::vector<const char*> empty_values = {};
304 Maybe<bool> found_currency = Intl::GetStringOption(
305 isolate, options,
"currency", empty_values, service, ¤cy_cstr);
306 MAYBE_RETURN(found_currency, MaybeHandle<JSNumberFormat>());
308 std::string currency;
310 if (found_currency.FromJust()) {
311 DCHECK_NOT_NULL(currency_cstr.get());
312 currency = currency_cstr.get();
315 if (!IsWellFormedCurrencyCode(currency)) {
318 NewRangeError(MessageTemplate::kInvalidCurrencyCode,
319 factory->NewStringFromAsciiChecked(currency.c_str())),
326 if (style == Style::CURRENCY && !found_currency.FromJust()) {
327 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kCurrencyCode),
332 icu::UnicodeString currency_ustr;
333 if (style == Style::CURRENCY) {
336 std::transform(currency.begin(), currency.end(), currency.begin(), toupper);
338 currency_ustr = currency.c_str();
339 c_digits = CurrencyDigits(currency_ustr);
344 Maybe<CurrencyDisplay> maybe_currencyDisplay =
345 Intl::GetStringOption<CurrencyDisplay>(
346 isolate, options,
"currencyDisplay", service,
347 {
"code",
"symbol",
"name"},
348 {CurrencyDisplay::CODE, CurrencyDisplay::SYMBOL,
349 CurrencyDisplay::NAME},
350 CurrencyDisplay::SYMBOL);
351 MAYBE_RETURN(maybe_currencyDisplay, MaybeHandle<JSNumberFormat>());
352 CurrencyDisplay currency_display = maybe_currencyDisplay.FromJust();
353 UNumberFormatStyle format_style = ToNumberFormatStyle(currency_display);
355 UErrorCode status = U_ZERO_ERROR;
356 std::unique_ptr<icu::NumberFormat> icu_number_format;
357 if (style == Style::DECIMAL) {
358 icu_number_format.reset(
359 icu::NumberFormat::createInstance(icu_locale, status));
360 }
else if (style == Style::PERCENT) {
361 icu_number_format.reset(
362 icu::NumberFormat::createPercentInstance(icu_locale, status));
364 DCHECK_EQ(style, Style::CURRENCY);
365 icu_number_format.reset(
366 icu::NumberFormat::createInstance(icu_locale, format_style, status));
369 if (U_FAILURE(status) || icu_number_format.get() ==
nullptr) {
370 status = U_ZERO_ERROR;
372 icu::Locale no_extension_locale(icu_locale.getBaseName());
373 icu_number_format.reset(
374 icu::NumberFormat::createInstance(no_extension_locale, status));
376 if (U_FAILURE(status) || icu_number_format.get() ==
nullptr) {
377 FATAL(
"Failed to create ICU number_format, are ICU data files missing?");
380 DCHECK(U_SUCCESS(status));
381 CHECK_NOT_NULL(icu_number_format.get());
382 if (style == Style::CURRENCY) {
385 number_format->set_currency_display(currency_display);
388 if (!currency_ustr.isEmpty()) {
389 status = U_ZERO_ERROR;
390 icu_number_format->setCurrency(currency_ustr.getBuffer(), status);
391 CHECK(U_SUCCESS(status));
396 int mnfd_default, mxfd_default;
397 if (style == Style::CURRENCY) {
400 mnfd_default = c_digits;
401 mxfd_default = c_digits;
407 if (style == Style::PERCENT) {
418 icu::DecimalFormat* icu_decimal_format =
419 static_cast<icu::DecimalFormat*
>(icu_number_format.get());
420 Maybe<bool> maybe_set_number_for_digit_options =
421 Intl::SetNumberFormatDigitOptions(isolate, icu_decimal_format, options,
422 mnfd_default, mxfd_default);
423 MAYBE_RETURN(maybe_set_number_for_digit_options, Handle<JSNumberFormat>());
427 bool use_grouping =
true;
428 Maybe<bool> found_use_grouping = Intl::GetBoolOption(
429 isolate, options,
"useGrouping", service, &use_grouping);
430 MAYBE_RETURN(found_use_grouping, MaybeHandle<JSNumberFormat>());
432 icu_number_format->setGroupingUsed(use_grouping ? TRUE : FALSE);
448 Handle<Managed<icu::NumberFormat>> managed_number_format =
449 Managed<icu::NumberFormat>::FromUniquePtr(isolate, 0,
450 std::move(icu_number_format));
451 number_format->set_icu_number_format(*managed_number_format);
452 number_format->set_bound_format(*factory->undefined_value());
455 return number_format;
458 Handle<String> JSNumberFormat::StyleAsString()
const {
461 return GetReadOnlyRoots().decimal_string_handle();
463 return GetReadOnlyRoots().percent_string_handle();
464 case Style::CURRENCY:
465 return GetReadOnlyRoots().currency_string_handle();
471 Handle<String> JSNumberFormat::CurrencyDisplayAsString()
const {
472 switch (currency_display()) {
473 case CurrencyDisplay::CODE:
474 return GetReadOnlyRoots().code_string_handle();
475 case CurrencyDisplay::SYMBOL:
476 return GetReadOnlyRoots().symbol_string_handle();
477 case CurrencyDisplay::NAME:
478 return GetReadOnlyRoots().name_string_handle();
479 case CurrencyDisplay::COUNT:
484 MaybeHandle<String> JSNumberFormat::FormatNumber(
485 Isolate* isolate,
const icu::NumberFormat& number_format,
double number) {
486 icu::UnicodeString result;
487 number_format.format(number, result);
489 return isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
490 reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
495 bool cmp_NumberFormatSpan(
const NumberFormatSpan& a,
496 const NumberFormatSpan& b) {
498 if (a.begin_pos < b.begin_pos)
return true;
499 if (a.begin_pos > b.begin_pos)
return false;
502 if (a.end_pos < b.end_pos)
return false;
503 if (a.end_pos > b.end_pos)
return true;
507 return a.field_id < b.field_id;
513 Handle<String> IcuNumberFieldIdToNumberType(int32_t field_id,
double number,
515 switch (static_cast<UNumberFormatFields>(field_id)) {
516 case UNUM_INTEGER_FIELD:
517 if (std::isfinite(number))
return isolate->factory()->integer_string();
518 if (std::isnan(number))
return isolate->factory()->nan_string();
519 return isolate->factory()->infinity_string();
520 case UNUM_FRACTION_FIELD:
521 return isolate->factory()->fraction_string();
522 case UNUM_DECIMAL_SEPARATOR_FIELD:
523 return isolate->factory()->decimal_string();
524 case UNUM_GROUPING_SEPARATOR_FIELD:
525 return isolate->factory()->group_string();
526 case UNUM_CURRENCY_FIELD:
527 return isolate->factory()->currency_string();
528 case UNUM_PERCENT_FIELD:
529 return isolate->factory()->percentSign_string();
530 case UNUM_SIGN_FIELD:
531 return number < 0 ? isolate->factory()->minusSign_string()
532 : isolate->factory()->plusSign_string();
534 case UNUM_EXPONENT_SYMBOL_FIELD:
535 case UNUM_EXPONENT_SIGN_FIELD:
536 case UNUM_EXPONENT_FIELD:
540 return Handle<String>();
542 case UNUM_PERMILL_FIELD:
546 return Handle<String>();
550 return Handle<String>();
560 std::vector<NumberFormatSpan> FlattenRegionsToParts(
561 std::vector<NumberFormatSpan>* regions) {
593 std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan);
594 std::vector<size_t> overlapping_region_index_stack;
597 overlapping_region_index_stack.push_back(0);
598 NumberFormatSpan top_region = regions->at(0);
599 size_t region_iterator = 1;
600 int32_t entire_size = top_region.end_pos;
602 std::vector<NumberFormatSpan> out_parts;
608 while (climber < entire_size) {
609 int32_t next_region_begin_pos;
610 if (region_iterator < regions->size()) {
611 next_region_begin_pos = regions->at(region_iterator).begin_pos;
614 next_region_begin_pos = entire_size;
617 if (climber < next_region_begin_pos) {
618 while (top_region.end_pos < next_region_begin_pos) {
619 if (climber < top_region.end_pos) {
621 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
622 top_region.end_pos));
623 climber = top_region.end_pos;
627 overlapping_region_index_stack.pop_back();
628 top_region = regions->at(overlapping_region_index_stack.back());
630 if (climber < next_region_begin_pos) {
632 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
633 next_region_begin_pos));
634 climber = next_region_begin_pos;
637 if (region_iterator < regions->size()) {
638 overlapping_region_index_stack.push_back(region_iterator++);
639 top_region = regions->at(overlapping_region_index_stack.back());
645 Maybe<int> JSNumberFormat::FormatToParts(Isolate* isolate,
646 Handle<JSArray> result,
648 const icu::NumberFormat& fmt,
649 double number, Handle<String> unit) {
650 icu::UnicodeString formatted;
651 icu::FieldPositionIterator fp_iter;
652 UErrorCode status = U_ZERO_ERROR;
653 fmt.format(number, formatted, &fp_iter, status);
654 if (U_FAILURE(status)) {
655 THROW_NEW_ERROR_RETURN_VALUE(
656 isolate, NewTypeError(MessageTemplate::kIcuError), Nothing<int>());
659 int32_t length = formatted.length();
660 int index = start_index;
661 if (length == 0)
return Just(index);
663 std::vector<NumberFormatSpan> regions;
668 regions.push_back(NumberFormatSpan(-1, 0, formatted.length()));
671 icu::FieldPosition fp;
672 while (fp_iter.next(fp)) {
673 regions.push_back(NumberFormatSpan(fp.getField(), fp.getBeginIndex(),
678 std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(®ions);
680 for (
auto it = parts.begin(); it < parts.end(); it++) {
681 NumberFormatSpan part = *it;
682 Handle<String> field_type_string =
684 ? isolate->factory()->literal_string()
685 : IcuNumberFieldIdToNumberType(part.field_id, number, isolate);
686 Handle<String> substring;
687 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
689 Intl::ToString(isolate, formatted, part.begin_pos, part.end_pos),
691 if (unit.is_null()) {
692 Intl::AddElement(isolate, result, index, field_type_string, substring);
694 Intl::AddElement(isolate, result, index, field_type_string, substring,
695 isolate->factory()->unit_string(), unit);
699 JSObject::ValidateElements(*result);
703 MaybeHandle<JSArray> JSNumberFormat::FormatToParts(
704 Isolate* isolate, Handle<JSNumberFormat> number_format,
double number) {
705 Factory* factory = isolate->factory();
706 icu::NumberFormat* fmt = number_format->icu_number_format()->raw();
709 Handle<JSArray> result = factory->NewJSArray(0);
711 Maybe<int> maybe_format_to_parts = JSNumberFormat::FormatToParts(
712 isolate, result, 0, *fmt, number, Handle<String>());
713 MAYBE_RETURN(maybe_format_to_parts, Handle<JSArray>());
718 std::set<std::string> JSNumberFormat::GetAvailableLocales() {
719 int32_t num_locales = 0;
720 const icu::Locale* icu_available_locales =
721 icu::NumberFormat::getAvailableLocales(num_locales);
722 return Intl::BuildLocaleSet(icu_available_locales, num_locales);