1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-16 20:15:07 +00:00
serenity/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
Linus Groh 50428ea8d2 LibJS: Move intrinsics to the realm
Intrinsics, i.e. mostly constructor and prototype objects, but also
things like empty and new object shape now live on a new heap-allocated
JS::Intrinsics object, thus completing the long journey of taking all
the magic away from the global object.
This represents the Realm's [[Intrinsics]] slot in the spec and matches
its existing [[GlobalObject]] / [[GlobalEnv]] slots in terms of
architecture.

In the majority of cases it should now be possibly to fully allocate a
regular object without the global object existing, and in fact that's
what we do now - the realm is allocated before the global object, and
the intrinsics between both :^)
2022-08-27 11:29:10 +01:00

136 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/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NumberConstructor.h>
#include <LibJS/Runtime/NumberObject.h>
#ifdef __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(vm().names.Number.as_string(), *realm.intrinsics().function_prototype())
{
}
void NumberConstructor::initialize(Realm& realm)
{
auto& vm = this->vm();
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);
// NOTE: These are set from the global object as at this point we don't have them allocated yet;
// The native functions are part of the global object itself.
define_direct_property(vm.names.parseInt, js_undefined(), attr);
define_direct_property(vm.names.parseFloat, js_undefined(), 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);
}
// 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<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);
}
}