1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 19:27:45 +00:00

LibJS: Make Number() constructor spec compliant

By using to_numeric() and adding BigInt handling, we get support for
`Number(123n)`.

Fixes #8125.
This commit is contained in:
Linus Groh 2021-06-19 01:34:17 +01:00
parent 7f8245439b
commit 8e26c7a1dd
2 changed files with 55 additions and 25 deletions

View file

@ -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 * 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 // 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
Value NumberConstructor::call() Value NumberConstructor::call()
{ {
if (!vm().argument_count()) return get_value_from_constructor_argument(global_object());
return Value(0);
return vm().argument(0).to_number(global_object());
} }
// 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value // 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
Value NumberConstructor::construct(Function&) Value NumberConstructor::construct(Function&)
{ {
double number = 0; auto number = get_value_from_constructor_argument(global_object());
if (vm().argument_count()) { // FIXME: Use OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%")
number = vm().argument(0).to_double(global_object()); return NumberObject::create(global_object(), number.as_double());
if (vm().exception())
return {};
}
return NumberObject::create(global_object(), number);
} }
// 21.1.2.2 Number.isFinite ( number ), https://tc39.es/ecma262/#sec-number.isfinite // 21.1.2.2 Number.isFinite ( number ), https://tc39.es/ecma262/#sec-number.isfinite

View file

@ -1,33 +1,46 @@
test("basic functionality", () => { test("length is 1", () => {
expect(Number).toHaveLength(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 Number()).toBe("number");
expect(typeof new Number()).toBe("object"); expect(typeof new Number()).toBe("object");
expect(Number()).toBe(0); 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(Number("42")).toBe(42);
expect(new Number("42").valueOf()).toBe(42);
expect(Number(null)).toBe(0); expect(Number(null)).toBe(0);
expect(new Number(null).valueOf()).toBe(0);
expect(Number(true)).toBe(1); expect(Number(true)).toBe(1);
expect(new Number(true).valueOf()).toBe(1);
expect(Number("Infinity")).toBe(Infinity); expect(Number("Infinity")).toBe(Infinity);
expect(new Number("Infinity").valueOf()).toBe(Infinity);
expect(Number("+Infinity")).toBe(Infinity); expect(Number("+Infinity")).toBe(Infinity);
expect(new Number("+Infinity").valueOf()).toBe(Infinity);
expect(Number("-Infinity")).toBe(-Infinity); expect(Number("-Infinity")).toBe(-Infinity);
expect(new Number("-Infinity").valueOf()).toBe(-Infinity);
expect(Number(undefined)).toBeNaN(); expect(Number(undefined)).toBeNaN();
expect(new Number(undefined).valueOf()).toBeNaN();
expect(Number({})).toBeNaN(); expect(Number({})).toBeNaN();
expect(new Number({}).valueOf()).toBeNaN();
expect(Number({ a: 1 })).toBeNaN(); expect(Number({ a: 1 })).toBeNaN();
expect(new Number({ a: 1 }).valueOf()).toBeNaN();
expect(Number([1, 2, 3])).toBeNaN(); expect(Number([1, 2, 3])).toBeNaN();
expect(new Number([1, 2, 3]).valueOf()).toBeNaN();
expect(Number("foo")).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(); expect(new Number("foo").valueOf()).toBeNaN();
}); });