From d7825f368007778d7debcb6fcb79292e190679c1 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 2 Sep 2021 10:49:06 -0400 Subject: [PATCH] LibJS: Implement most Intl.Locale.Prototype.<> properties The keyword accessors all have the same function body in the spec, except for the Intl.Locale method they invoke. This generates those properties in the same manner as RegExp.prototype. Intl.Locale.prototype.calendar Intl.Locale.prototype.caseFirst Intl.Locale.prototype.collation Intl.Locale.prototype.hourCycle Intl.Locale.prototype.numberingSystem The exception is Intl.Locale.prototype.numeric, which will be defined separately because it is a boolean value. --- .../LibJS/Runtime/Intl/LocalePrototype.cpp | 30 +++++++++++++++++++ .../LibJS/Runtime/Intl/LocalePrototype.h | 5 ++++ .../Intl/Locale/Locale.prototype.calendar.js | 16 ++++++++++ .../Intl/Locale/Locale.prototype.caseFirst.js | 16 ++++++++++ .../Intl/Locale/Locale.prototype.collation.js | 16 ++++++++++ .../Intl/Locale/Locale.prototype.hourCycle.js | 16 ++++++++++ .../Locale.prototype.numberingSystem.js | 18 +++++++++++ 7 files changed, 117 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.calendar.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.caseFirst.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.collation.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.hourCycle.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.numberingSystem.js diff --git a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp index e0808a8e4f..a8e11e443f 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp @@ -47,6 +47,11 @@ void LocalePrototype::initialize(GlobalObject& global_object) define_direct_property(*vm.well_known_symbol_to_string_tag(), js_string(vm, "Intl.Locale"), Attribute::Configurable); define_native_accessor(vm.names.baseName, base_name, {}, Attribute::Configurable); + define_native_accessor(vm.names.calendar, calendar, {}, Attribute::Configurable); + define_native_accessor(vm.names.caseFirst, case_first, {}, Attribute::Configurable); + define_native_accessor(vm.names.collation, collation, {}, Attribute::Configurable); + define_native_accessor(vm.names.hourCycle, hour_cycle, {}, Attribute::Configurable); + define_native_accessor(vm.names.numberingSystem, numbering_system, {}, Attribute::Configurable); } // 14.3.5 Intl.Locale.prototype.toString ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.toString @@ -79,4 +84,29 @@ JS_DEFINE_NATIVE_GETTER(LocalePrototype::base_name) return js_string(vm, locale->language_id.to_string()); } +#define JS_ENUMERATE_LOCALE_KEYWORD_PROPERTIES \ + __JS_ENUMERATE(calendar) \ + __JS_ENUMERATE(case_first) \ + __JS_ENUMERATE(collation) \ + __JS_ENUMERATE(hour_cycle) \ + __JS_ENUMERATE(numbering_system) + +// 14.3.7 get Intl.Locale.prototype.calendar, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.calendar +// 14.3.8 get Intl.Locale.prototype.caseFirst, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.caseFirst +// 14.3.9 get Intl.Locale.prototype.collation, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.collation +// 14.3.10 get Intl.Locale.prototype.hourCycle, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle +// 14.3.12 get Intl.Locale.prototype.numberingSystem, https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numberingSystem +#define __JS_ENUMERATE(keyword) \ + JS_DEFINE_NATIVE_GETTER(LocalePrototype::keyword) \ + { \ + auto* locale_object = typed_this(global_object); \ + if (!locale_object) \ + return {}; \ + if (!locale_object->has_##keyword()) \ + return js_undefined(); \ + return js_string(vm, locale_object->keyword()); \ + } +JS_ENUMERATE_LOCALE_KEYWORD_PROPERTIES +#undef __JS_ENUMERATE + } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h index b6b6b76f6e..02880cb91d 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h @@ -22,6 +22,11 @@ private: JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_GETTER(base_name); + JS_DECLARE_NATIVE_GETTER(calendar); + JS_DECLARE_NATIVE_GETTER(case_first); + JS_DECLARE_NATIVE_GETTER(collation); + JS_DECLARE_NATIVE_GETTER(hour_cycle); + JS_DECLARE_NATIVE_GETTER(numbering_system); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.calendar.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.calendar.js new file mode 100644 index 0000000000..de372899ad --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.calendar.js @@ -0,0 +1,16 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.calendar; + }).toThrowWithMessage(TypeError, "Not a Intl.Locale object"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + expect(new Intl.Locale("en").calendar).toBeUndefined(); + expect(new Intl.Locale("en-u-ca-abc").calendar).toBe("abc"); + expect(new Intl.Locale("en", { calendar: "abc" }).calendar).toBe("abc"); + expect(new Intl.Locale("en-u-ca-abc", { calendar: "def" }).calendar).toBe("def"); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.caseFirst.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.caseFirst.js new file mode 100644 index 0000000000..852f4ea88c --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.caseFirst.js @@ -0,0 +1,16 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.caseFirst; + }).toThrowWithMessage(TypeError, "Not a Intl.Locale object"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + expect(new Intl.Locale("en").caseFirst).toBeUndefined(); + expect(new Intl.Locale("en-u-kf-upper").caseFirst).toBe("upper"); + expect(new Intl.Locale("en", { caseFirst: "lower" }).caseFirst).toBe("lower"); + expect(new Intl.Locale("en-u-kf-upper", { caseFirst: "false" }).caseFirst).toBe("false"); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.collation.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.collation.js new file mode 100644 index 0000000000..363a679ae5 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.collation.js @@ -0,0 +1,16 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.collation; + }).toThrowWithMessage(TypeError, "Not a Intl.Locale object"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + expect(new Intl.Locale("en").collation).toBeUndefined(); + expect(new Intl.Locale("en-u-co-abc").collation).toBe("abc"); + expect(new Intl.Locale("en", { collation: "abc" }).collation).toBe("abc"); + expect(new Intl.Locale("en-u-co-abc", { collation: "def" }).collation).toBe("def"); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.hourCycle.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.hourCycle.js new file mode 100644 index 0000000000..53c8e3c44f --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.hourCycle.js @@ -0,0 +1,16 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.hourCycle; + }).toThrowWithMessage(TypeError, "Not a Intl.Locale object"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + expect(new Intl.Locale("en").hourCycle).toBeUndefined(); + expect(new Intl.Locale("en-u-hc-h11").hourCycle).toBe("h11"); + expect(new Intl.Locale("en", { hourCycle: "h12" }).hourCycle).toBe("h12"); + expect(new Intl.Locale("en-u-hc-h23", { hourCycle: "h24" }).hourCycle).toBe("h24"); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.numberingSystem.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.numberingSystem.js new file mode 100644 index 0000000000..ff1563cbbc --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.numberingSystem.js @@ -0,0 +1,18 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.numberingSystem; + }).toThrowWithMessage(TypeError, "Not a Intl.Locale object"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + expect(new Intl.Locale("en").numberingSystem).toBeUndefined(); + expect(new Intl.Locale("en-u-nu-abc").numberingSystem).toBe("abc"); + expect(new Intl.Locale("en", { numberingSystem: "abc" }).numberingSystem).toBe("abc"); + expect(new Intl.Locale("en-u-nu-abc", { numberingSystem: "def" }).numberingSystem).toBe( + "def" + ); + }); +});