summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-06-19 01:34:17 +0100
committerLinus Groh <mail@linusgroh.de>2021-06-19 01:34:17 +0100
commit8e26c7a1dd770a830f510c91160af7cd79d71a9b (patch)
tree8051be1809872e4029f1a0624e2618c424022cea /Userland/Libraries
parent7f8245439b0c5063377e5af9304e39b9226d6a18 (diff)
downloadserenity-8e26c7a1dd770a830f510c91160af7cd79d71a9b.zip
LibJS: Make Number() constructor spec compliant
By using to_numeric() and adding BigInt handling, we get support for `Number(123n)`. Fixes #8125.
Diffstat (limited to 'Userland/Libraries')
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp39
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Number/Number.js41
2 files changed, 55 insertions, 25 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp b/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
index 5962b579cf..ac4f7a1917 100644
--- a/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
+++ b/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -58,24 +58,41 @@ NumberConstructor::~NumberConstructor()
{
}
+// Most of 21.1.1.1 Number ( value ) factored into a separate function for sharing between call() and construct().
+static Value get_value_from_constructor_argument(GlobalObject& global_object)
+{
+ auto& vm = global_object.vm();
+
+ Value number;
+ if (vm.argument_count() > 0) {
+ auto primitive = vm.argument(0).to_numeric(global_object);
+ if (vm.exception())
+ return {};
+ if (primitive.is_bigint()) {
+ // FIXME: How should huge values be handled here?
+ auto& big_integer = primitive.as_bigint().big_integer();
+ number = Value(static_cast<double>(big_integer.unsigned_value().to_u64()) * (big_integer.is_negative() ? -1.0 : 1.0));
+ } else {
+ number = primitive;
+ }
+ } else {
+ number = Value(0);
+ }
+ return number;
+}
+
// 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
Value NumberConstructor::call()
{
- if (!vm().argument_count())
- return Value(0);
- return vm().argument(0).to_number(global_object());
+ return get_value_from_constructor_argument(global_object());
}
// 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
Value NumberConstructor::construct(Function&)
{
- double number = 0;
- if (vm().argument_count()) {
- number = vm().argument(0).to_double(global_object());
- if (vm().exception())
- return {};
- }
- return NumberObject::create(global_object(), number);
+ auto number = get_value_from_constructor_argument(global_object());
+ // FIXME: Use OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%")
+ return NumberObject::create(global_object(), number.as_double());
}
// 21.1.2.2 Number.isFinite ( number ), https://tc39.es/ecma262/#sec-number.isfinite
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Number/Number.js b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.js
index 7fd7067449..7ee0f557a9 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Number/Number.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Number/Number.js
@@ -1,33 +1,46 @@
-test("basic functionality", () => {
+test("length is 1", () => {
expect(Number).toHaveLength(1);
- expect(Number.name).toBe("Number");
- expect(Number.prototype).not.toHaveProperty("length");
+});
+test("constructor without new", () => {
expect(typeof Number()).toBe("number");
expect(typeof new Number()).toBe("object");
expect(Number()).toBe(0);
- expect(new Number().valueOf()).toBe(0);
+ expect(Number(123)).toBe(123);
+ expect(Number(-123)).toBe(-123);
+ expect(Number(123n)).toBe(123);
+ expect(Number(-123n)).toBe(-123);
expect(Number("42")).toBe(42);
- expect(new Number("42").valueOf()).toBe(42);
expect(Number(null)).toBe(0);
- expect(new Number(null).valueOf()).toBe(0);
expect(Number(true)).toBe(1);
- expect(new Number(true).valueOf()).toBe(1);
expect(Number("Infinity")).toBe(Infinity);
- expect(new Number("Infinity").valueOf()).toBe(Infinity);
expect(Number("+Infinity")).toBe(Infinity);
- expect(new Number("+Infinity").valueOf()).toBe(Infinity);
expect(Number("-Infinity")).toBe(-Infinity);
- expect(new Number("-Infinity").valueOf()).toBe(-Infinity);
expect(Number(undefined)).toBeNaN();
- expect(new Number(undefined).valueOf()).toBeNaN();
expect(Number({})).toBeNaN();
- expect(new Number({}).valueOf()).toBeNaN();
expect(Number({ a: 1 })).toBeNaN();
- expect(new Number({ a: 1 }).valueOf()).toBeNaN();
expect(Number([1, 2, 3])).toBeNaN();
- expect(new Number([1, 2, 3]).valueOf()).toBeNaN();
expect(Number("foo")).toBeNaN();
+});
+
+test("constructor with new", () => {
+ expect(typeof new Number()).toBe("object");
+
+ expect(new Number().valueOf()).toBe(0);
+ expect(new Number(123).valueOf()).toBe(123);
+ expect(new Number(-123).valueOf()).toBe(-123);
+ expect(new Number(123n).valueOf()).toBe(123);
+ expect(new Number(-123n).valueOf()).toBe(-123);
+ expect(new Number("42").valueOf()).toBe(42);
+ expect(new Number(null).valueOf()).toBe(0);
+ expect(new Number(true).valueOf()).toBe(1);
+ expect(new Number("Infinity").valueOf()).toBe(Infinity);
+ expect(new Number("+Infinity").valueOf()).toBe(Infinity);
+ expect(new Number("-Infinity").valueOf()).toBe(-Infinity);
+ expect(new Number(undefined).valueOf()).toBeNaN();
+ expect(new Number({}).valueOf()).toBeNaN();
+ expect(new Number({ a: 1 }).valueOf()).toBeNaN();
+ expect(new Number([1, 2, 3]).valueOf()).toBeNaN();
expect(new Number("foo").valueOf()).toBeNaN();
});