ladybird/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
Timothy Flynn 2692db8699 LibJS+Everywhere: Allow Cell::initialize overrides to throw OOM errors
Note that as of this commit, there aren't any such throwers, and the
call site in Heap::allocate will drop exceptions on the floor. This
commit only serves to change the declaration of the overrides, make sure
they return an empty value, and to propagate OOM errors frm their base
initialize invocations.
2023-01-29 00:02:45 +00:00

137 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Math.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NumberConstructor.h>
#include <LibJS/Runtime/NumberObject.h>
#if defined(AK_COMPILER_CLANG)
# define EPSILON_VALUE AK::exp2(-52.)
# define MAX_SAFE_INTEGER_VALUE AK::exp2(53.) - 1
# define MIN_SAFE_INTEGER_VALUE -(AK::exp2(53.) - 1)
#else
constexpr double const EPSILON_VALUE { __builtin_exp2(-52) };
constexpr double const MAX_SAFE_INTEGER_VALUE { __builtin_exp2(53) - 1 };
constexpr double const MIN_SAFE_INTEGER_VALUE { -(__builtin_exp2(53) - 1) };
#endif
namespace JS {
NumberConstructor::NumberConstructor(Realm& realm)
: NativeFunction(realm.vm().names.Number.as_string(), *realm.intrinsics().function_prototype())
{
}
ThrowCompletionOr<void> NumberConstructor::initialize(Realm& realm)
{
auto& vm = this->vm();
MUST_OR_THROW_OOM(NativeFunction::initialize(realm));
// 21.1.2.15 Number.prototype, https://tc39.es/ecma262/#sec-number.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().number_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.isFinite, is_finite, 1, attr);
define_native_function(realm, vm.names.isInteger, is_integer, 1, attr);
define_native_function(realm, vm.names.isNaN, is_nan, 1, attr);
define_native_function(realm, vm.names.isSafeInteger, is_safe_integer, 1, attr);
define_direct_property(vm.names.parseInt, realm.intrinsics().parse_int_function(), attr);
define_direct_property(vm.names.parseFloat, realm.intrinsics().parse_float_function(), attr);
define_direct_property(vm.names.EPSILON, Value(EPSILON_VALUE), 0);
define_direct_property(vm.names.MAX_VALUE, Value(NumericLimits<double>::max()), 0);
define_direct_property(vm.names.MIN_VALUE, Value(NumericLimits<double>::min()), 0);
define_direct_property(vm.names.MAX_SAFE_INTEGER, Value(MAX_SAFE_INTEGER_VALUE), 0);
define_direct_property(vm.names.MIN_SAFE_INTEGER, Value(MIN_SAFE_INTEGER_VALUE), 0);
define_direct_property(vm.names.NEGATIVE_INFINITY, js_negative_infinity(), 0);
define_direct_property(vm.names.POSITIVE_INFINITY, js_infinity(), 0);
define_direct_property(vm.names.NaN, js_nan(), 0);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
return {};
}
// Most of 21.1.1.1 Number ( value ) factored into a separate function for sharing between call() and construct().
static ThrowCompletionOr<Value> get_value_from_constructor_argument(VM& vm)
{
Value number;
// 1. If value is present, then
if (vm.argument_count() > 0) {
// a. Let prim be ? ToNumeric(value).
auto primitive = TRY(vm.argument(0).to_numeric(vm));
// b. If Type(prim) is BigInt, let n be 𝔽((prim)).
if (primitive.is_bigint()) {
number = Value(primitive.as_bigint().big_integer().to_double(Crypto::UnsignedBigInteger::RoundingMode::ECMAScriptNumberValueFor));
}
// c. Otherwise, let n be prim.
else {
number = primitive;
}
}
// 2. Else,
else {
// a. Let n be +0𝔽.
number = Value(0);
}
return number;
}
// 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
ThrowCompletionOr<Value> NumberConstructor::call()
{
// NOTE: get_value_from_constructor_argument performs steps 1 and 2 and returns n.
// 3. If NewTarget is undefined, return n.
return get_value_from_constructor_argument(vm());
}
// 21.1.1.1 Number ( value ), https://tc39.es/ecma262/#sec-number-constructor-number-value
ThrowCompletionOr<NonnullGCPtr<Object>> NumberConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// NOTE: get_value_from_constructor_argument performs steps 1 and 2 and returns n.
auto number = TRY(get_value_from_constructor_argument(vm));
// 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%", « [[NumberData]] »).
// 5. Set O.[[NumberData]] to n.
// 6. Return O.
return TRY(ordinary_create_from_constructor<NumberObject>(vm, new_target, &Intrinsics::number_prototype, number.as_double()));
}
// 21.1.2.2 Number.isFinite ( number ), https://tc39.es/ecma262/#sec-number.isfinite
JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_finite)
{
return Value(vm.argument(0).is_finite_number());
}
// 21.1.2.3 Number.isInteger ( number ), https://tc39.es/ecma262/#sec-number.isinteger
JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_integer)
{
return Value(vm.argument(0).is_integral_number());
}
// 21.1.2.4 Number.isNaN ( number ), https://tc39.es/ecma262/#sec-number.isnan
JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_nan)
{
return Value(vm.argument(0).is_nan());
}
// 21.1.2.5 Number.isSafeInteger ( number ), https://tc39.es/ecma262/#sec-number.issafeinteger
JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_safe_integer)
{
if (!vm.argument(0).is_number())
return Value(false);
if (!vm.argument(0).is_integral_number())
return Value(false);
auto value = vm.argument(0).as_double();
return Value(value >= MIN_SAFE_INTEGER_VALUE && value <= MAX_SAFE_INTEGER_VALUE);
}
}