diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp index 4c2f91a256..403a774493 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp @@ -571,7 +571,7 @@ ThrowCompletionOr supported_locales(GlobalObject& global_object, Vector< auto* options_object = TRY(coerce_options_to_object(global_object, options)); // 2. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options_object, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options_object, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); Vector supported_locales; @@ -603,49 +603,7 @@ ThrowCompletionOr coerce_options_to_object(GlobalObject& global_object, return TRY(options.to_object(global_object)); } -// 9.2.13 GetOption ( options, property, type, values, fallback ), https://tc39.es/ecma402/#sec-getoption -ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, Value::Type type, Span values, Fallback fallback) -{ - auto& vm = global_object.vm(); - - // 1. Assert: Type(options) is Object. - - // 2. Let value be ? Get(options, property). - auto value = TRY(options.get(property)); - - // 3. If value is undefined, return fallback. - if (value.is_undefined()) { - return fallback.visit( - [](Empty) { return js_undefined(); }, - [](bool f) { return Value(f); }, - [&vm](StringView f) { return Value(js_string(vm, f)); }); - } - - // 4. Assert: type is "boolean" or "string". - VERIFY((type == Value::Type::Boolean) || (type == Value::Type::String)); - - // 5. If type is "boolean", then - if (type == Value::Type::Boolean) { - // a. Set value to ToBoolean(value). - value = Value(value.to_boolean()); - } - // 6. If type is "string", then - else { - // a. Set value to ? ToString(value). - value = TRY(value.to_primitive_string(global_object)); - } - - // 7. If values is not undefined and values does not contain an element equal to value, throw a RangeError exception. - if (!values.is_empty()) { - // Note: Every location in the spec that invokes GetOption with type=boolean also has values=undefined. - VERIFY(value.is_string()); - if (!values.contains_slow(value.as_string().string())) - return vm.throw_completion(global_object, ErrorType::OptionIsNotValidValue, value.to_string_without_side_effects(), property.as_string()); - } - - // 8. Return value. - return value; -} +// NOTE: 9.2.13 GetOption has been removed and is being pulled in from ECMA-262 in the Temporal proposal. // 9.2.14 DefaultNumberOption ( value, minimum, maximum, fallback ), https://tc39.es/ecma402/#sec-defaultnumberoption ThrowCompletionOr> default_number_option(GlobalObject& global_object, Value value, int minimum, int maximum, Optional fallback) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h index 55f146cee1..920b2088d6 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h @@ -13,13 +13,12 @@ #include #include #include +#include #include #include namespace JS::Intl { -using Fallback = Variant; - struct LocaleOptions { Value locale_matcher; Optional ca; // [[Calendar]] @@ -72,15 +71,13 @@ Vector lookup_supported_locales(Vector const& requested_locales) Vector best_fit_supported_locales(Vector const& requested_locales); ThrowCompletionOr supported_locales(GlobalObject&, Vector const& requested_locales, Value options); ThrowCompletionOr coerce_options_to_object(GlobalObject& global_object, Value options); -ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, Value::Type type, Span values, Fallback fallback); ThrowCompletionOr> default_number_option(GlobalObject& global_object, Value value, int minimum, int maximum, Optional fallback); ThrowCompletionOr> get_number_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, int minimum, int maximum, Optional fallback); Vector partition_pattern(StringView pattern); -template -ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, Value::Type type, StringView const (&values)[Size], Fallback fallback) -{ - return get_option(global_object, options, property, type, Span { values }, fallback); -} +// NOTE: ECMA-402's GetOption is being removed in favor of a shared ECMA-262 GetOption in the Temporal proposal. +// Until Temporal is merged into ECMA-262, our implementation lives in the Temporal-specific AO file & namespace. +using Temporal::get_option; +using Temporal::OptionType; } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp index e31de7272c..76440d657f 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp @@ -26,7 +26,7 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj auto* options = TRY(coerce_options_to_object(global_object, options_value)); // 3. Let usage be ? GetOption(options, "usage", "string", « "sort", "search" », "sort"). - auto usage = TRY(get_option(global_object, *options, vm.names.usage, Value::Type::String, { "sort"sv, "search"sv }, "sort"sv)); + auto usage = TRY(get_option(global_object, *options, vm.names.usage, OptionType::String, { "sort"sv, "search"sv }, "sort"sv)); // 4. Set collator.[[Usage]] to usage. collator.set_usage(usage.as_string().string()); @@ -40,13 +40,13 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj LocaleOptions opt {}; // 8. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); // 9. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; // 10. Let collation be ? GetOption(options, "collation", "string", undefined, undefined). - auto collation = TRY(get_option(global_object, *options, vm.names.collation, Value::Type::String, {}, Empty {})); + auto collation = TRY(get_option(global_object, *options, vm.names.collation, OptionType::String, {}, Empty {})); // 11. If collation is not undefined, then if (!collation.is_undefined()) { @@ -59,7 +59,7 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj } // 13. Let numeric be ? GetOption(options, "numeric", "boolean", undefined, undefined). - auto numeric = TRY(get_option(global_object, *options, vm.names.numeric, Value::Type::Boolean, {}, Empty {})); + auto numeric = TRY(get_option(global_object, *options, vm.names.numeric, OptionType::Boolean, {}, Empty {})); // 14. If numeric is not undefined, then // a. Let numeric be ! ToString(numeric). @@ -69,7 +69,7 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj // 16. Let caseFirst be ? GetOption(options, "caseFirst", "string", « "upper", "lower", "false" », undefined). // 17. Set opt.[[kf]] to caseFirst. - auto case_first = TRY(get_option(global_object, *options, vm.names.caseFirst, Value::Type::String, { "upper"sv, "lower"sv, "false"sv }, Empty {})); + auto case_first = TRY(get_option(global_object, *options, vm.names.caseFirst, OptionType::String, { "upper"sv, "lower"sv, "false"sv }, Empty {})); if (!case_first.is_undefined()) opt.kf = case_first.as_string().string(); @@ -100,7 +100,7 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj } // 26. Let sensitivity be ? GetOption(options, "sensitivity", "string", « "base", "accent", "case", "variant" », undefined). - auto sensitivity = TRY(get_option(global_object, *options, vm.names.sensitivity, Value::Type::String, { "base"sv, "accent"sv, "case"sv, "variant"sv }, Empty {})); + auto sensitivity = TRY(get_option(global_object, *options, vm.names.sensitivity, OptionType::String, { "base"sv, "accent"sv, "case"sv, "variant"sv }, Empty {})); // 27. If sensitivity is undefined, then if (sensitivity.is_undefined()) { @@ -122,7 +122,7 @@ static ThrowCompletionOr initialize_collator(GlobalObject& global_obj collator.set_sensitivity(sensitivity.as_string().string()); // 29. Let ignorePunctuation be ? GetOption(options, "ignorePunctuation", "boolean", undefined, false). - auto ignore_punctuation = TRY(get_option(global_object, *options, vm.names.ignorePunctuation, Value::Type::Boolean, {}, false)); + auto ignore_punctuation = TRY(get_option(global_object, *options, vm.names.ignorePunctuation, OptionType::Boolean, {}, false)); // 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation. collator.set_ignore_punctuation(ignore_punctuation.as_bool()); diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp index 7587dff0f6..4fa7b4b51e 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp @@ -97,13 +97,13 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo LocaleOptions opt {}; // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); // 5. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; // 6. Let calendar be ? GetOption(options, "calendar", "string", undefined, undefined). - auto calendar = TRY(get_option(global_object, *options, vm.names.calendar, Value::Type::String, {}, Empty {})); + auto calendar = TRY(get_option(global_object, *options, vm.names.calendar, OptionType::String, {}, Empty {})); // 7. If calendar is not undefined, then if (!calendar.is_undefined()) { @@ -116,7 +116,7 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo } // 9. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). - auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, Value::Type::String, {}, Empty {})); + auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {})); // 10. If numberingSystem is not undefined, then if (!numbering_system.is_undefined()) { @@ -129,10 +129,10 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo } // 12. Let hour12 be ? GetOption(options, "hour12", "boolean", undefined, undefined). - auto hour12 = TRY(get_option(global_object, *options, vm.names.hour12, Value::Type::Boolean, {}, Empty {})); + auto hour12 = TRY(get_option(global_object, *options, vm.names.hour12, OptionType::Boolean, {}, Empty {})); // 13. Let hourCycle be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined). - auto hour_cycle = TRY(get_option(global_object, *options, vm.names.hourCycle, Value::Type::String, AK::Array { "h11"sv, "h12"sv, "h23"sv, "h24"sv }, Empty {})); + auto hour_cycle = TRY(get_option(global_object, *options, vm.names.hourCycle, OptionType::String, AK::Array { "h11"sv, "h12"sv, "h23"sv, "h24"sv }, Empty {})); // 14. If hour12 is not undefined, then if (!hour12.is_undefined()) { @@ -273,7 +273,7 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo else { // i. Let values be a List whose elements are the strings given in the Values column of the row. // ii. Let value be ? GetOption(options, prop, "string", values, undefined). - auto value = TRY(get_option(global_object, *options, property, Value::Type::String, values, Empty {})); + auto value = TRY(get_option(global_object, *options, property, OptionType::String, values, Empty {})); // d. Set formatOptions.[[]] to value. if (!value.is_undefined()) { @@ -289,17 +289,17 @@ ThrowCompletionOr initialize_date_time_format(GlobalObject& glo })); // 37. Let matcher be ? GetOption(options, "formatMatcher", "string", « "basic", "best fit" », "best fit"). - matcher = TRY(get_option(global_object, *options, vm.names.formatMatcher, Value::Type::String, AK::Array { "basic"sv, "best fit"sv }, "best fit"sv)); + matcher = TRY(get_option(global_object, *options, vm.names.formatMatcher, OptionType::String, AK::Array { "basic"sv, "best fit"sv }, "best fit"sv)); // 38. Let dateStyle be ? GetOption(options, "dateStyle", "string", « "full", "long", "medium", "short" », undefined). - auto date_style = TRY(get_option(global_object, *options, vm.names.dateStyle, Value::Type::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); + auto date_style = TRY(get_option(global_object, *options, vm.names.dateStyle, OptionType::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); // 39. Set dateTimeFormat.[[DateStyle]] to dateStyle. if (!date_style.is_undefined()) date_time_format.set_date_style(date_style.as_string().string()); // 40. Let timeStyle be ? GetOption(options, "timeStyle", "string", « "full", "long", "medium", "short" », undefined). - auto time_style = TRY(get_option(global_object, *options, vm.names.timeStyle, Value::Type::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); + auto time_style = TRY(get_option(global_object, *options, vm.names.timeStyle, OptionType::String, AK::Array { "full"sv, "long"sv, "medium"sv, "short"sv }, Empty {})); // 41. Set dateTimeFormat.[[TimeStyle]] to timeStyle. if (!time_style.is_undefined()) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp index cb62b6a160..a8bedd21c2 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp @@ -71,7 +71,7 @@ ThrowCompletionOr DisplayNamesConstructor::construct(FunctionObject& ne // 7. Let localeData be %DisplayNames%.[[LocaleData]]. // 8. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); // 9. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; @@ -80,13 +80,13 @@ ThrowCompletionOr DisplayNamesConstructor::construct(FunctionObject& ne auto result = resolve_locale(requested_locales, opt, {}); // 11. Let style be ? GetOption(options, "style", "string", « "narrow", "short", "long" », "long"). - auto style = TRY(get_option(global_object, *options, vm.names.style, Value::Type::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); + auto style = TRY(get_option(global_object, *options, vm.names.style, OptionType::String, { "narrow"sv, "short"sv, "long"sv }, "long"sv)); // 12. Set displayNames.[[Style]] to style. display_names->set_style(style.as_string().string()); // 13. Let type be ? GetOption(options, "type", "string", « "language", "region", "script", "currency", "calendar", "dateTimeField" », undefined). - auto type = TRY(get_option(global_object, *options, vm.names.type, Value::Type::String, { "language"sv, "region"sv, "script"sv, "currency"sv, "calendar"sv, "dateTimeField"sv }, Empty {})); + auto type = TRY(get_option(global_object, *options, vm.names.type, OptionType::String, { "language"sv, "region"sv, "script"sv, "currency"sv, "calendar"sv, "dateTimeField"sv }, Empty {})); // 14. If type is undefined, throw a TypeError exception. if (type.is_undefined()) @@ -96,7 +96,7 @@ ThrowCompletionOr DisplayNamesConstructor::construct(FunctionObject& ne display_names->set_type(type.as_string().string()); // 16. Let fallback be ? GetOption(options, "fallback", "string", « "code", "none" », "code"). - auto fallback = TRY(get_option(global_object, *options, vm.names.fallback, Value::Type::String, { "code"sv, "none"sv }, "code"sv)); + auto fallback = TRY(get_option(global_object, *options, vm.names.fallback, OptionType::String, { "code"sv, "none"sv }, "code"sv)); // 17. Set displayNames.[[Fallback]] to fallback. display_names->set_fallback(fallback.as_string().string()); @@ -112,7 +112,7 @@ ThrowCompletionOr DisplayNamesConstructor::construct(FunctionObject& ne // 22. Assert: types is a Record (see 12.4.3). // 23. Let languageDisplay be ? GetOption(options, "languageDisplay", "string", « "dialect", "standard" », "dialect"). - auto language_display = TRY(get_option(global_object, *options, vm.names.languageDisplay, Value::Type::String, { "dialect"sv, "standard"sv }, "dialect"sv)); + auto language_display = TRY(get_option(global_object, *options, vm.names.languageDisplay, OptionType::String, { "dialect"sv, "standard"sv }, "dialect"sv)); // 24. Let typeFields be types.[[]]. // 25. Assert: typeFields is a Record (see 12.4.3). diff --git a/Userland/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp index b1103aac42..cb449352cf 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/ListFormatConstructor.cpp @@ -64,7 +64,7 @@ ThrowCompletionOr ListFormatConstructor::construct(FunctionObject& new_ LocaleOptions opt {}; // 6. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); // 7. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; @@ -78,13 +78,13 @@ ThrowCompletionOr ListFormatConstructor::construct(FunctionObject& new_ list_format->set_locale(move(result.locale)); // 11. Let type be ? GetOption(options, "type", "string", « "conjunction", "disjunction", "unit" », "conjunction"). - auto type = TRY(get_option(global_object, *options, vm.names.type, Value::Type::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv)); + auto type = TRY(get_option(global_object, *options, vm.names.type, OptionType::String, { "conjunction"sv, "disjunction"sv, "unit"sv }, "conjunction"sv)); // 12. Set listFormat.[[Type]] to type. list_format->set_type(type.as_string().string()); // 13. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long"). - auto style = TRY(get_option(global_object, *options, vm.names.style, Value::Type::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); + auto style = TRY(get_option(global_object, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); // 14. Set listFormat.[[Style]] to style. list_format->set_style(style.as_string().string()); diff --git a/Userland/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp index 2b9e2647b6..d959a9c910 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/LocaleConstructor.cpp @@ -30,7 +30,7 @@ static ThrowCompletionOr> get_string_option(GlobalObject& globa { auto& vm = global_object.vm(); - auto option = TRY(get_option(global_object, options, property, Value::Type::String, values, Empty {})); + auto option = TRY(get_option(global_object, options, property, OptionType::String, values, Empty {})); if (option.is_undefined()) return Optional {}; @@ -312,7 +312,7 @@ ThrowCompletionOr LocaleConstructor::construct(FunctionObject& new_targ opt.kf = TRY(get_string_option(global_object, *options, vm.names.caseFirst, nullptr, AK::Array { "upper"sv, "lower"sv, "false"sv })); // 23. Let kn be ? GetOption(options, "numeric", "boolean", undefined, undefined). - auto kn = TRY(get_option(global_object, *options, vm.names.numeric, Value::Type::Boolean, {}, Empty {})); + auto kn = TRY(get_option(global_object, *options, vm.names.numeric, OptionType::Boolean, {}, Empty {})); // 24. If kn is not undefined, set kn to ! ToString(kn). // 25. Set opt.[[kn]] to kn. diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp index 88f6118020..58a9a3cfa2 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp @@ -94,13 +94,13 @@ ThrowCompletionOr initialize_number_format(GlobalObject& global_o LocaleOptions opt {}; // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); // 5. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; // 6. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). - auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, Value::Type::String, {}, Empty {})); + auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {})); // 7. If numberingSystem is not undefined, then if (!numbering_system.is_undefined()) { @@ -162,7 +162,7 @@ ThrowCompletionOr initialize_number_format(GlobalObject& global_o } // 18. Let notation be ? GetOption(options, "notation", "string", « "standard", "scientific", "engineering", "compact" », "standard"). - auto notation = TRY(get_option(global_object, *options, vm.names.notation, Value::Type::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv)); + auto notation = TRY(get_option(global_object, *options, vm.names.notation, OptionType::String, { "standard"sv, "scientific"sv, "engineering"sv, "compact"sv }, "standard"sv)); // 19. Set numberFormat.[[Notation]] to notation. number_format.set_notation(notation.as_string().string()); @@ -171,7 +171,7 @@ ThrowCompletionOr initialize_number_format(GlobalObject& global_o TRY(set_number_format_digit_options(global_object, number_format, *options, default_min_fraction_digits, default_max_fraction_digits, number_format.notation())); // 21. Let compactDisplay be ? GetOption(options, "compactDisplay", "string", « "short", "long" », "short"). - auto compact_display = TRY(get_option(global_object, *options, vm.names.compactDisplay, Value::Type::String, { "short"sv, "long"sv }, "short"sv)); + auto compact_display = TRY(get_option(global_object, *options, vm.names.compactDisplay, OptionType::String, { "short"sv, "long"sv }, "short"sv)); // 22. If notation is "compact", then if (number_format.notation() == NumberFormat::Notation::Compact) { @@ -180,13 +180,13 @@ ThrowCompletionOr initialize_number_format(GlobalObject& global_o } // 23. Let useGrouping be ? GetOption(options, "useGrouping", "boolean", undefined, true). - auto use_grouping = TRY(get_option(global_object, *options, vm.names.useGrouping, Value::Type::Boolean, {}, true)); + auto use_grouping = TRY(get_option(global_object, *options, vm.names.useGrouping, OptionType::Boolean, {}, true)); // 24. Set numberFormat.[[UseGrouping]] to useGrouping. number_format.set_use_grouping(use_grouping.as_bool()); // 25. Let signDisplay be ? GetOption(options, "signDisplay", "string", « "auto", "never", "always", "exceptZero" », "auto"). - auto sign_display = TRY(get_option(global_object, *options, vm.names.signDisplay, Value::Type::String, { "auto"sv, "never"sv, "always"sv, "exceptZero"sv }, "auto"sv)); + auto sign_display = TRY(get_option(global_object, *options, vm.names.signDisplay, OptionType::String, { "auto"sv, "never"sv, "always"sv, "exceptZero"sv }, "auto"sv)); // 26. Set numberFormat.[[SignDisplay]] to signDisplay. number_format.set_sign_display(sign_display.as_string().string()); @@ -321,13 +321,13 @@ ThrowCompletionOr set_number_format_unit_options(GlobalObject& global_obje // 2. Assert: Type(options) is Object. // 3. Let style be ? GetOption(options, "style", "string", « "decimal", "percent", "currency", "unit" », "decimal"). - auto style = TRY(get_option(global_object, options, vm.names.style, Value::Type::String, { "decimal"sv, "percent"sv, "currency"sv, "unit"sv }, "decimal"sv)); + auto style = TRY(get_option(global_object, options, vm.names.style, OptionType::String, { "decimal"sv, "percent"sv, "currency"sv, "unit"sv }, "decimal"sv)); // 4. Set intlObj.[[Style]] to style. intl_object.set_style(style.as_string().string()); // 5. Let currency be ? GetOption(options, "currency", "string", undefined, undefined). - auto currency = TRY(get_option(global_object, options, vm.names.currency, Value::Type::String, {}, Empty {})); + auto currency = TRY(get_option(global_object, options, vm.names.currency, OptionType::String, {}, Empty {})); // 6. If currency is undefined, then if (currency.is_undefined()) { @@ -341,13 +341,13 @@ ThrowCompletionOr set_number_format_unit_options(GlobalObject& global_obje return vm.throw_completion(global_object, ErrorType::OptionIsNotValidValue, currency, "currency"sv); // 8. Let currencyDisplay be ? GetOption(options, "currencyDisplay", "string", « "code", "symbol", "narrowSymbol", "name" », "symbol"). - auto currency_display = TRY(get_option(global_object, options, vm.names.currencyDisplay, Value::Type::String, { "code"sv, "symbol"sv, "narrowSymbol"sv, "name"sv }, "symbol"sv)); + auto currency_display = TRY(get_option(global_object, options, vm.names.currencyDisplay, OptionType::String, { "code"sv, "symbol"sv, "narrowSymbol"sv, "name"sv }, "symbol"sv)); // 9. Let currencySign be ? GetOption(options, "currencySign", "string", « "standard", "accounting" », "standard"). - auto currency_sign = TRY(get_option(global_object, options, vm.names.currencySign, Value::Type::String, { "standard"sv, "accounting"sv }, "standard"sv)); + auto currency_sign = TRY(get_option(global_object, options, vm.names.currencySign, OptionType::String, { "standard"sv, "accounting"sv }, "standard"sv)); // 10. Let unit be ? GetOption(options, "unit", "string", undefined, undefined). - auto unit = TRY(get_option(global_object, options, vm.names.unit, Value::Type::String, {}, Empty {})); + auto unit = TRY(get_option(global_object, options, vm.names.unit, OptionType::String, {}, Empty {})); // 11. If unit is undefined, then if (unit.is_undefined()) { @@ -361,7 +361,7 @@ ThrowCompletionOr set_number_format_unit_options(GlobalObject& global_obje return vm.throw_completion(global_object, ErrorType::OptionIsNotValidValue, unit, "unit"sv); // 13. Let unitDisplay be ? GetOption(options, "unitDisplay", "string", « "short", "narrow", "long" », "short"). - auto unit_display = TRY(get_option(global_object, options, vm.names.unitDisplay, Value::Type::String, { "short"sv, "narrow"sv, "long"sv }, "short"sv)); + auto unit_display = TRY(get_option(global_object, options, vm.names.unitDisplay, OptionType::String, { "short"sv, "narrow"sv, "long"sv }, "short"sv)); // 14. If style is "currency", then if (intl_object.style() == NumberFormat::Style::Currency) { diff --git a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp index 06ebe0712d..8d732ecf89 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesConstructor.cpp @@ -88,13 +88,13 @@ ThrowCompletionOr initialize_plural_rules(GlobalObject& global_obj LocaleOptions opt {}; // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); // 5. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; // 6. Let t be ? GetOption(options, "type", "string", « "cardinal", "ordinal" », "cardinal"). - auto type = TRY(get_option(global_object, *options, vm.names.type, Value::Type::String, AK::Array { "cardinal"sv, "ordinal"sv }, "cardinal"sv)); + auto type = TRY(get_option(global_object, *options, vm.names.type, OptionType::String, AK::Array { "cardinal"sv, "ordinal"sv }, "cardinal"sv)); // 7. Set pluralRules.[[Type]] to t. plural_rules.set_type(type.as_string().string()); diff --git a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp index 48b6421550..e1ae18c2f8 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp @@ -89,13 +89,13 @@ ThrowCompletionOr initialize_relative_time_format(GlobalObj LocaleOptions opt {}; // 4. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, AK::Array { "lookup"sv, "best fit"sv }, "best fit"sv)); // 5. Set opt.[[LocaleMatcher]] to matcher. opt.locale_matcher = matcher; // 6. Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined). - auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, Value::Type::String, {}, Empty {})); + auto numbering_system = TRY(get_option(global_object, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {})); // 7. If numberingSystem is not undefined, then if (!numbering_system.is_undefined()) { @@ -125,13 +125,13 @@ ThrowCompletionOr initialize_relative_time_format(GlobalObj relative_time_format.set_numbering_system(result.nu.release_value()); // 15. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long"). - auto style = TRY(get_option(global_object, *options, vm.names.style, Value::Type::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); + auto style = TRY(get_option(global_object, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv)); // 16. Set relativeTimeFormat.[[Style]] to style. relative_time_format.set_style(style.as_string().string()); // 17. Let numeric be ? GetOption(options, "numeric", "string", « "always", "auto" », "always"). - auto numeric = TRY(get_option(global_object, *options, vm.names.numeric, Value::Type::String, { "always"sv, "auto"sv }, "always"sv)); + auto numeric = TRY(get_option(global_object, *options, vm.names.numeric, OptionType::String, { "always"sv, "auto"sv }, "always"sv)); // 18. Set relativeTimeFormat.[[Numeric]] to numeric. relative_time_format.set_numeric(numeric.as_string().string()); diff --git a/Userland/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp index 4b16af0711..1bbaac0f8d 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/SegmenterConstructor.cpp @@ -64,7 +64,7 @@ ThrowCompletionOr SegmenterConstructor::construct(FunctionObject& new_t LocaleOptions opt {}; // 7. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit"). - auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); + auto matcher = TRY(get_option(global_object, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); // 8. Set opt.[[localeMatcher]] to matcher. opt.locale_matcher = matcher; @@ -78,7 +78,7 @@ ThrowCompletionOr SegmenterConstructor::construct(FunctionObject& new_t segmenter->set_locale(move(result.locale)); // 12. Let granularity be ? GetOption(options, "granularity", "string", « "grapheme", "word", "sentence" », "grapheme"). - auto granularity = TRY(get_option(global_object, *options, vm.names.granularity, Value::Type::String, { "grapheme"sv, "word"sv, "sentence"sv }, "grapheme"sv)); + auto granularity = TRY(get_option(global_object, *options, vm.names.granularity, OptionType::String, { "grapheme"sv, "word"sv, "sentence"sv }, "grapheme"sv)); // 13. Set segmenter.[[SegmenterGranularity]] to granularity. segmenter->set_segmenter_granularity(granularity.as_string().string()); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index e0533e8239..17650de2b8 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -100,22 +100,29 @@ ThrowCompletionOr get_options_object(GlobalObject& global_object, Value } // 13.3 GetOption ( options, property, type, values, fallback ), https://tc39.es/proposal-temporal/#sec-getoption -ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, OptionType type, Vector const& values, Value fallback) +ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, OptionType type, Span values, OptionDefault const& default_) { VERIFY(property.is_string()); auto& vm = global_object.vm(); - // 1. Assert: Type(options) is Object. - - // 2. Let value be ? Get(options, property). + // 1. Let value be ? Get(options, property). auto value = TRY(options.get(property)); - // 3. If value is undefined, return fallback. - if (value.is_undefined()) - return fallback; + // 2. If value is undefined, then + if (value.is_undefined()) { + // a. If default is required, throw a RangeError exception. + if (default_.has()) + return vm.throw_completion(global_object, ErrorType::OptionIsNotValidValue, "undefined"sv, property.as_string()); - // 4. Assert: type is "boolean" or "string" or "number". + // b. Return default. + return default_.visit( + [](GetOptionRequired) -> Value { VERIFY_NOT_REACHED(); }, + [](Empty) { return js_undefined(); }, + [](bool b) { return Value(b); }, + [](double d) { return Value(d); }, + [&vm](StringView s) { return Value(js_string(vm, s)); }); + } // 5. If type is "boolean", then if (type == OptionType::Boolean) { @@ -162,7 +169,7 @@ ThrowCompletionOr to_temporal_overflow(GlobalObject& global_object, Obje return "constrain"sv; // 2. Return ? GetOption(options, "overflow", "string", « "constrain", "reject" », "constrain"). - auto option = TRY(get_option(global_object, *options, vm.names.overflow, OptionType::String, { "constrain"sv, "reject"sv }, js_string(vm, "constrain"))); + auto option = TRY(get_option(global_object, *options, vm.names.overflow, OptionType::String, { "constrain"sv, "reject"sv }, "constrain"sv)); VERIFY(option.is_string()); return option.as_string().string(); @@ -178,7 +185,7 @@ ThrowCompletionOr to_temporal_disambiguation(GlobalObject& global_object return "compatible"sv; // 2. Return ? GetOption(options, "disambiguation", "string", « "compatible", "earlier", "later", "reject" », "compatible"). - auto option = TRY(get_option(global_object, *options, vm.names.disambiguation, OptionType::String, { "compatible"sv, "earlier"sv, "later"sv, "reject"sv }, js_string(vm, "compatible"))); + auto option = TRY(get_option(global_object, *options, vm.names.disambiguation, OptionType::String, { "compatible"sv, "earlier"sv, "later"sv, "reject"sv }, "compatible"sv)); VERIFY(option.is_string()); return option.as_string().string(); @@ -190,7 +197,7 @@ ThrowCompletionOr to_temporal_rounding_mode(GlobalObject& global_object, auto& vm = global_object.vm(); // 1. Return ? GetOption(normalizedOptions, "roundingMode", "string", « "ceil", "floor", "trunc", "halfExpand" », fallback). - auto option = TRY(get_option(global_object, normalized_options, vm.names.roundingMode, OptionType::String, { "ceil"sv, "floor"sv, "trunc"sv, "halfExpand"sv }, js_string(vm, fallback))); + auto option = TRY(get_option(global_object, normalized_options, vm.names.roundingMode, OptionType::String, { "ceil"sv, "floor"sv, "trunc"sv, "halfExpand"sv }, fallback.view())); VERIFY(option.is_string()); return option.as_string().string(); @@ -221,7 +228,7 @@ ThrowCompletionOr to_temporal_offset(GlobalObject& global_object, Object return fallback; // 2. Return ? GetOption(options, "offset", "string", « "prefer", "use", "ignore", "reject" », fallback). - auto option = TRY(get_option(global_object, *options, vm.names.offset, OptionType::String, { "prefer"sv, "use"sv, "ignore"sv, "reject"sv }, js_string(vm, fallback))); + auto option = TRY(get_option(global_object, *options, vm.names.offset, OptionType::String, { "prefer"sv, "use"sv, "ignore"sv, "reject"sv }, fallback.view())); VERIFY(option.is_string()); return option.as_string().string(); @@ -233,7 +240,7 @@ ThrowCompletionOr to_show_calendar_option(GlobalObject& global_object, O auto& vm = global_object.vm(); // 1. Return ? GetOption(normalizedOptions, "calendarName", "string", « "auto", "always", "never" », "auto"). - auto option = TRY(get_option(global_object, normalized_options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv }, js_string(vm, "auto"sv))); + auto option = TRY(get_option(global_object, normalized_options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv }, "auto"sv)); VERIFY(option.is_string()); return option.as_string().string(); @@ -245,7 +252,7 @@ ThrowCompletionOr to_show_time_zone_name_option(GlobalObject& global_obj auto& vm = global_object.vm(); // 1. Return ? GetOption(normalizedOptions, "timeZoneName", "string, « "auto", "never" », "auto"). - auto option = TRY(get_option(global_object, normalized_options, vm.names.timeZoneName, OptionType::String, { "auto"sv, "never"sv }, js_string(vm, "auto"sv))); + auto option = TRY(get_option(global_object, normalized_options, vm.names.timeZoneName, OptionType::String, { "auto"sv, "never"sv }, "auto"sv)); VERIFY(option.is_string()); return option.as_string().string(); @@ -257,7 +264,7 @@ ThrowCompletionOr to_show_offset_option(GlobalObject& global_object, Obj auto& vm = global_object.vm(); // 1. Return ? GetOption(normalizedOptions, "offset", "string", « "auto", "never" », "auto"). - auto option = TRY(get_option(global_object, normalized_options, vm.names.offset, OptionType::String, { "auto"sv, "never"sv }, js_string(vm, "auto"sv))); + auto option = TRY(get_option(global_object, normalized_options, vm.names.offset, OptionType::String, { "auto"sv, "never"sv }, "auto"sv)); VERIFY(option.is_string()); return option.as_string().string(); @@ -291,7 +298,7 @@ ThrowCompletionOr to_temporal_rounding_increment(GlobalObject& global_objec } // 5. Let increment be ? GetOption(normalizedOptions, "roundingIncrement", "number", undefined, 1𝔽). - auto increment_value = TRY(get_option(global_object, normalized_options, vm.names.roundingIncrement, OptionType::Number, {}, Value(1))); + auto increment_value = TRY(get_option(global_object, normalized_options, vm.names.roundingIncrement, OptionType::Number, {}, 1.0)); VERIFY(increment_value.is_number()); auto increment = increment_value.as_double(); @@ -488,19 +495,19 @@ ThrowCompletionOr> get_temporal_unit(GlobalObject& global_objec singular_names.extend(extra_values); } - Value default_value; + OptionDefault default_value; // 4. If default is required, then if (default_.has()) { // a. Let defaultValue be undefined. - default_value = js_undefined(); + default_value = {}; } // 5. Else, else { auto default_string = default_.get>(); // a. Let defaultValue be default. - default_value = default_string.has_value() ? js_string(vm, *default_string) : js_undefined(); + default_value = default_string.has_value() ? OptionDefault { *default_string } : OptionDefault {}; // b. If defaultValue is not undefined and singularNames does not contain defaultValue, then if (default_string.has_value() && !singular_names.contains_slow(*default_string)) { @@ -529,7 +536,7 @@ ThrowCompletionOr> get_temporal_unit(GlobalObject& global_objec // 8. NOTE: For each singular Temporal unit name that is contained within allowedValues, the corresponding plural name is also contained within it. // 9. Let value be ? GetOption(normalizedOptions, key, "string", allowedValues, defaultValue). - auto option_value = TRY(get_option(global_object, normalized_options, key, OptionType::String, allowed_values, default_value)); + auto option_value = TRY(get_option(global_object, normalized_options, key, OptionType::String, allowed_values.span(), default_value)); // 10. If value is undefined and default is required, throw a RangeError exception. if (option_value.is_undefined() && default_.has()) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index 2fa603929a..a2bd765012 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -123,10 +123,13 @@ struct SecondsStringPrecision { struct TemporalUnitRequired { }; struct PrepareTemporalFieldsPartial { }; +struct GetOptionRequired { }; + +using OptionDefault = Variant; ThrowCompletionOr> iterable_to_list_of_type(GlobalObject&, Value items, Vector const& element_types); ThrowCompletionOr get_options_object(GlobalObject&, Value options); -ThrowCompletionOr get_option(GlobalObject&, Object const& options, PropertyKey const& property, OptionType type, Vector const& values, Value fallback); +ThrowCompletionOr get_option(GlobalObject&, Object const& options, PropertyKey const& property, OptionType type, Span values, OptionDefault const&); ThrowCompletionOr to_temporal_overflow(GlobalObject&, Object const* options); ThrowCompletionOr to_temporal_disambiguation(GlobalObject&, Object const* options); ThrowCompletionOr to_temporal_rounding_mode(GlobalObject&, Object const& normalized_options, String const& fallback); @@ -167,6 +170,12 @@ ThrowCompletionOr parse_temporal_year_month_string(GlobalObje ThrowCompletionOr to_positive_integer(GlobalObject&, Value argument); ThrowCompletionOr prepare_temporal_fields(GlobalObject&, Object const& fields, Vector const& field_names, Variant> const& required_fields); +template +ThrowCompletionOr get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_) +{ + return get_option(global_object, options, property, type, Span { values }, default_); +} + // 13.40 ToIntegerThrowOnInfinity ( argument ), https://tc39.es/proposal-temporal/#sec-temporal-tointegerthrowoninfinity template ThrowCompletionOr to_integer_throw_on_infinity(GlobalObject& global_object, Value argument, ErrorType error_type, Args... args)