diff options
author | Linus Groh <mail@linusgroh.de> | 2021-06-19 01:34:17 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-06-19 01:34:17 +0100 |
commit | 8e26c7a1dd770a830f510c91160af7cd79d71a9b (patch) | |
tree | 8051be1809872e4029f1a0624e2618c424022cea /Userland/Libraries | |
parent | 7f8245439b0c5063377e5af9304e39b9226d6a18 (diff) | |
download | serenity-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.cpp | 39 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/builtins/Number/Number.js | 41 |
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(); }); |