summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
blob: 6e61b56c6a41973793ba502c88a6ff248919c409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * 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);
}

}