From 74939eb9434284437386404da4292d42ba1e7a1b Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 28 Jan 2022 13:42:20 -0500 Subject: [PATCH] LibJS: Implement Intl.PluralRules.prototype.resolvedOptions --- .../LibJS/Runtime/CommonPropertyNames.h | 1 + .../Runtime/Intl/PluralRulesPrototype.cpp | 43 +++++++++ .../LibJS/Runtime/Intl/PluralRulesPrototype.h | 3 + .../PluralRules.prototype.resolvedOptions.js | 87 +++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/PluralRules/PluralRules.prototype.resolvedOptions.js diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 4ecb28b846..dff47546d7 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -357,6 +357,7 @@ namespace JS { P(plainDateTimeISO) \ P(plainTime) \ P(plainTimeISO) \ + P(pluralCategories) \ P(pop) \ P(pow) \ P(preventExtensions) \ diff --git a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.cpp index e294dff19f..fcca6f56e3 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.cpp @@ -4,8 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include namespace JS::Intl { @@ -23,6 +25,47 @@ void PluralRulesPrototype::initialize(GlobalObject& global_object) // 16.4.2 Intl.PluralRules.prototype [ @@toStringTag ], https://tc39.es/ecma402/#sec-intl.pluralrules.prototype-tostringtag define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.PluralRules"sv), Attribute::Configurable); + + u8 attr = Attribute::Writable | Attribute::Configurable; + define_native_function(vm.names.resolvedOptions, resolved_options, 0, attr); +} + +// 16.4.4 Intl.PluralRules.prototype.resolvedOptions ( ), https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions +JS_DEFINE_NATIVE_FUNCTION(PluralRulesPrototype::resolved_options) +{ + // 1. Let pr be the this value. + // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]). + auto* plural_rules = TRY(typed_this_object(global_object)); + + // 3. Let options be ! OrdinaryObjectCreate(%Object.prototype%). + auto* options = Object::create(global_object, global_object.object_prototype()); + + // 4. For each row of Table 14, except the header row, in table order, do + // a. Let p be the Property value of the current row. + // b. Let v be the value of pr's internal slot whose name is the Internal Slot value of the current row. + // c. If v is not undefined, then + // i. Perform ! CreateDataPropertyOrThrow(options, p, v). + MUST(options->create_data_property_or_throw(vm.names.locale, js_string(vm, plural_rules->locale()))); + MUST(options->create_data_property_or_throw(vm.names.type, js_string(vm, plural_rules->type_string()))); + MUST(options->create_data_property_or_throw(vm.names.minimumIntegerDigits, Value(plural_rules->min_integer_digits()))); + if (plural_rules->has_min_fraction_digits()) + MUST(options->create_data_property_or_throw(vm.names.minimumFractionDigits, Value(plural_rules->min_fraction_digits()))); + if (plural_rules->has_max_fraction_digits()) + MUST(options->create_data_property_or_throw(vm.names.maximumFractionDigits, Value(plural_rules->max_fraction_digits()))); + if (plural_rules->has_min_significant_digits()) + MUST(options->create_data_property_or_throw(vm.names.minimumSignificantDigits, Value(plural_rules->min_significant_digits()))); + if (plural_rules->has_max_significant_digits()) + MUST(options->create_data_property_or_throw(vm.names.maximumSignificantDigits, Value(plural_rules->max_significant_digits()))); + + // 5. Let pluralCategories be a List of Strings containing all possible results of PluralRuleSelect for the selected locale pr.[[Locale]]. + // FIXME: Implement this when the data is available in LibUnicode. + MarkedValueList plural_categories { vm.heap() }; + + // 6. Perform ! CreateDataProperty(options, "pluralCategories", CreateArrayFromList(pluralCategories)). + MUST(options->create_data_property_or_throw(vm.names.pluralCategories, Array::create_from(global_object, plural_categories))); + + // 7. Return options. + return options; } } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.h b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.h index e4c10caa55..03f9183430 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/PluralRulesPrototype.h @@ -18,6 +18,9 @@ public: explicit PluralRulesPrototype(GlobalObject&); virtual void initialize(GlobalObject&) override; virtual ~PluralRulesPrototype() override = default; + +private: + JS_DECLARE_NATIVE_FUNCTION(resolved_options); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/PluralRules/PluralRules.prototype.resolvedOptions.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/PluralRules/PluralRules.prototype.resolvedOptions.js new file mode 100644 index 0000000000..29e7cd88ba --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/PluralRules/PluralRules.prototype.resolvedOptions.js @@ -0,0 +1,87 @@ +describe("correct behavior", () => { + test("length is 0", () => { + expect(Intl.PluralRules.prototype.resolvedOptions).toHaveLength(0); + }); + + test("locale only contains relevant extension keys", () => { + const en1 = new Intl.PluralRules("en-u-ca-islamicc"); + expect(en1.resolvedOptions().locale).toBe("en"); + + const en2 = new Intl.PluralRules("en-u-nu-latn"); + expect(en2.resolvedOptions().locale).toBe("en"); + + const en3 = new Intl.PluralRules("en-u-ca-islamicc-nu-latn"); + expect(en3.resolvedOptions().locale).toBe("en"); + }); + + test("type", () => { + const en1 = new Intl.PluralRules("en"); + expect(en1.resolvedOptions().type).toBe("cardinal"); + + ["cardinal", "ordinal"].forEach(type => { + const en2 = new Intl.PluralRules("en", { type: type }); + expect(en2.resolvedOptions().type).toBe(type); + }); + }); + + test("min integer digits", () => { + const en1 = new Intl.PluralRules("en"); + expect(en1.resolvedOptions().minimumIntegerDigits).toBe(1); + + const en2 = new Intl.PluralRules("en", { minimumIntegerDigits: 5 }); + expect(en2.resolvedOptions().minimumIntegerDigits).toBe(5); + }); + + test("min/max fraction digits", () => { + const en1 = new Intl.PluralRules("en", { minimumFractionDigits: 5 }); + expect(en1.resolvedOptions().minimumFractionDigits).toBe(5); + expect(en1.resolvedOptions().maximumFractionDigits).toBe(5); + expect(en1.resolvedOptions().minimumSignificantDigits).toBeUndefined(); + expect(en1.resolvedOptions().maximumSignificantDigits).toBeUndefined(); + + const en2 = new Intl.PluralRules("en", { maximumFractionDigits: 5 }); + expect(en2.resolvedOptions().minimumFractionDigits).toBe(0); + expect(en2.resolvedOptions().maximumFractionDigits).toBe(5); + expect(en2.resolvedOptions().minimumSignificantDigits).toBeUndefined(); + expect(en2.resolvedOptions().maximumSignificantDigits).toBeUndefined(); + + const en3 = new Intl.PluralRules("en", { + minimumFractionDigits: 5, + maximumFractionDigits: 10, + }); + expect(en3.resolvedOptions().minimumFractionDigits).toBe(5); + expect(en3.resolvedOptions().maximumFractionDigits).toBe(10); + expect(en3.resolvedOptions().minimumSignificantDigits).toBeUndefined(); + expect(en3.resolvedOptions().maximumSignificantDigits).toBeUndefined(); + }); + + test("min/max significant digits", () => { + const en1 = new Intl.PluralRules("en", { minimumSignificantDigits: 5 }); + expect(en1.resolvedOptions().minimumFractionDigits).toBeUndefined(); + expect(en1.resolvedOptions().maximumFractionDigits).toBeUndefined(); + expect(en1.resolvedOptions().minimumSignificantDigits).toBe(5); + expect(en1.resolvedOptions().maximumSignificantDigits).toBe(21); + + const en2 = new Intl.PluralRules("en", { maximumSignificantDigits: 5 }); + expect(en2.resolvedOptions().minimumFractionDigits).toBeUndefined(); + expect(en2.resolvedOptions().maximumFractionDigits).toBeUndefined(); + expect(en2.resolvedOptions().minimumSignificantDigits).toBe(1); + expect(en2.resolvedOptions().maximumSignificantDigits).toBe(5); + + const en3 = new Intl.PluralRules("en", { + minimumSignificantDigits: 5, + maximumSignificantDigits: 10, + }); + expect(en3.resolvedOptions().minimumFractionDigits).toBeUndefined(); + expect(en3.resolvedOptions().maximumFractionDigits).toBeUndefined(); + expect(en3.resolvedOptions().minimumSignificantDigits).toBe(5); + expect(en3.resolvedOptions().maximumSignificantDigits).toBe(10); + }); + + test("plural categories", () => { + // FIXME: Write better tests when this is implemented. + const en = new Intl.PluralRules("en"); + expect(en.resolvedOptions().pluralCategories).toBeDefined(); + expect(en.resolvedOptions().pluralCategories).toEqual([]); + }); +});