/src/serenity/Userland/Libraries/LibJS/Runtime/Intl/Intl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/QuickSort.h> |
8 | | #include <LibJS/Runtime/Array.h> |
9 | | #include <LibJS/Runtime/GlobalObject.h> |
10 | | #include <LibJS/Runtime/Intl/AbstractOperations.h> |
11 | | #include <LibJS/Runtime/Intl/CollatorConstructor.h> |
12 | | #include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h> |
13 | | #include <LibJS/Runtime/Intl/DisplayNamesConstructor.h> |
14 | | #include <LibJS/Runtime/Intl/DurationFormatConstructor.h> |
15 | | #include <LibJS/Runtime/Intl/Intl.h> |
16 | | #include <LibJS/Runtime/Intl/ListFormatConstructor.h> |
17 | | #include <LibJS/Runtime/Intl/LocaleConstructor.h> |
18 | | #include <LibJS/Runtime/Intl/NumberFormatConstructor.h> |
19 | | #include <LibJS/Runtime/Intl/PluralRulesConstructor.h> |
20 | | #include <LibJS/Runtime/Intl/RelativeTimeFormatConstructor.h> |
21 | | #include <LibJS/Runtime/Intl/SegmenterConstructor.h> |
22 | | #include <LibJS/Runtime/Temporal/TimeZone.h> |
23 | | #include <LibLocale/DateTimeFormat.h> |
24 | | #include <LibLocale/Locale.h> |
25 | | #include <LibLocale/NumberFormat.h> |
26 | | |
27 | | namespace JS::Intl { |
28 | | |
29 | | JS_DEFINE_ALLOCATOR(Intl); |
30 | | |
31 | | // 8 The Intl Object, https://tc39.es/ecma402/#intl-object |
32 | | Intl::Intl(Realm& realm) |
33 | 0 | : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype()) |
34 | 0 | { |
35 | 0 | } |
36 | | |
37 | | void Intl::initialize(Realm& realm) |
38 | 0 | { |
39 | 0 | Base::initialize(realm); |
40 | |
|
41 | 0 | auto& vm = this->vm(); |
42 | | |
43 | | // 8.1.1 Intl[ @@toStringTag ], https://tc39.es/ecma402/#sec-Intl-toStringTag |
44 | 0 | define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Intl"_string), Attribute::Configurable); |
45 | |
|
46 | 0 | u8 attr = Attribute::Writable | Attribute::Configurable; |
47 | 0 | define_intrinsic_accessor(vm.names.Collator, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_collator_constructor(); }); |
48 | 0 | define_intrinsic_accessor(vm.names.DateTimeFormat, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_date_time_format_constructor(); }); |
49 | 0 | define_intrinsic_accessor(vm.names.DisplayNames, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_display_names_constructor(); }); |
50 | 0 | define_intrinsic_accessor(vm.names.DurationFormat, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_duration_format_constructor(); }); |
51 | 0 | define_intrinsic_accessor(vm.names.ListFormat, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_list_format_constructor(); }); |
52 | 0 | define_intrinsic_accessor(vm.names.Locale, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_locale_constructor(); }); |
53 | 0 | define_intrinsic_accessor(vm.names.NumberFormat, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_number_format_constructor(); }); |
54 | 0 | define_intrinsic_accessor(vm.names.PluralRules, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_plural_rules_constructor(); }); |
55 | 0 | define_intrinsic_accessor(vm.names.RelativeTimeFormat, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_relative_time_format_constructor(); }); |
56 | 0 | define_intrinsic_accessor(vm.names.Segmenter, attr, [](auto& realm) -> Value { return realm.intrinsics().intl_segmenter_constructor(); }); |
57 | |
|
58 | 0 | define_native_function(realm, vm.names.getCanonicalLocales, get_canonical_locales, 1, attr); |
59 | 0 | define_native_function(realm, vm.names.supportedValuesOf, supported_values_of, 1, attr); |
60 | 0 | } |
61 | | |
62 | | // 8.3.1 Intl.getCanonicalLocales ( locales ), https://tc39.es/ecma402/#sec-intl.getcanonicallocales |
63 | | JS_DEFINE_NATIVE_FUNCTION(Intl::get_canonical_locales) |
64 | 0 | { |
65 | 0 | auto& realm = *vm.current_realm(); |
66 | |
|
67 | 0 | auto locales = vm.argument(0); |
68 | | |
69 | | // 1. Let ll be ? CanonicalizeLocaleList(locales). |
70 | 0 | auto locale_list = TRY(canonicalize_locale_list(vm, locales)); |
71 | | |
72 | 0 | MarkedVector<Value> marked_locale_list { vm.heap() }; |
73 | 0 | marked_locale_list.ensure_capacity(locale_list.size()); |
74 | |
|
75 | 0 | for (auto& locale : locale_list) |
76 | 0 | marked_locale_list.unchecked_append(PrimitiveString::create(vm, move(locale))); |
77 | | |
78 | | // 2. Return CreateArrayFromList(ll). |
79 | 0 | return Array::create_from(realm, marked_locale_list); |
80 | 0 | } |
81 | | |
82 | | // 6.5.4 AvailableCanonicalTimeZones ( ), https://tc39.es/ecma402/#sec-availablecanonicaltimezones |
83 | | static Vector<StringView> available_canonical_time_zones() |
84 | 0 | { |
85 | | // 1. Let names be a List of all supported Zone and Link names in the IANA Time Zone Database. |
86 | 0 | auto names = TimeZone::all_time_zones(); |
87 | | |
88 | | // 2. Let result be a new empty List. |
89 | 0 | Vector<StringView> result; |
90 | | |
91 | | // 3. For each element name of names, do |
92 | 0 | for (auto const& name : names) { |
93 | | // a. Assert: IsValidTimeZoneName( name ) is true. |
94 | | // b. Let canonical be ! CanonicalizeTimeZoneName( name ). |
95 | 0 | auto canonical = TimeZone::canonicalize_time_zone(name.name).value(); |
96 | | |
97 | | // c. If result does not contain an element equal to canonical, then |
98 | 0 | if (!result.contains_slow(canonical)) { |
99 | | // i. Append canonical to the end of result. |
100 | 0 | result.append(canonical); |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | // 4. Sort result in order as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn. |
105 | 0 | quick_sort(result); |
106 | | |
107 | | // 5. Return result. |
108 | 0 | return result; |
109 | 0 | } |
110 | | |
111 | | // 8.3.2 Intl.supportedValuesOf ( key ), https://tc39.es/ecma402/#sec-intl.supportedvaluesof |
112 | | JS_DEFINE_NATIVE_FUNCTION(Intl::supported_values_of) |
113 | 0 | { |
114 | 0 | auto& realm = *vm.current_realm(); |
115 | | |
116 | | // 1. Let key be ? ToString(key). |
117 | 0 | auto key = TRY(vm.argument(0).to_string(vm)); |
118 | | |
119 | 0 | ReadonlySpan<StringView> list; |
120 | | |
121 | | // 2. If key is "calendar", then |
122 | 0 | if (key == "calendar"sv) { |
123 | | // a. Let list be ! AvailableCanonicalCalendars( ). |
124 | 0 | list = ::Locale::get_available_calendars(); |
125 | 0 | } |
126 | | // 3. Else if key is "collation", then |
127 | 0 | else if (key == "collation"sv) { |
128 | | // a. Let list be ! AvailableCanonicalCollations( ). |
129 | 0 | list = ::Locale::get_available_collation_types(); |
130 | 0 | } |
131 | | // 4. Else if key is "currency", then |
132 | 0 | else if (key == "currency"sv) { |
133 | | // a. Let list be ! AvailableCanonicalCurrencies( ). |
134 | 0 | list = ::Locale::get_available_currencies(); |
135 | 0 | } |
136 | | // 5. Else if key is "numberingSystem", then |
137 | 0 | else if (key == "numberingSystem"sv) { |
138 | | // a. Let list be ! AvailableCanonicalNumberingSystems( ). |
139 | 0 | list = ::Locale::get_available_number_systems(); |
140 | 0 | } |
141 | | // 6. Else if key is "timeZone", then |
142 | 0 | else if (key == "timeZone"sv) { |
143 | | // a. Let list be ! AvailableCanonicalTimeZones( ). |
144 | 0 | static auto time_zones = available_canonical_time_zones(); |
145 | 0 | list = time_zones.span(); |
146 | 0 | } |
147 | | // 7. Else if key is "unit", then |
148 | 0 | else if (key == "unit"sv) { |
149 | | // a. Let list be ! AvailableCanonicalUnits( ). |
150 | 0 | static auto units = sanctioned_single_unit_identifiers(); |
151 | 0 | list = units.span(); |
152 | 0 | } |
153 | | // 8. Else, |
154 | 0 | else { |
155 | | // a. Throw a RangeError exception. |
156 | 0 | return vm.throw_completion<RangeError>(ErrorType::IntlInvalidKey, key); |
157 | 0 | } |
158 | | |
159 | | // 9. Return CreateArrayFromList( list ). |
160 | 0 | return Array::create_from<StringView>(realm, list, [&](auto value) { |
161 | 0 | return PrimitiveString::create(vm, value); |
162 | 0 | }); |
163 | 0 | } |
164 | | |
165 | | } |