summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2021-11-16 19:35:40 -0500
committerLinus Groh <mail@linusgroh.de>2021-11-17 09:01:32 +0000
commitc19c3205ffe0747fa299950a725a57ebd6667e95 (patch)
treec2f28a77357ecd7a43b604d5383e17e89d9b63bc /Userland/Libraries/LibJS
parent8fe1c1f78876c8ef189a12ac9174488ed9576384 (diff)
downloadserenity-c19c3205ffe0747fa299950a725a57ebd6667e95.zip
LibJS: Implement ECMA-402 Number.prototype.toLocaleString
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp27
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberPrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js78
3 files changed, 106 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp
index 1213b26e9b..07ddaf2f40 100644
--- a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp
@@ -7,9 +7,12 @@
#include <AK/Function.h>
#include <AK/TypeCasts.h>
+#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Intl/NumberFormat.h>
+#include <LibJS/Runtime/Intl/NumberFormatConstructor.h>
#include <LibJS/Runtime/NumberObject.h>
#include <LibJS/Runtime/NumberPrototype.h>
@@ -37,6 +40,7 @@ void NumberPrototype::initialize(GlobalObject& object)
Object::initialize(object);
u8 attr = Attribute::Configurable | Attribute::Writable;
define_native_function(vm.names.toFixed, to_fixed, 1, attr);
+ define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(vm.names.toString, to_string, 1, attr);
define_native_function(vm.names.valueOf, value_of, 0, attr);
}
@@ -77,6 +81,29 @@ JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_fixed)
return js_string(vm, String::formatted("{:0.{1}}", number, static_cast<size_t>(fraction_digits)));
}
+// 18.2.1 Number.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-number.prototype.tolocalestring
+JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_locale_string)
+{
+ auto locales = vm.argument(0);
+ auto options = vm.argument(1);
+
+ // 1. Let x be ? thisNumberValue(this value).
+ auto number_value = TRY(this_number_value(global_object, vm.this_value(global_object)));
+
+ MarkedValueList arguments { vm.heap() };
+ arguments.append(locales);
+ arguments.append(options);
+
+ // 2. Let numberFormat be ? Construct(%NumberFormat%, ยซ locales, options ยป).
+ auto* number_format = static_cast<Intl::NumberFormat*>(TRY(construct(global_object, *global_object.intl_number_format_constructor(), move(arguments))));
+
+ // 3. Return ? FormatNumeric(numberFormat, x).
+ // Note: Our implementation of FormatNumeric does not throw.
+ auto formatted = Intl::format_numeric(*number_format, number_value.as_double());
+
+ return js_string(vm, move(formatted));
+}
+
// 21.1.3.6 Number.prototype.toString ( [ radix ] ), https://tc39.es/ecma262/#sec-number.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
{
diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.h b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h
index ed36778376..bf35eb8332 100644
--- a/Userland/Libraries/LibJS/Runtime/NumberPrototype.h
+++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h
@@ -19,6 +19,7 @@ public:
virtual ~NumberPrototype() override;
JS_DECLARE_NATIVE_FUNCTION(to_fixed);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js
new file mode 100644
index 0000000000..72ffb24573
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.prototype.toLocaleString.js
@@ -0,0 +1,78 @@
+describe("errors", () => {
+ test("must be called with numeric |this|", () => {
+ [true, [], {}, Symbol("foo"), "bar", 1n].forEach(value => {
+ expect(() => Number.prototype.toLocaleString.call(value)).toThrowWithMessage(
+ TypeError,
+ "Not an object of type Number"
+ );
+ });
+ });
+});
+
+describe("correct behavior", () => {
+ test("length", () => {
+ expect(Number.prototype.toLocaleString).toHaveLength(0);
+ });
+});
+
+describe("special values", () => {
+ test("NaN", () => {
+ expect(NaN.toLocaleString()).toBe("NaN");
+ expect(NaN.toLocaleString("en")).toBe("NaN");
+ expect(NaN.toLocaleString("ar")).toBe("ู„ูŠุณย ุฑู‚ู…");
+ });
+
+ test("Infinity", () => {
+ expect(Infinity.toLocaleString()).toBe("โˆž");
+ expect(Infinity.toLocaleString("en")).toBe("โˆž");
+ expect(Infinity.toLocaleString("ar")).toBe("โˆž");
+ });
+});
+
+describe("styles", () => {
+ test("decimal", () => {
+ expect((12).toLocaleString("en")).toBe("12");
+ expect((12).toLocaleString("ar")).toBe("\u0661\u0662");
+ });
+
+ test("percent", () => {
+ expect((0.234).toLocaleString("en", { style: "percent" })).toBe("23%");
+ expect((0.234).toLocaleString("ar", { style: "percent" })).toBe("\u0662\u0663\u066a\u061c");
+ });
+
+ test("currency", () => {
+ expect(
+ (1.23).toLocaleString("en", {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ })
+ ).toBe("1.23 US dollars");
+
+ expect(
+ (1.23).toLocaleString("ar", {
+ style: "currency",
+ currency: "USD",
+ currencyDisplay: "name",
+ })
+ ).toBe("\u0661\u066b\u0662\u0663 ุฏูˆู„ุงุฑ ุฃู…ุฑูŠูƒูŠ");
+ });
+
+ test("unit", () => {
+ expect(
+ (1.23).toLocaleString("en", {
+ style: "unit",
+ unit: "kilometer-per-hour",
+ unitDisplay: "long",
+ })
+ ).toBe("1.23 kilometers per hour");
+
+ expect(
+ (1.23).toLocaleString("ar", {
+ style: "unit",
+ unit: "kilometer-per-hour",
+ unitDisplay: "long",
+ })
+ ).toBe("\u0661\u066b\u0662\u0663 ูƒูŠู„ูˆู…ุชุฑ ููŠ ุงู„ุณุงุนุฉ");
+ });
+});