2021-09-08 21:34:27 -04:00
/*
2025-02-28 08:54:42 -05:00
* Copyright ( c ) 2021 - 2025 , Tim Flynn < trflynn89 @ ladybird . org >
2021-09-08 21:34:27 -04:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/AbstractOperations.h>
2021-09-10 11:12:05 -04:00
# include <LibJS/Runtime/Array.h>
2021-09-08 21:34:27 -04:00
# include <LibJS/Runtime/GlobalObject.h>
2021-09-10 11:04:54 -04:00
# include <LibJS/Runtime/Intl/AbstractOperations.h>
2021-09-08 21:34:27 -04:00
# include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
2024-06-23 09:14:27 -04:00
# include <LibUnicode/Locale.h>
2021-09-08 21:34:27 -04:00
namespace JS : : Intl {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( NumberFormatConstructor ) ;
2023-11-19 09:45:05 +01:00
2025-02-28 08:54:42 -05:00
// 16.1 The Intl.NumberFormat Constructor, https://tc39.es/ecma402/#sec-intl-numberformat-constructor
2022-08-16 00:20:49 +01:00
NumberFormatConstructor : : NumberFormatConstructor ( Realm & realm )
2023-04-13 00:47:15 +02:00
: NativeFunction ( realm . vm ( ) . names . NumberFormat . as_string ( ) , realm . intrinsics ( ) . function_prototype ( ) )
2021-09-08 21:34:27 -04:00
{
}
2023-08-07 08:41:28 +02:00
void NumberFormatConstructor : : initialize ( Realm & realm )
2021-09-08 21:34:27 -04:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2021-09-08 21:34:27 -04:00
auto & vm = this - > vm ( ) ;
2025-02-28 08:54:42 -05:00
// 16.2.1 Intl.NumberFormat.prototype, https://tc39.es/ecma402/#sec-intl.numberformat.prototype
2022-08-27 00:54:55 +01:00
define_direct_property ( vm . names . prototype , realm . intrinsics ( ) . intl_number_format_prototype ( ) , 0 ) ;
2021-09-10 11:12:05 -04:00
u8 attr = Attribute : : Writable | Attribute : : Configurable ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . supportedLocalesOf , supported_locales_of , 1 , attr ) ;
2021-09-10 11:12:05 -04:00
2021-09-08 21:34:27 -04:00
define_direct_property ( vm . names . length , Value ( 0 ) , Attribute : : Configurable ) ;
}
2025-02-28 08:54:42 -05:00
// 16.1.1 Intl.NumberFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.numberformat
2021-10-20 21:16:30 +01:00
ThrowCompletionOr < Value > NumberFormatConstructor : : call ( )
2021-09-08 21:34:27 -04:00
{
// 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.
2021-10-20 21:16:30 +01:00
return TRY ( construct ( * this ) ) ;
2021-09-08 21:34:27 -04:00
}
2025-02-28 08:54:42 -05:00
// 16.1.1 Intl.NumberFormat ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sec-intl.numberformat
2024-11-15 04:01:23 +13:00
ThrowCompletionOr < GC : : Ref < Object > > NumberFormatConstructor : : construct ( FunctionObject & new_target )
2021-09-08 21:34:27 -04:00
{
auto & vm = this - > vm ( ) ;
2024-08-15 16:14:53 -04:00
auto locales_value = vm . argument ( 0 ) ;
auto options_value = vm . argument ( 1 ) ;
2021-09-10 11:04:54 -04:00
2024-08-15 16:14:53 -04:00
// 2. Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget, "%Intl.NumberFormat.prototype%", « [[InitializedNumberFormat]], [[Locale]], [[LocaleData]], [[NumberingSystem]], [[Style]], [[Unit]], [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencySign]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[RoundingIncrement]], [[RoundingMode]], [[ComputedRoundingPriority]], [[TrailingZeroDisplay]], [[BoundFormat]] »).
2022-12-14 18:34:32 +00:00
auto number_format = TRY ( ordinary_create_from_constructor < NumberFormat > ( vm , new_target , & Intrinsics : : intl_number_format_prototype ) ) ;
2021-09-08 21:34:27 -04:00
2025-04-07 17:39:23 -04:00
// 3. Let optionsResolution be ? ResolveOptions(%Intl.NumberFormat%, %Intl.NumberFormat%.[[LocaleData]], locales, options, « COERCE-OPTIONS »).
// 4. Set options to optionsResolution.[[Options]].
// 5. Let r be optionsResolution.[[ResolvedLocale]].
auto [ options , result , _ ] = TRY ( resolve_options ( vm , number_format , locales_value , options_value , SpecialBehaviors : : CoerceOptions ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 6. Set numberFormat.[[Locale]] to r.[[Locale]].
2024-08-15 16:14:53 -04:00
number_format - > set_locale ( move ( result . locale ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 7. Set numberFormat.[[LocaleData]] to r.[[LocaleData]].
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 8. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
2024-06-16 15:36:34 -04:00
if ( auto * resolved_numbering_system = result . nu . get_pointer < String > ( ) )
2024-08-15 16:14:53 -04:00
number_format - > set_numbering_system ( move ( * resolved_numbering_system ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 9. Perform ? SetNumberFormatUnitOptions(numberFormat, options).
2022-08-20 08:25:24 +01:00
TRY ( set_number_format_unit_options ( vm , number_format , * options ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 10. Let style be numberFormat.[[Style]].
2024-08-15 16:14:53 -04:00
auto style = number_format - > style ( ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 11. Let notation be ? GetOption(options, "notation", STRING, « "standard", "scientific", "engineering", "compact" », "standard").
2024-12-06 12:16:33 -05:00
auto notation = TRY ( get_option ( vm , * options , vm . names . notation , OptionType : : String , { " standard " sv , " scientific " sv , " engineering " sv , " compact " sv } , " standard " sv ) ) ;
2025-04-07 17:39:23 -04:00
// 12. Set numberFormat.[[Notation]] to notation.
2024-12-06 12:16:33 -05:00
number_format - > set_notation ( notation . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:58:47 -04:00
int default_min_fraction_digits = 0 ;
int default_max_fraction_digits = 0 ;
2025-04-07 17:39:23 -04:00
// 13. If style is "currency" and notation is "standard", then
2024-12-06 12:16:33 -05:00
if ( style = = Unicode : : NumberFormatStyle : : Currency & & number_format - > notation ( ) = = Unicode : : Notation : : Standard ) {
2022-03-15 10:58:47 -04:00
// a. Let currency be numberFormat.[[Currency]].
2024-08-15 16:14:53 -04:00
auto const & currency = number_format - > currency ( ) ;
2022-03-15 10:58:47 -04:00
// b. Let cDigits be CurrencyDigits(currency).
int digits = currency_digits ( currency ) ;
// c. Let mnfdDefault be cDigits.
default_min_fraction_digits = digits ;
// d. Let mxfdDefault be cDigits.
default_max_fraction_digits = digits ;
}
2025-04-07 17:39:23 -04:00
// 14. Else,
2022-03-15 10:58:47 -04:00
else {
// a. Let mnfdDefault be 0.
default_min_fraction_digits = 0 ;
// b. If style is "percent", then
// i. Let mxfdDefault be 0.
// c. Else,
// i. Let mxfdDefault be 3.
2024-06-23 09:14:27 -04:00
default_max_fraction_digits = style = = Unicode : : NumberFormatStyle : : Percent ? 0 : 3 ;
2022-03-15 10:58:47 -04:00
}
2025-04-07 17:39:23 -04:00
// 15. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation).
2024-08-15 16:14:53 -04:00
TRY ( set_number_format_digit_options ( vm , number_format , * options , default_min_fraction_digits , default_max_fraction_digits , number_format - > notation ( ) ) ) ;
2022-11-28 12:09:59 -05:00
2025-04-07 17:39:23 -04:00
// 16. Let compactDisplay be ? GetOption(options, "compactDisplay", STRING, « "short", "long" », "short").
2022-08-20 08:52:42 +01:00
auto compact_display = TRY ( get_option ( vm , * options , vm . names . compactDisplay , OptionType : : String , { " short " sv , " long " sv } , " short " sv ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 17. Let defaultUseGrouping be "auto".
2022-07-12 13:18:23 -04:00
auto default_use_grouping = " auto " sv ;
2025-04-07 17:39:23 -04:00
// 18. If notation is "compact", then
2024-08-15 16:14:53 -04:00
if ( number_format - > notation ( ) = = Unicode : : Notation : : Compact ) {
2022-03-15 10:58:47 -04:00
// a. Set numberFormat.[[CompactDisplay]] to compactDisplay.
2024-08-15 16:14:53 -04:00
number_format - > set_compact_display ( compact_display . as_string ( ) . utf8_string_view ( ) ) ;
2022-07-12 13:18:23 -04:00
// b. Set defaultUseGrouping to "min2".
default_use_grouping = " min2 " sv ;
2022-03-15 10:58:47 -04:00
}
2025-04-07 17:39:23 -04:00
// 19. NOTE: For historical reasons, the strings "true" and "false" are accepted and replaced with the default value.
// 20. Let useGrouping be ? GetBooleanOrStringNumberFormatOption(options, "useGrouping", « "min2", "auto", "always", "true", "false" », defaultUseGrouping).
2023-01-30 10:36:47 -05:00
auto use_grouping = TRY ( get_boolean_or_string_number_format_option ( vm , * options , vm . names . useGrouping , { " min2 " sv , " auto " sv , " always " sv , " true " sv , " false " sv } , default_use_grouping ) ) ;
2025-04-07 17:39:23 -04:00
// 21. If useGrouping is "true" or useGrouping is "false", set useGrouping to defaultUseGrouping.
2023-01-30 10:36:47 -05:00
if ( auto const * use_grouping_string = use_grouping . get_pointer < StringView > ( ) ) {
if ( use_grouping_string - > is_one_of ( " true " sv , " false " sv ) )
use_grouping = default_use_grouping ;
}
2025-04-07 17:39:23 -04:00
// 22. If useGrouping is true, set useGrouping to "always".
2023-01-30 10:36:47 -05:00
if ( auto const * use_grouping_boolean = use_grouping . get_pointer < bool > ( ) ) {
if ( * use_grouping_boolean )
use_grouping = " always " sv ;
}
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 23. Set numberFormat.[[UseGrouping]] to useGrouping.
2024-08-15 16:14:53 -04:00
number_format - > set_use_grouping ( use_grouping ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 24. Let signDisplay be ? GetOption(options, "signDisplay", STRING, « "auto", "never", "always", "exceptZero", "negative" », "auto").
2022-08-20 08:52:42 +01:00
auto sign_display = TRY ( get_option ( vm , * options , vm . names . signDisplay , OptionType : : String , { " auto " sv , " never " sv , " always " sv , " exceptZero " sv , " negative " sv } , " auto " sv ) ) ;
2022-03-15 10:58:47 -04:00
2025-04-07 17:39:23 -04:00
// 25. Set numberFormat.[[SignDisplay]] to signDisplay.
2024-08-15 16:14:53 -04:00
number_format - > set_sign_display ( sign_display . as_string ( ) . utf8_string_view ( ) ) ;
2025-04-07 17:39:23 -04:00
// 26. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then
2024-08-15 16:14:53 -04:00
// a. Let this be the this value.
// b. Return ? ChainNumberFormat(numberFormat, NewTarget, this).
2022-03-15 10:58:47 -04:00
2024-06-09 14:36:48 -04:00
// Non-standard, create an ICU number formatter for this Intl object.
2024-06-23 09:14:27 -04:00
auto formatter = Unicode : : NumberFormat : : create (
2025-03-17 16:24:09 -04:00
result . icu_locale ,
2024-08-15 16:14:53 -04:00
number_format - > display_options ( ) ,
number_format - > rounding_options ( ) ) ;
number_format - > set_formatter ( move ( formatter ) ) ;
2024-06-09 14:36:48 -04:00
2025-04-07 17:39:23 -04:00
// 27. Return numberFormat.
2023-08-30 12:30:39 -04:00
return number_format ;
2022-03-15 10:58:47 -04:00
}
2025-02-28 08:54:42 -05:00
// 16.1.2 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ), https://tc39.es/ecma402/#sec-setnfdigitoptions
2024-06-23 09:14:27 -04:00
ThrowCompletionOr < void > set_number_format_digit_options ( VM & vm , NumberFormatBase & intl_object , Object const & options , int default_min_fraction_digits , int default_max_fraction_digits , Unicode : : Notation notation )
2022-03-15 10:58:47 -04:00
{
// 1. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1).
2022-08-20 08:25:24 +01:00
auto min_integer_digits = TRY ( get_number_option ( vm , options , vm . names . minimumIntegerDigits , 1 , 21 , 1 ) ) ;
2022-03-15 10:58:47 -04:00
// 2. Let mnfd be ? Get(options, "minimumFractionDigits").
auto min_fraction_digits = TRY ( options . get ( vm . names . minimumFractionDigits ) ) ;
// 3. Let mxfd be ? Get(options, "maximumFractionDigits").
auto max_fraction_digits = TRY ( options . get ( vm . names . maximumFractionDigits ) ) ;
// 4. Let mnsd be ? Get(options, "minimumSignificantDigits").
auto min_significant_digits = TRY ( options . get ( vm . names . minimumSignificantDigits ) ) ;
// 5. Let mxsd be ? Get(options, "maximumSignificantDigits").
auto max_significant_digits = TRY ( options . get ( vm . names . maximumSignificantDigits ) ) ;
// 6. Set intlObj.[[MinimumIntegerDigits]] to mnid.
intl_object . set_min_integer_digits ( * min_integer_digits ) ;
2023-10-04 17:01:25 -04:00
// 7. Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement", 1, 5000, 1).
2023-01-30 09:44:00 -05:00
auto rounding_increment = TRY ( get_number_option ( vm , options , vm . names . roundingIncrement , 1 , 5000 , 1 ) ) ;
2023-10-04 17:01:25 -04:00
// 8. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception.
2023-01-30 09:44:00 -05:00
static constexpr auto sanctioned_rounding_increments = AK : : Array { 1 , 2 , 5 , 10 , 20 , 25 , 50 , 100 , 200 , 250 , 500 , 1000 , 2000 , 2500 , 5000 } ;
if ( ! sanctioned_rounding_increments . span ( ) . contains_slow ( * rounding_increment ) )
return vm . throw_completion < RangeError > ( ErrorType : : IntlInvalidRoundingIncrement , * rounding_increment ) ;
2024-12-06 12:15:55 -05:00
// 9. Let roundingMode be ? GetOption(options, "roundingMode", STRING, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfExpand").
2023-01-30 09:44:00 -05:00
auto rounding_mode = TRY ( get_option ( vm , options , vm . names . roundingMode , OptionType : : String , { " ceil " sv , " floor " sv , " expand " sv , " trunc " sv , " halfCeil " sv , " halfFloor " sv , " halfExpand " sv , " halfTrunc " sv , " halfEven " sv } , " halfExpand " sv ) ) ;
2024-12-06 12:15:55 -05:00
// 10. Let roundingPriority be ? GetOption(options, "roundingPriority", STRING, « "auto", "morePrecision", "lessPrecision" », "auto").
2023-10-04 17:01:25 -04:00
auto rounding_priority_option = TRY ( get_option ( vm , options , vm . names . roundingPriority , OptionType : : String , { " auto " sv , " morePrecision " sv , " lessPrecision " sv } , " auto " sv ) ) ;
auto rounding_priority = rounding_priority_option . as_string ( ) . utf8_string_view ( ) ;
2024-12-06 12:15:55 -05:00
// 11. Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay", STRING, « "auto", "stripIfInteger" », "auto").
2023-01-30 09:44:00 -05:00
auto trailing_zero_display = TRY ( get_option ( vm , options , vm . names . trailingZeroDisplay , OptionType : : String , { " auto " sv , " stripIfInteger " sv } , " auto " sv ) ) ;
// 12. NOTE: All fields required by SetNumberFormatDigitOptions have now been read from options. The remainder of this AO interprets the options and may throw exceptions.
// 13. If roundingIncrement is not 1, set mxfdDefault to mnfdDefault.
if ( rounding_increment ! = 1 )
default_max_fraction_digits = default_min_fraction_digits ;
// 14. Set intlObj.[[RoundingIncrement]] to roundingIncrement.
intl_object . set_rounding_increment ( * rounding_increment ) ;
// 15. Set intlObj.[[RoundingMode]] to roundingMode.
2023-08-08 19:17:55 +02:00
intl_object . set_rounding_mode ( rounding_mode . as_string ( ) . utf8_string_view ( ) ) ;
2023-01-30 09:44:00 -05:00
// 16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.
2023-08-08 19:17:55 +02:00
intl_object . set_trailing_zero_display ( trailing_zero_display . as_string ( ) . utf8_string_view ( ) ) ;
2023-01-30 09:44:00 -05:00
2024-08-15 16:14:53 -04:00
// 17. If mnsd is undefined and mxsd is undefined, let hasSd be false. Otherwise, let hasSd be true.
2022-03-15 10:58:47 -04:00
bool has_significant_digits = ! min_significant_digits . is_undefined ( ) | | ! max_significant_digits . is_undefined ( ) ;
2024-08-15 16:14:53 -04:00
// 18. If mnfd is undefined and mxsd is undefined, let hasFd be false. Otherwise, let hasFd be true.
2022-03-15 10:58:47 -04:00
bool has_fraction_digits = ! min_fraction_digits . is_undefined ( ) | | ! max_fraction_digits . is_undefined ( ) ;
2024-08-15 16:14:53 -04:00
// 19. Let needSd be true.
2022-07-12 13:18:23 -04:00
bool need_significant_digits = true ;
2024-08-15 16:14:53 -04:00
// 20. Let needFd be true.
2022-07-12 13:18:23 -04:00
bool need_fraction_digits = true ;
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 21. If roundingPriority is "auto", then
2023-04-11 08:45:51 -04:00
if ( rounding_priority = = " auto " sv ) {
2022-07-12 13:18:23 -04:00
// a. Set needSd to hasSd.
need_significant_digits = has_significant_digits ;
2024-12-06 12:16:33 -05:00
// b. If needSd is true, or hasFd is false and notation is "compact", then
if ( need_significant_digits | | ( ! has_fraction_digits & & notation = = Unicode : : Notation : : Compact ) ) {
2022-07-12 13:18:23 -04:00
// i. Set needFd to false.
need_fraction_digits = false ;
}
}
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 22. If needSd is true, then
2022-03-15 10:58:47 -04:00
if ( need_significant_digits ) {
2022-07-12 13:18:23 -04:00
// a. If hasSd is true, then
if ( has_significant_digits ) {
2023-04-11 08:45:51 -04:00
// i. Set intlObj.[[MinimumSignificantDigits]] to ? DefaultNumberOption(mnsd, 1, 21, 1).
2022-08-20 08:25:24 +01:00
auto min_digits = TRY ( default_number_option ( vm , min_significant_digits , 1 , 21 , 1 ) ) ;
2022-07-12 13:18:23 -04:00
intl_object . set_min_significant_digits ( * min_digits ) ;
2022-03-15 10:58:47 -04:00
2023-04-11 08:45:51 -04:00
// ii. Set intlObj.[[MaximumSignificantDigits]] to ? DefaultNumberOption(mxsd, intlObj.[[MinimumSignificantDigits]], 21, 21).
auto max_digits = TRY ( default_number_option ( vm , max_significant_digits , * min_digits , 21 , 21 ) ) ;
2022-07-12 13:18:23 -04:00
intl_object . set_max_significant_digits ( * max_digits ) ;
}
// b. Else,
else {
// i. Set intlObj.[[MinimumSignificantDigits]] to 1.
intl_object . set_min_significant_digits ( 1 ) ;
2022-03-15 10:58:47 -04:00
2022-07-12 13:18:23 -04:00
// ii. Set intlObj.[[MaximumSignificantDigits]] to 21.
intl_object . set_max_significant_digits ( 21 ) ;
}
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 23. If needFd is true, then
2022-03-15 10:58:47 -04:00
if ( need_fraction_digits ) {
// a. If hasFd is true, then
if ( has_fraction_digits ) {
2023-07-21 20:49:29 -04:00
// i. Set mnfd to ? DefaultNumberOption(mnfd, 0, 100, undefined).
auto min_digits = TRY ( default_number_option ( vm , min_fraction_digits , 0 , 100 , { } ) ) ;
2022-03-15 10:58:47 -04:00
2023-07-21 20:49:29 -04:00
// ii. Set mxfd to ? DefaultNumberOption(mxfd, 0, 100, undefined).
auto max_digits = TRY ( default_number_option ( vm , max_fraction_digits , 0 , 100 , { } ) ) ;
2022-03-15 10:58:47 -04:00
// iii. If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).
if ( ! min_digits . has_value ( ) )
min_digits = min ( default_min_fraction_digits , * max_digits ) ;
// iv. Else if mxfd is undefined, set mxfd to max(mxfdDefault, mnfd).
else if ( ! max_digits . has_value ( ) )
max_digits = max ( default_max_fraction_digits , * min_digits ) ;
// v. Else if mnfd is greater than mxfd, throw a RangeError exception.
else if ( * min_digits > * max_digits )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : IntlMinimumExceedsMaximum , * min_digits , * max_digits ) ;
2022-03-15 10:58:47 -04:00
// vi. Set intlObj.[[MinimumFractionDigits]] to mnfd.
intl_object . set_min_fraction_digits ( * min_digits ) ;
// vii. Set intlObj.[[MaximumFractionDigits]] to mxfd.
intl_object . set_max_fraction_digits ( * max_digits ) ;
}
// b. Else,
else {
// i. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
intl_object . set_min_fraction_digits ( default_min_fraction_digits ) ;
// ii. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
intl_object . set_max_fraction_digits ( default_max_fraction_digits ) ;
}
}
2024-08-15 16:14:53 -04:00
// 24. If needSd is false and needFd is false, then
2023-04-11 08:45:51 -04:00
if ( ! need_significant_digits & & ! need_fraction_digits ) {
// a. Set intlObj.[[MinimumFractionDigits]] to 0.
2022-07-12 13:18:23 -04:00
intl_object . set_min_fraction_digits ( 0 ) ;
2023-04-11 08:45:51 -04:00
// b. Set intlObj.[[MaximumFractionDigits]] to 0.
2022-07-12 13:18:23 -04:00
intl_object . set_max_fraction_digits ( 0 ) ;
2023-04-11 08:45:51 -04:00
// c. Set intlObj.[[MinimumSignificantDigits]] to 1.
2022-07-12 13:18:23 -04:00
intl_object . set_min_significant_digits ( 1 ) ;
2023-04-11 08:45:51 -04:00
// d. Set intlObj.[[MaximumSignificantDigits]] to 2.
2022-07-12 13:18:23 -04:00
intl_object . set_max_significant_digits ( 2 ) ;
2023-04-11 08:45:51 -04:00
2024-08-15 16:14:53 -04:00
// e. Set intlObj.[[RoundingType]] to MORE-PRECISION.
2024-06-23 09:14:27 -04:00
intl_object . set_rounding_type ( Unicode : : RoundingType : : MorePrecision ) ;
2023-08-11 07:50:34 -04:00
// f. Set intlObj.[[ComputedRoundingPriority]] to "morePrecision".
intl_object . set_computed_rounding_priority ( NumberFormatBase : : ComputedRoundingPriority : : MorePrecision ) ;
2023-04-11 08:45:51 -04:00
}
2024-08-15 16:14:53 -04:00
// 25. Else if roundingPriority is "morePrecision", then
2023-04-11 08:45:51 -04:00
else if ( rounding_priority = = " morePrecision " sv ) {
2024-08-15 16:14:53 -04:00
// a. Set intlObj.[[RoundingType]] to MORE-PRECISION.
2024-06-23 09:14:27 -04:00
intl_object . set_rounding_type ( Unicode : : RoundingType : : MorePrecision ) ;
2023-04-11 08:45:51 -04:00
2023-08-11 07:50:34 -04:00
// b. Set intlObj.[[ComputedRoundingPriority]] to "morePrecision".
intl_object . set_computed_rounding_priority ( NumberFormatBase : : ComputedRoundingPriority : : MorePrecision ) ;
2023-04-11 08:45:51 -04:00
}
2024-08-15 16:14:53 -04:00
// 26. Else if roundingPriority is "lessPrecision", then
2023-04-11 08:45:51 -04:00
else if ( rounding_priority = = " lessPrecision " sv ) {
2024-08-15 16:14:53 -04:00
// a. Set intlObj.[[RoundingType]] to LESS-PRECISION.
2024-06-23 09:14:27 -04:00
intl_object . set_rounding_type ( Unicode : : RoundingType : : LessPrecision ) ;
2023-08-11 07:50:34 -04:00
// b. Set intlObj.[[ComputedRoundingPriority]] to "lessPrecision".
intl_object . set_computed_rounding_priority ( NumberFormatBase : : ComputedRoundingPriority : : LessPrecision ) ;
2023-04-11 08:45:51 -04:00
}
2024-08-15 16:14:53 -04:00
// 27. Else if hasSd is true, then
2023-04-11 08:45:51 -04:00
else if ( has_significant_digits ) {
2024-08-15 16:14:53 -04:00
// a. Set intlObj.[[RoundingType]] to SIGNIFICANT-DIGITS.
2024-06-23 09:14:27 -04:00
intl_object . set_rounding_type ( Unicode : : RoundingType : : SignificantDigits ) ;
2023-08-11 07:50:34 -04:00
// b. Set intlObj.[[ComputedRoundingPriority]] to "auto".
intl_object . set_computed_rounding_priority ( NumberFormatBase : : ComputedRoundingPriority : : Auto ) ;
2023-04-11 08:45:51 -04:00
}
2024-08-15 16:14:53 -04:00
// 28. Else,
2023-04-11 08:45:51 -04:00
else {
2024-08-15 16:14:53 -04:00
// a. Set intlObj.[[RoundingType]] to FRACTION-DIGITS.
2024-06-23 09:14:27 -04:00
intl_object . set_rounding_type ( Unicode : : RoundingType : : FractionDigits ) ;
2023-08-11 07:50:34 -04:00
// b. Set intlObj.[[ComputedRoundingPriority]] to "auto".
intl_object . set_computed_rounding_priority ( NumberFormatBase : : ComputedRoundingPriority : : Auto ) ;
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 29. If roundingIncrement is not 1, then
2023-01-30 09:44:00 -05:00
if ( rounding_increment ! = 1 ) {
2024-08-15 16:14:53 -04:00
// a. If intlObj.[[RoundingType]] is not FRACTION-DIGITS, throw a TypeError exception.
2024-06-23 09:14:27 -04:00
if ( intl_object . rounding_type ( ) ! = Unicode : : RoundingType : : FractionDigits )
2023-01-30 09:44:00 -05:00
return vm . throw_completion < TypeError > ( ErrorType : : IntlInvalidRoundingIncrementForRoundingType , * rounding_increment , intl_object . rounding_type_string ( ) ) ;
2024-12-06 12:16:33 -05:00
// b. If intlObj.[[MaximumFractionDigits]] is not intlObj.[[MinimumFractionDigits]], throw a RangeError exception.
2023-01-30 09:44:00 -05:00
if ( intl_object . max_fraction_digits ( ) ! = intl_object . min_fraction_digits ( ) )
return vm . throw_completion < RangeError > ( ErrorType : : IntlInvalidRoundingIncrementForFractionDigits , * rounding_increment ) ;
}
2024-08-15 16:14:53 -04:00
// 30. Return UNUSED.
2022-03-15 10:58:47 -04:00
return { } ;
}
2025-02-28 08:54:42 -05:00
// 16.1.3 SetNumberFormatUnitOptions ( intlObj, options ), https://tc39.es/ecma402/#sec-setnumberformatunitoptions
2022-08-20 08:25:24 +01:00
ThrowCompletionOr < void > set_number_format_unit_options ( VM & vm , NumberFormat & intl_object , Object const & options )
2022-03-15 10:58:47 -04:00
{
2024-12-06 12:15:55 -05:00
// 1. Let style be ? GetOption(options, "style", STRING, « "decimal", "percent", "currency", "unit" », "decimal").
2022-08-20 08:52:42 +01:00
auto style = TRY ( get_option ( vm , options , vm . names . style , OptionType : : String , { " decimal " sv , " percent " sv , " currency " sv , " unit " sv } , " decimal " sv ) ) ;
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 2. Set intlObj.[[Style]] to style.
2023-08-08 19:17:55 +02:00
intl_object . set_style ( style . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:58:47 -04:00
2024-12-06 12:15:55 -05:00
// 3. Let currency be ? GetOption(options, "currency", STRING, EMPTY, undefined).
2022-08-20 08:52:42 +01:00
auto currency = TRY ( get_option ( vm , options , vm . names . currency , OptionType : : String , { } , Empty { } ) ) ;
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 4. If currency is undefined, then
2022-03-15 10:58:47 -04:00
if ( currency . is_undefined ( ) ) {
// a. If style is "currency", throw a TypeError exception.
2024-06-23 09:14:27 -04:00
if ( intl_object . style ( ) = = Unicode : : NumberFormatStyle : : Currency )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : IntlOptionUndefined , " currency " sv , " style " sv , style ) ;
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 5. Else,
2023-04-11 08:45:51 -04:00
// a. If IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.
2024-08-15 16:14:53 -04:00
else if ( ! is_well_formed_currency_code ( currency . as_string ( ) . utf8_string_view ( ) ) ) {
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : OptionIsNotValidValue , currency , " currency " sv ) ;
2024-08-15 16:14:53 -04:00
}
2022-03-15 10:58:47 -04:00
2024-12-06 12:15:55 -05:00
// 6. Let currencyDisplay be ? GetOption(options, "currencyDisplay", STRING, « "code", "symbol", "narrowSymbol", "name" », "symbol").
2022-08-20 08:52:42 +01:00
auto currency_display = TRY ( get_option ( vm , options , vm . names . currencyDisplay , OptionType : : String , { " code " sv , " symbol " sv , " narrowSymbol " sv , " name " sv } , " symbol " sv ) ) ;
2022-03-15 10:58:47 -04:00
2024-12-06 12:15:55 -05:00
// 7. Let currencySign be ? GetOption(options, "currencySign", STRING, « "standard", "accounting" », "standard").
2022-08-20 08:52:42 +01:00
auto currency_sign = TRY ( get_option ( vm , options , vm . names . currencySign , OptionType : : String , { " standard " sv , " accounting " sv } , " standard " sv ) ) ;
2022-03-15 10:58:47 -04:00
2024-12-06 12:15:55 -05:00
// 8. Let unit be ? GetOption(options, "unit", STRING, EMPTY, undefined).
2022-08-20 08:52:42 +01:00
auto unit = TRY ( get_option ( vm , options , vm . names . unit , OptionType : : String , { } , Empty { } ) ) ;
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 9. If unit is undefined, then
2022-03-15 10:58:47 -04:00
if ( unit . is_undefined ( ) ) {
// a. If style is "unit", throw a TypeError exception.
2024-06-23 09:14:27 -04:00
if ( intl_object . style ( ) = = Unicode : : NumberFormatStyle : : Unit )
2022-08-16 20:33:17 +01:00
return vm . throw_completion < TypeError > ( ErrorType : : IntlOptionUndefined , " unit " sv , " style " sv , style ) ;
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 10. Else,
// a. If IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.
else if ( ! is_well_formed_unit_identifier ( unit . as_string ( ) . utf8_string_view ( ) ) ) {
2022-08-16 20:33:17 +01:00
return vm . throw_completion < RangeError > ( ErrorType : : OptionIsNotValidValue , unit , " unit " sv ) ;
2024-08-15 16:14:53 -04:00
}
2022-03-15 10:58:47 -04:00
2024-12-06 12:15:55 -05:00
// 11. Let unitDisplay be ? GetOption(options, "unitDisplay", STRING, « "short", "narrow", "long" », "short").
2022-08-20 08:52:42 +01:00
auto unit_display = TRY ( get_option ( vm , options , vm . names . unitDisplay , OptionType : : String , { " short " sv , " narrow " sv , " long " sv } , " short " sv ) ) ;
2022-03-15 10:58:47 -04:00
2024-08-15 16:14:53 -04:00
// 12. If style is "currency", then
2024-06-23 09:14:27 -04:00
if ( intl_object . style ( ) = = Unicode : : NumberFormatStyle : : Currency ) {
2022-03-28 09:53:06 -04:00
// a. Set intlObj.[[Currency]] to the ASCII-uppercase of currency.
2023-08-30 12:30:39 -04:00
intl_object . set_currency ( MUST ( currency . as_string ( ) . utf8_string ( ) . to_uppercase ( ) ) ) ;
2022-03-15 10:58:47 -04:00
// c. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
2023-08-08 19:17:55 +02:00
intl_object . set_currency_display ( currency_display . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:58:47 -04:00
// d. Set intlObj.[[CurrencySign]] to currencySign.
2023-08-08 19:17:55 +02:00
intl_object . set_currency_sign ( currency_sign . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 13. If style is "unit", then
2024-06-23 09:14:27 -04:00
if ( intl_object . style ( ) = = Unicode : : NumberFormatStyle : : Unit ) {
2022-03-15 10:58:47 -04:00
// a. Set intlObj.[[Unit]] to unit.
2023-08-08 19:17:55 +02:00
intl_object . set_unit ( unit . as_string ( ) . utf8_string ( ) ) ;
2022-03-15 10:58:47 -04:00
// b. Set intlObj.[[UnitDisplay]] to unitDisplay.
2023-08-08 19:17:55 +02:00
intl_object . set_unit_display ( unit_display . as_string ( ) . utf8_string_view ( ) ) ;
2022-03-15 10:58:47 -04:00
}
2024-08-15 16:14:53 -04:00
// 14. Return UNUSED.
2022-03-15 10:58:47 -04:00
return { } ;
}
2025-02-28 08:54:42 -05:00
// 16.2.2 Intl.NumberFormat.supportedLocalesOf ( locales [ , options ] ), https://tc39.es/ecma402/#sec-intl.numberformat.supportedlocalesof
2024-08-15 16:14:53 -04:00
JS_DEFINE_NATIVE_FUNCTION ( NumberFormatConstructor : : supported_locales_of )
{
auto locales = vm . argument ( 0 ) ;
auto options = vm . argument ( 1 ) ;
// 1. Let availableLocales be %NumberFormat%.[[AvailableLocales]].
// 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
auto requested_locales = TRY ( canonicalize_locale_list ( vm , locales ) ) ;
// 3. Return ? FilterLocales(availableLocales, requestedLocales, options).
return TRY ( filter_locales ( vm , requested_locales , options ) ) ;
}
2021-09-08 21:34:27 -04:00
}