Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibJS/Runtime/MathObject.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2023, Andreas Kling <kling@serenityos.org>
3
 * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
4
 * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
5
 * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 */
9
10
#include <AK/BuiltinWrappers.h>
11
#include <AK/Function.h>
12
#include <AK/Random.h>
13
#include <LibJS/Runtime/GlobalObject.h>
14
#include <LibJS/Runtime/MathObject.h>
15
#include <LibJS/Runtime/ValueInlines.h>
16
#include <math.h>
17
18
namespace JS {
19
20
JS_DEFINE_ALLOCATOR(MathObject);
21
22
MathObject::MathObject(Realm& realm)
23
0
    : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype())
24
0
{
25
0
}
26
27
void MathObject::initialize(Realm& realm)
28
0
{
29
0
    auto& vm = this->vm();
30
0
    Base::initialize(realm);
31
0
    u8 attr = Attribute::Writable | Attribute::Configurable;
32
0
    define_native_function(realm, vm.names.abs, abs, 1, attr, Bytecode::Builtin::MathAbs);
33
0
    define_native_function(realm, vm.names.random, random, 0, attr);
34
0
    define_native_function(realm, vm.names.sqrt, sqrt, 1, attr, Bytecode::Builtin::MathSqrt);
35
0
    define_native_function(realm, vm.names.floor, floor, 1, attr, Bytecode::Builtin::MathFloor);
36
0
    define_native_function(realm, vm.names.ceil, ceil, 1, attr, Bytecode::Builtin::MathCeil);
37
0
    define_native_function(realm, vm.names.round, round, 1, attr, Bytecode::Builtin::MathRound);
38
0
    define_native_function(realm, vm.names.max, max, 2, attr);
39
0
    define_native_function(realm, vm.names.min, min, 2, attr);
40
0
    define_native_function(realm, vm.names.trunc, trunc, 1, attr);
41
0
    define_native_function(realm, vm.names.sin, sin, 1, attr);
42
0
    define_native_function(realm, vm.names.cos, cos, 1, attr);
43
0
    define_native_function(realm, vm.names.tan, tan, 1, attr);
44
0
    define_native_function(realm, vm.names.pow, pow, 2, attr, Bytecode::Builtin::MathPow);
45
0
    define_native_function(realm, vm.names.exp, exp, 1, attr, Bytecode::Builtin::MathExp);
46
0
    define_native_function(realm, vm.names.expm1, expm1, 1, attr);
47
0
    define_native_function(realm, vm.names.sign, sign, 1, attr);
48
0
    define_native_function(realm, vm.names.clz32, clz32, 1, attr);
49
0
    define_native_function(realm, vm.names.acos, acos, 1, attr);
50
0
    define_native_function(realm, vm.names.acosh, acosh, 1, attr);
51
0
    define_native_function(realm, vm.names.asin, asin, 1, attr);
52
0
    define_native_function(realm, vm.names.asinh, asinh, 1, attr);
53
0
    define_native_function(realm, vm.names.atan, atan, 1, attr);
54
0
    define_native_function(realm, vm.names.atanh, atanh, 1, attr);
55
0
    define_native_function(realm, vm.names.log1p, log1p, 1, attr);
56
0
    define_native_function(realm, vm.names.cbrt, cbrt, 1, attr);
57
0
    define_native_function(realm, vm.names.atan2, atan2, 2, attr);
58
0
    define_native_function(realm, vm.names.fround, fround, 1, attr);
59
0
    define_native_function(realm, vm.names.hypot, hypot, 2, attr);
60
0
    define_native_function(realm, vm.names.imul, imul, 2, attr);
61
0
    define_native_function(realm, vm.names.log, log, 1, attr, Bytecode::Builtin::MathLog);
62
0
    define_native_function(realm, vm.names.log2, log2, 1, attr);
63
0
    define_native_function(realm, vm.names.log10, log10, 1, attr);
64
0
    define_native_function(realm, vm.names.sinh, sinh, 1, attr);
65
0
    define_native_function(realm, vm.names.cosh, cosh, 1, attr);
66
0
    define_native_function(realm, vm.names.tanh, tanh, 1, attr);
67
68
    // 21.3.1 Value Properties of the Math Object, https://tc39.es/ecma262/#sec-value-properties-of-the-math-object
69
0
    define_direct_property(vm.names.E, Value(M_E), 0);
70
0
    define_direct_property(vm.names.LN2, Value(M_LN2), 0);
71
0
    define_direct_property(vm.names.LN10, Value(M_LN10), 0);
72
0
    define_direct_property(vm.names.LOG2E, Value(::log2(M_E)), 0);
73
0
    define_direct_property(vm.names.LOG10E, Value(::log10(M_E)), 0);
74
0
    define_direct_property(vm.names.PI, Value(M_PI), 0);
75
0
    define_direct_property(vm.names.SQRT1_2, Value(M_SQRT1_2), 0);
76
0
    define_direct_property(vm.names.SQRT2, Value(M_SQRT2), 0);
77
78
    // 21.3.1.9 Math [ @@toStringTag ], https://tc39.es/ecma262/#sec-math-@@tostringtag
79
0
    define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.Math.as_string()), Attribute::Configurable);
80
0
}
81
82
// 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
83
ThrowCompletionOr<Value> MathObject::abs_impl(VM& vm, Value x)
84
0
{
85
    // OPTIMIZATION: Fast path for Int32 values.
86
0
    if (x.is_int32())
87
0
        return Value(AK::abs(x.as_i32()));
88
89
    // Let n be ? ToNumber(x).
90
0
    auto number = TRY(x.to_number(vm));
91
92
    // 2. If n is NaN, return NaN.
93
0
    if (number.is_nan())
94
0
        return js_nan();
95
96
    // 3. If n is -0𝔽, return +0𝔽.
97
0
    if (number.is_negative_zero())
98
0
        return Value(0);
99
100
    // 4. If n is -∞𝔽, return +∞𝔽.
101
0
    if (number.is_negative_infinity())
102
0
        return js_infinity();
103
104
    // 5. If n < -0𝔽, return -n.
105
    // 6. Return n.
106
0
    return Value(number.as_double() < 0 ? -number.as_double() : number.as_double());
107
0
}
108
109
// 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
110
JS_DEFINE_NATIVE_FUNCTION(MathObject::abs)
111
0
{
112
0
    return abs_impl(vm, vm.argument(0));
113
0
}
114
115
// 21.3.2.2 Math.acos ( x ), https://tc39.es/ecma262/#sec-math.acos
116
JS_DEFINE_NATIVE_FUNCTION(MathObject::acos)
117
0
{
118
    // 1. Let n be ? ToNumber(x).
119
0
    auto number = TRY(vm.argument(0).to_number(vm));
120
121
    // 2. If n is NaN, n > 1𝔽, or n < -1𝔽, return NaN.
122
0
    if (number.is_nan() || number.as_double() > 1 || number.as_double() < -1)
123
0
        return js_nan();
124
125
    // 3. If n is 1𝔽, return +0𝔽.
126
0
    if (number.as_double() == 1)
127
0
        return Value(0);
128
129
    // 4. Return an implementation-approximated Number value representing the result of the inverse cosine of ℝ(n).
130
0
    return Value(::acos(number.as_double()));
131
0
}
132
133
// 21.3.2.3 Math.acosh ( x ), https://tc39.es/ecma262/#sec-math.acosh
134
JS_DEFINE_NATIVE_FUNCTION(MathObject::acosh)
135
0
{
136
    // 1. Let n be ? ToNumber(x).
137
0
    auto number = TRY(vm.argument(0).to_number(vm));
138
139
    // 2. If n is NaN or n is +∞𝔽, return n.
140
0
    if (number.is_nan() || number.is_positive_infinity())
141
0
        return number;
142
143
    // 3. If n is 1𝔽, return +0𝔽.
144
0
    if (number.as_double() == 1.0)
145
0
        return Value(0.0);
146
147
    // 4. If n < 1𝔽, return NaN.
148
0
    if (number.as_double() < 1)
149
0
        return js_nan();
150
151
    // 5. Return an implementation-approximated Number value representing the result of the inverse hyperbolic cosine of ℝ(n).
152
0
    return Value(::acosh(number.as_double()));
153
0
}
154
155
// 21.3.2.4 Math.asin ( x ), https://tc39.es/ecma262/#sec-math.asin
156
JS_DEFINE_NATIVE_FUNCTION(MathObject::asin)
157
0
{
158
    // 1. Let n be ? ToNumber(x).
159
0
    auto number = TRY(vm.argument(0).to_number(vm));
160
161
    // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
162
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
163
0
        return number;
164
165
    // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
166
0
    if (number.as_double() > 1 || number.as_double() < -1)
167
0
        return js_nan();
168
169
    // 4. Return an implementation-approximated Number value representing the result of the inverse sine of ℝ(n).
170
0
    return Value(::asin(number.as_double()));
171
0
}
172
173
// 21.3.2.5 Math.asinh ( x ), https://tc39.es/ecma262/#sec-math.asinh
174
JS_DEFINE_NATIVE_FUNCTION(MathObject::asinh)
175
0
{
176
    // 1. Let n be ? ToNumber(x).
177
0
    auto number = TRY(vm.argument(0).to_number(vm));
178
179
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
180
0
    if (!number.is_finite_number() || number.is_positive_zero() || number.is_negative_zero())
181
0
        return number;
182
183
    // 3. Return an implementation-approximated Number value representing the result of the inverse hyperbolic sine of ℝ(n).
184
0
    return Value(::asinh(number.as_double()));
185
0
}
186
187
// 21.3.2.6 Math.atan ( x ), https://tc39.es/ecma262/#sec-math.atan
188
JS_DEFINE_NATIVE_FUNCTION(MathObject::atan)
189
0
{
190
    // Let n be ? ToNumber(x).
191
0
    auto number = TRY(vm.argument(0).to_number(vm));
192
193
    // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n.
194
0
    if (number.is_nan() || number.as_double() == 0)
195
0
        return number;
196
197
    // 3. If n is +∞𝔽, return an implementation-approximated Number value representing π / 2.
198
0
    if (number.is_positive_infinity())
199
0
        return Value(M_PI_2);
200
201
    // 4. If n is -∞𝔽, return an implementation-approximated Number value representing -π / 2.
202
0
    if (number.is_negative_infinity())
203
0
        return Value(-M_PI_2);
204
205
    // 5. Return an implementation-approximated Number value representing the result of the inverse tangent of ℝ(n).
206
0
    return Value(::atan(number.as_double()));
207
0
}
208
209
// 21.3.2.7 Math.atanh ( x ), https://tc39.es/ecma262/#sec-math.atanh
210
JS_DEFINE_NATIVE_FUNCTION(MathObject::atanh)
211
0
{
212
    // 1. Let n be ? ToNumber(x).
213
0
    auto number = TRY(vm.argument(0).to_number(vm));
214
215
    // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
216
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
217
0
        return number;
218
219
    // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
220
0
    if (number.as_double() > 1. || number.as_double() < -1.)
221
0
        return js_nan();
222
223
    // 4. If n is 1𝔽, return +∞𝔽.
224
0
    if (number.as_double() == 1.)
225
0
        return js_infinity();
226
227
    // 5. If n is -1𝔽, return -∞𝔽.
228
0
    if (number.as_double() == -1.)
229
0
        return js_negative_infinity();
230
231
    // 6. Return an implementation-approximated Number value representing the result of the inverse hyperbolic tangent of ℝ(n).
232
0
    return Value(::atanh(number.as_double()));
233
0
}
234
235
// 21.3.2.8 Math.atan2 ( y, x ), https://tc39.es/ecma262/#sec-math.atan2
236
JS_DEFINE_NATIVE_FUNCTION(MathObject::atan2)
237
0
{
238
0
    auto constexpr three_quarters_pi = M_PI_4 + M_PI_2;
239
240
    // 1. Let ny be ? ToNumber(y).
241
0
    auto y = TRY(vm.argument(0).to_number(vm));
242
243
    // 2. Let nx be ? ToNumber(x).
244
0
    auto x = TRY(vm.argument(1).to_number(vm));
245
246
    // 3. If ny is NaN or nx is NaN, return NaN.
247
0
    if (y.is_nan() || x.is_nan())
248
0
        return js_nan();
249
250
    // 4. If ny is +∞𝔽, then
251
0
    if (y.is_positive_infinity()) {
252
        // a. If nx is +∞𝔽, return an implementation-approximated Number value representing π / 4.
253
0
        if (x.is_positive_infinity())
254
0
            return Value(M_PI_4);
255
256
        // b. If nx is -∞𝔽, return an implementation-approximated Number value representing 3π / 4.
257
0
        if (x.is_negative_infinity())
258
0
            return Value(three_quarters_pi);
259
260
        // c. Return an implementation-approximated Number value representing π / 2.
261
0
        return Value(M_PI_2);
262
0
    }
263
264
    // 5. If ny is -∞𝔽, then
265
0
    if (y.is_negative_infinity()) {
266
        // a. If nx is +∞𝔽, return an implementation-approximated Number value representing -π / 4.
267
0
        if (x.is_positive_infinity())
268
0
            return Value(-M_PI_4);
269
270
        // b. If nx is -∞𝔽, return an implementation-approximated Number value representing -3π / 4.
271
0
        if (x.is_negative_infinity())
272
0
            return Value(-three_quarters_pi);
273
274
        // c. Return an implementation-approximated Number value representing -π / 2.
275
0
        return Value(-M_PI_2);
276
0
    }
277
278
    // 6. If ny is +0𝔽, then
279
0
    if (y.is_positive_zero()) {
280
        // a. If nx > +0𝔽 or nx is +0𝔽, return +0𝔽.
281
0
        if (x.as_double() > 0 || x.is_positive_zero())
282
0
            return Value(0.0);
283
284
        // b. Return an implementation-approximated Number value representing π.
285
0
        return Value(M_PI);
286
0
    }
287
288
    // 7. If ny is -0𝔽, then
289
0
    if (y.is_negative_zero()) {
290
        // a. If nx > +0𝔽 or nx is +0𝔽, return -0𝔽
291
0
        if (x.as_double() > 0 || x.is_positive_zero())
292
0
            return Value(-0.0);
293
294
        // b. Return an implementation-approximated Number value representing -π.
295
0
        return Value(-M_PI);
296
0
    }
297
298
    // 8. Assert: ny is finite and is neither +0𝔽 nor -0𝔽.
299
0
    VERIFY(y.is_finite_number() && !y.is_positive_zero() && !y.is_negative_zero());
300
301
    // 9. If ny > +0𝔽, then
302
0
    if (y.as_double() > 0) {
303
        // a. If nx is +∞𝔽, return +0𝔽.
304
0
        if (x.is_positive_infinity())
305
0
            return Value(0);
306
307
        // b. If nx is -∞𝔽, return an implementation-approximated Number value representing π.
308
0
        if (x.is_negative_infinity())
309
0
            return Value(M_PI);
310
311
        // c. If nx is either +0𝔽 or -0𝔽, return an implementation-approximated Number value representing π / 2.
312
0
        if (x.is_positive_zero() || x.is_negative_zero())
313
0
            return Value(M_PI_2);
314
0
    }
315
316
    // 10. If ny < -0𝔽, then
317
0
    if (y.as_double() < -0) {
318
        // a. If nx is +∞𝔽, return -0𝔽.
319
0
        if (x.is_positive_infinity())
320
0
            return Value(-0.0);
321
322
        // b. If nx is -∞𝔽, return an implementation-approximated Number value representing -π.
323
0
        if (x.is_negative_infinity())
324
0
            return Value(-M_PI);
325
326
        // c. If nx is either +0𝔽 or -0𝔽, return an implementation-approximated Number value representing -π / 2.
327
0
        if (x.is_positive_zero() || x.is_negative_zero())
328
0
            return Value(-M_PI_2);
329
0
    }
330
331
    // 11. Assert: nx is finite and is neither +0𝔽 nor -0𝔽.
332
0
    VERIFY(x.is_finite_number() && !x.is_positive_zero() && !x.is_negative_zero());
333
334
    // 12. Return an implementation-approximated Number value representing the result of the inverse tangent of the quotient ℝ(ny) / ℝ(nx).
335
0
    return Value(::atan2(y.as_double(), x.as_double()));
336
0
}
337
338
// 21.3.2.9 Math.cbrt ( x ), https://tc39.es/ecma262/#sec-math.cbrt
339
JS_DEFINE_NATIVE_FUNCTION(MathObject::cbrt)
340
0
{
341
    // 1. Let n be ? ToNumber(x).
342
0
    auto number = TRY(vm.argument(0).to_number(vm));
343
344
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
345
0
    if (!number.is_finite_number() || number.as_double() == 0)
346
0
        return number;
347
348
    // 3. Return an implementation-approximated Number value representing the result of the cube root of ℝ(n).
349
0
    return Value(::cbrt(number.as_double()));
350
0
}
351
352
// 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
353
ThrowCompletionOr<Value> MathObject::ceil_impl(VM& vm, Value x)
354
0
{
355
    // 1. Let n be ? ToNumber(x).
356
0
    auto number = TRY(x.to_number(vm));
357
358
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
359
0
    if (!number.is_finite_number() || number.as_double() == 0)
360
0
        return number;
361
362
    // 3. If n < -0𝔽 and n > -1𝔽, return -0𝔽.
363
0
    if (number.as_double() < 0 && number.as_double() > -1)
364
0
        return Value(-0.f);
365
366
    // 4. If n is an integral Number, return n.
367
    // 5. Return the smallest (closest to -∞) integral Number value that is not less than n.
368
0
    return Value(::ceil(number.as_double()));
369
0
}
370
371
// 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
372
JS_DEFINE_NATIVE_FUNCTION(MathObject::ceil)
373
0
{
374
0
    return ceil_impl(vm, vm.argument(0));
375
0
}
376
377
// 21.3.2.11 Math.clz32 ( x ), https://tc39.es/ecma262/#sec-math.clz32
378
JS_DEFINE_NATIVE_FUNCTION(MathObject::clz32)
379
0
{
380
    // 1. Let n be ? ToUint32(x).
381
0
    auto number = TRY(vm.argument(0).to_u32(vm));
382
383
    // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
384
    // 3. Return 𝔽(p).
385
0
    return Value(count_leading_zeroes_safe(number));
386
0
}
387
388
// 21.3.2.12 Math.cos ( x ), https://tc39.es/ecma262/#sec-math.cos
389
JS_DEFINE_NATIVE_FUNCTION(MathObject::cos)
390
0
{
391
    // 1. Let n be ? ToNumber(x).
392
0
    auto number = TRY(vm.argument(0).to_number(vm));
393
394
    // 2. If n is NaN, n is +∞𝔽, or n is -∞𝔽, return NaN.
395
0
    if (number.is_nan() || number.is_infinity())
396
0
        return js_nan();
397
398
    // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
399
0
    if (number.is_positive_zero() || number.is_negative_zero())
400
0
        return Value(1);
401
402
    // 4. Return an implementation-approximated Number value representing the result of the cosine of ℝ(n).
403
0
    return Value(::cos(number.as_double()));
404
0
}
405
406
// 21.3.2.13 Math.cosh ( x ), https://tc39.es/ecma262/#sec-math.cosh
407
JS_DEFINE_NATIVE_FUNCTION(MathObject::cosh)
408
0
{
409
    // 1. Let n be ? ToNumber(x).
410
0
    auto number = TRY(vm.argument(0).to_number(vm));
411
412
    // 2. If n is NaN, return NaN.
413
0
    if (number.is_nan())
414
0
        return js_nan();
415
416
    // 3. If n is +∞𝔽 or n is -∞𝔽, return +∞𝔽.
417
0
    if (number.is_positive_infinity() || number.is_negative_infinity())
418
0
        return js_infinity();
419
420
    // 4. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
421
0
    if (number.is_positive_zero() || number.is_negative_zero())
422
0
        return Value(1);
423
424
    // 5. Return an implementation-approximated Number value representing the result of the hyperbolic cosine of ℝ(n).
425
0
    return Value(::cosh(number.as_double()));
426
0
}
427
428
// 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
429
ThrowCompletionOr<Value> MathObject::exp_impl(VM& vm, Value x)
430
0
{
431
    // 1. Let n be ? ToNumber(x).
432
0
    auto number = TRY(x.to_number(vm));
433
434
    // 2. If n is either NaN or +∞𝔽, return n.
435
0
    if (number.is_nan() || number.is_positive_infinity())
436
0
        return number;
437
438
    // 3. If n is either +0𝔽 or -0𝔽, return 1𝔽.
439
0
    if (number.as_double() == 0)
440
0
        return Value(1);
441
442
    // 4. If n is -∞𝔽, return +0𝔽.
443
0
    if (number.is_negative_infinity())
444
0
        return Value(0);
445
446
    // 5. Return an implementation-approximated Number value representing the result of the exponential function of ℝ(n).
447
0
    return Value(::exp(number.as_double()));
448
0
}
449
450
// 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
451
JS_DEFINE_NATIVE_FUNCTION(MathObject::exp)
452
0
{
453
0
    return exp_impl(vm, vm.argument(0));
454
0
}
455
456
// 21.3.2.15 Math.expm1 ( x ), https://tc39.es/ecma262/#sec-math.expm1
457
JS_DEFINE_NATIVE_FUNCTION(MathObject::expm1)
458
0
{
459
    // 1. Let n be ? ToNumber(x).
460
0
    auto number = TRY(vm.argument(0).to_number(vm));
461
462
    // 2. If n is one of NaN, +0𝔽, -0𝔽, or +∞𝔽, return n.
463
0
    if (number.is_nan() || number.as_double() == 0 || number.is_positive_infinity())
464
0
        return number;
465
466
    // 3. If n is -∞𝔽, return -1𝔽.
467
0
    if (number.is_negative_infinity())
468
0
        return Value(-1);
469
470
    // 4. Return an implementation-approximated Number value representing the result of subtracting 1 from the exponential function of ℝ(n).
471
0
    return Value(::expm1(number.as_double()));
472
0
}
473
474
// 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
475
ThrowCompletionOr<Value> MathObject::floor_impl(VM& vm, Value x)
476
0
{
477
    // 1. Let n be ? ToNumber(x).
478
0
    auto number = TRY(x.to_number(vm));
479
480
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
481
0
    if (!number.is_finite_number() || number.as_double() == 0)
482
0
        return number;
483
484
    // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
485
    // 4. If n is an integral Number, return n.
486
    // 5. Return the greatest (closest to +∞) integral Number value that is not greater than n.
487
0
    return Value(::floor(number.as_double()));
488
0
}
489
490
// 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
491
JS_DEFINE_NATIVE_FUNCTION(MathObject::floor)
492
0
{
493
0
    return floor_impl(vm, vm.argument(0));
494
0
}
495
496
// 21.3.2.17 Math.fround ( x ), https://tc39.es/ecma262/#sec-math.fround
497
JS_DEFINE_NATIVE_FUNCTION(MathObject::fround)
498
0
{
499
    // 1. Let n be ? ToNumber(x).
500
0
    auto number = TRY(vm.argument(0).to_number(vm));
501
502
    // 2. If n is NaN, return NaN.
503
0
    if (number.is_nan())
504
0
        return js_nan();
505
506
    // 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n.
507
0
    if (number.as_double() == 0 || number.is_infinity())
508
0
        return number;
509
510
    // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
511
    // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
512
    // 6. Return the ECMAScript Number value corresponding to n64.
513
0
    return Value((float)number.as_double());
514
0
}
515
516
// 21.3.2.18 Math.hypot ( ...args ), https://tc39.es/ecma262/#sec-math.hypot
517
JS_DEFINE_NATIVE_FUNCTION(MathObject::hypot)
518
0
{
519
    // 1. Let coerced be a new empty List.
520
0
    Vector<Value> coerced;
521
522
    // 2. For each element arg of args, do
523
0
    for (size_t i = 0; i < vm.argument_count(); ++i) {
524
        // a. Let n be ? ToNumber(arg).
525
0
        auto number = TRY(vm.argument(i).to_number(vm));
526
527
        // b. Append n to coerced.
528
0
        coerced.append(number);
529
0
    }
530
531
    // 3. For each element number of coerced, do
532
0
    for (auto& number : coerced) {
533
        // a. If number is either +∞𝔽 or -∞𝔽, return +∞𝔽.
534
0
        if (number.is_infinity())
535
0
            return js_infinity();
536
0
    }
537
538
    // 4. Let onlyZero be true.
539
0
    auto only_zero = true;
540
541
0
    double sum_of_squares = 0;
542
543
    // 5. For each element number of coerced, do
544
0
    for (auto& number : coerced) {
545
        // a. If number is NaN, return NaN.
546
        // OPTIMIZATION: For infinities, the result will be infinity with the same sign, so we can return early.
547
0
        if (number.is_nan() || number.is_infinity())
548
0
            return number;
549
550
        // b. If number is neither +0𝔽 nor -0𝔽, set onlyZero to false.
551
0
        if (number.as_double() != 0)
552
0
            only_zero = false;
553
554
0
        sum_of_squares += number.as_double() * number.as_double();
555
0
    }
556
557
    // 6. If onlyZero is true, return +0𝔽.
558
0
    if (only_zero)
559
0
        return Value(0);
560
561
    // 7. Return an implementation-approximated Number value representing the square root of the sum of squares of the mathematical values of the elements of coerced.
562
0
    return Value(::sqrt(sum_of_squares));
563
0
}
564
565
// 21.3.2.19 Math.imul ( x, y ), https://tc39.es/ecma262/#sec-math.imul
566
JS_DEFINE_NATIVE_FUNCTION(MathObject::imul)
567
0
{
568
    // 1. Let a be ℝ(? ToUint32(x)).
569
0
    auto a = TRY(vm.argument(0).to_u32(vm));
570
571
    // 2. Let b be ℝ(? ToUint32(y)).
572
0
    auto b = TRY(vm.argument(1).to_u32(vm));
573
574
    // 3. Let product be (a × b) modulo 2^32.
575
    // 4. If product ≥ 2^31, return 𝔽(product - 2^32); otherwise return 𝔽(product).
576
0
    return Value(static_cast<i32>(a * b));
577
0
}
578
579
// 21.3.2.20 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
580
ThrowCompletionOr<Value> MathObject::log_impl(VM& vm, Value x)
581
0
{
582
    // 1. Let n be ? ToNumber(x).
583
0
    auto number = TRY(x.to_number(vm));
584
585
    // 2. If n is NaN or n is +∞𝔽, return n.
586
0
    if (number.is_nan() || number.is_positive_infinity())
587
0
        return number;
588
589
    // 3. If n is 1𝔽, return +0𝔽.
590
0
    if (number.as_double() == 1.)
591
0
        return Value(0);
592
593
    // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.
594
0
    if (number.is_positive_zero() || number.is_negative_zero())
595
0
        return js_negative_infinity();
596
597
    // 5. If n < -0𝔽, return NaN.
598
0
    if (number.as_double() < -0.)
599
0
        return js_nan();
600
601
    // 6. Return an implementation-approximated Number value representing the result of the natural logarithm of ℝ(n).
602
0
    return Value(::log(number.as_double()));
603
0
}
604
605
// 21.3.2.20 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
606
JS_DEFINE_NATIVE_FUNCTION(MathObject::log)
607
0
{
608
0
    return log_impl(vm, vm.argument(0));
609
0
}
610
611
// 21.3.2.21 Math.log1p ( x ), https://tc39.es/ecma262/#sec-math.log1p
612
JS_DEFINE_NATIVE_FUNCTION(MathObject::log1p)
613
0
{
614
    // 1. Let n be ? ToNumber(x).
615
0
    auto number = TRY(vm.argument(0).to_number(vm));
616
617
    // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +∞𝔽, return n.
618
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero() || number.is_positive_infinity())
619
0
        return number;
620
621
    // 3. If n is -1𝔽, return -∞𝔽.
622
0
    if (number.as_double() == -1.)
623
0
        return js_negative_infinity();
624
625
    // 4. If n < -1𝔽, return NaN.
626
0
    if (number.as_double() < -1.)
627
0
        return js_nan();
628
629
    // 5. Return an implementation-approximated Number value representing the result of the natural logarithm of 1 + ℝ(n).
630
0
    return Value(::log1p(number.as_double()));
631
0
}
632
633
// 21.3.2.22 Math.log10 ( x ), https://tc39.es/ecma262/#sec-math.log10
634
JS_DEFINE_NATIVE_FUNCTION(MathObject::log10)
635
0
{
636
    // 1. Let n be ? ToNumber(x).
637
0
    auto number = TRY(vm.argument(0).to_number(vm));
638
639
    // 2. If n is NaN or n is +∞𝔽, return n.
640
0
    if (number.is_nan() || number.is_positive_infinity())
641
0
        return number;
642
643
    // 3. If n is 1𝔽, return +0𝔽.
644
0
    if (number.as_double() == 1.)
645
0
        return Value(0);
646
647
    // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.
648
0
    if (number.is_positive_zero() || number.is_negative_zero())
649
0
        return js_negative_infinity();
650
651
    // 5. If n < -0𝔽, return NaN.
652
0
    if (number.as_double() < -0.)
653
0
        return js_nan();
654
655
    // 6. Return an implementation-approximated Number value representing the result of the base 10 logarithm of ℝ(n).
656
0
    return Value(::log10(number.as_double()));
657
0
}
658
659
// 21.3.2.23 Math.log2 ( x ), https://tc39.es/ecma262/#sec-math.log2
660
JS_DEFINE_NATIVE_FUNCTION(MathObject::log2)
661
0
{
662
    // 1. Let n be ? ToNumber(x).
663
0
    auto number = TRY(vm.argument(0).to_number(vm));
664
665
    // 2. If n is NaN or n is +∞𝔽, return n.
666
0
    if (number.is_nan() || number.is_positive_infinity())
667
0
        return number;
668
669
    // 3. If n is 1𝔽, return +0𝔽.
670
0
    if (number.as_double() == 1.)
671
0
        return Value(0);
672
673
    // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.
674
0
    if (number.is_positive_zero() || number.is_negative_zero())
675
0
        return js_negative_infinity();
676
677
    // 5. If n < -0𝔽, return NaN.
678
0
    if (number.as_double() < -0.)
679
0
        return js_nan();
680
681
    // 6. Return an implementation-approximated Number value representing the result of the base 2 logarithm of ℝ(n).
682
0
    return Value(::log2(number.as_double()));
683
0
}
684
685
// 21.3.2.24 Math.max ( ...args ), https://tc39.es/ecma262/#sec-math.max
686
JS_DEFINE_NATIVE_FUNCTION(MathObject::max)
687
0
{
688
    // 1. Let coerced be a new empty List.
689
0
    Vector<Value> coerced;
690
691
    // 2. For each element arg of args, do
692
0
    for (size_t i = 0; i < vm.argument_count(); ++i) {
693
        // a. Let n be ? ToNumber(arg).
694
0
        auto number = TRY(vm.argument(i).to_number(vm));
695
696
        // b. Append n to coerced.
697
0
        coerced.append(number);
698
0
    }
699
700
    // 3. Let highest be -∞𝔽.
701
0
    auto highest = js_negative_infinity();
702
703
    // 4. For each element number of coerced, do
704
0
    for (auto& number : coerced) {
705
        // a. If number is NaN, return NaN.
706
0
        if (number.is_nan())
707
0
            return js_nan();
708
709
        // b. If number is +0𝔽 and highest is -0𝔽, set highest to +0𝔽.
710
        // c. If number > highest, set highest to number.
711
0
        if ((number.is_positive_zero() && highest.is_negative_zero()) || number.as_double() > highest.as_double())
712
0
            highest = number;
713
0
    }
714
715
    // 5. Return highest.
716
0
    return highest;
717
0
}
718
719
// 21.3.2.25 Math.min ( ...args ), https://tc39.es/ecma262/#sec-math.min
720
JS_DEFINE_NATIVE_FUNCTION(MathObject::min)
721
0
{
722
    // 1. Let coerced be a new empty List.
723
0
    Vector<Value> coerced;
724
725
    // 2. For each element arg of args, do
726
0
    for (size_t i = 0; i < vm.argument_count(); ++i) {
727
        // a. Let n be ? ToNumber(arg).
728
0
        auto number = TRY(vm.argument(i).to_number(vm));
729
730
        // b. Append n to coerced.
731
0
        coerced.append(number);
732
0
    }
733
734
    // 3. Let lowest be +∞𝔽.
735
0
    auto lowest = js_infinity();
736
737
    // 4. For each element number of coerced, do
738
0
    for (auto& number : coerced) {
739
        // a. If number is NaN, return NaN.
740
0
        if (number.is_nan())
741
0
            return js_nan();
742
743
        // b. If number is -0𝔽 and lowest is +0𝔽, set lowest to -0𝔽.
744
        // c. If number < lowest, set lowest to number.
745
0
        if ((number.is_negative_zero() && lowest.is_positive_zero()) || number.as_double() < lowest.as_double())
746
0
            lowest = number;
747
0
    }
748
749
    // 5. Return lowest.
750
0
    return lowest;
751
0
}
752
753
// 21.3.2.26 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
754
ThrowCompletionOr<Value> MathObject::pow_impl(VM& vm, Value base, Value exponent)
755
0
{
756
    // Set base to ? ToNumber(base).
757
0
    base = TRY(base.to_number(vm));
758
759
    // 2. Set exponent to ? ToNumber(exponent).
760
0
    exponent = TRY(exponent.to_number(vm));
761
762
    // 3. Return Number::exponentiate(base, exponent).
763
0
    return JS::exp(vm, base, exponent);
764
0
}
765
766
// 21.3.2.26 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
767
JS_DEFINE_NATIVE_FUNCTION(MathObject::pow)
768
0
{
769
0
    return pow_impl(vm, vm.argument(0), vm.argument(1));
770
0
}
771
772
// 21.3.2.27 Math.random ( ), https://tc39.es/ecma262/#sec-math.random
773
JS_DEFINE_NATIVE_FUNCTION(MathObject::random)
774
0
{
775
    // This function returns a Number value with positive sign, greater than or equal to +0𝔽 but strictly less than 1𝔽,
776
    // chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an
777
    // implementation-defined algorithm or strategy.
778
0
    double r = (double)get_random<u32>() / (double)UINT32_MAX;
779
0
    return Value(r);
780
0
}
781
782
// 21.3.2.28 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
783
ThrowCompletionOr<Value> MathObject::round_impl(VM& vm, Value x)
784
0
{
785
    // 1. Let n be ? ToNumber(x).
786
0
    auto number = TRY(x.to_number(vm));
787
788
    // 2. If n is not finite or n is an integral Number, return n.
789
0
    if (!number.is_finite_number() || number.as_double() == ::trunc(number.as_double()))
790
0
        return number;
791
792
    // 3. If n < 0.5𝔽 and n > +0𝔽, return +0𝔽.
793
    // 4. If n < -0𝔽 and n ≥ -0.5𝔽, return -0𝔽.
794
    // 5. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.
795
0
    double integer = ::ceil(number.as_double());
796
0
    if (integer - 0.5 > number.as_double())
797
0
        integer--;
798
0
    return Value(integer);
799
0
}
800
801
// 21.3.2.28 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
802
JS_DEFINE_NATIVE_FUNCTION(MathObject::round)
803
0
{
804
0
    return round_impl(vm, vm.argument(0));
805
0
}
806
807
// 21.3.2.29 Math.sign ( x ), https://tc39.es/ecma262/#sec-math.sign
808
JS_DEFINE_NATIVE_FUNCTION(MathObject::sign)
809
0
{
810
    // 1. Let n be ? ToNumber(x).
811
0
    auto number = TRY(vm.argument(0).to_number(vm));
812
813
    // 2. If n is one of NaN, +0𝔽, or -0𝔽, return n.
814
0
    if (number.is_nan() || number.as_double() == 0)
815
0
        return number;
816
817
    // 3. If n < -0𝔽, return -1𝔽.
818
0
    if (number.as_double() < 0)
819
0
        return Value(-1);
820
821
    // 4. Return 1𝔽.
822
0
    return Value(1);
823
0
}
824
825
// 21.3.2.30 Math.sin ( x ), https://tc39.es/ecma262/#sec-math.sin
826
JS_DEFINE_NATIVE_FUNCTION(MathObject::sin)
827
0
{
828
    // 1. Let n be ? ToNumber(x).
829
0
    auto number = TRY(vm.argument(0).to_number(vm));
830
831
    // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
832
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
833
0
        return number;
834
835
    // 3. If n is +∞𝔽 or n is -∞𝔽, return NaN.
836
0
    if (number.is_infinity())
837
0
        return js_nan();
838
839
    // 4. Return an implementation-approximated Number value representing the result of the sine of ℝ(n).
840
0
    return Value(::sin(number.as_double()));
841
0
}
842
843
// 21.3.2.31 Math.sinh ( x ), https://tc39.es/ecma262/#sec-math.sinh
844
JS_DEFINE_NATIVE_FUNCTION(MathObject::sinh)
845
0
{
846
    // 1. Let n be ? ToNumber(x).
847
0
    auto number = TRY(vm.argument(0).to_number(vm));
848
849
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
850
0
    if (!number.is_finite_number() || number.is_positive_zero() || number.is_negative_zero())
851
0
        return number;
852
853
    // 3. Return an implementation-approximated Number value representing the result of the hyperbolic sine of ℝ(n).
854
0
    return Value(::sinh(number.as_double()));
855
0
}
856
857
// 21.3.2.32 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
858
ThrowCompletionOr<Value> MathObject::sqrt_impl(VM& vm, Value x)
859
0
{
860
    // Let n be ? ToNumber(x).
861
0
    auto number = TRY(x.to_number(vm));
862
863
    // 2. If n is one of NaN, +0𝔽, -0𝔽, or +∞𝔽, return n.
864
0
    if (number.is_nan() || number.as_double() == 0 || number.is_positive_infinity())
865
0
        return number;
866
867
    // 3. If n < -0𝔽, return NaN.
868
0
    if (number.as_double() < 0)
869
0
        return js_nan();
870
871
    // 4. Return an implementation-approximated Number value representing the result of the square root of ℝ(n).
872
0
    return Value(::sqrt(number.as_double()));
873
0
}
874
875
// 21.3.2.32 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
876
JS_DEFINE_NATIVE_FUNCTION(MathObject::sqrt)
877
0
{
878
0
    return sqrt_impl(vm, vm.argument(0));
879
0
}
880
881
// 21.3.2.33 Math.tan ( x ), https://tc39.es/ecma262/#sec-math.tan
882
JS_DEFINE_NATIVE_FUNCTION(MathObject::tan)
883
0
{
884
    // Let n be ? ToNumber(x).
885
0
    auto number = TRY(vm.argument(0).to_number(vm));
886
887
    // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
888
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
889
0
        return number;
890
891
    // 3. If n is +∞𝔽, or n is -∞𝔽, return NaN.
892
0
    if (number.is_infinity())
893
0
        return js_nan();
894
895
    // 4. Return an implementation-approximated Number value representing the result of the tangent of ℝ(n).
896
0
    return Value(::tan(number.as_double()));
897
0
}
898
899
// 21.3.2.34 Math.tanh ( x ), https://tc39.es/ecma262/#sec-math.tanh
900
JS_DEFINE_NATIVE_FUNCTION(MathObject::tanh)
901
0
{
902
    // 1. Let n be ? ToNumber(x).
903
0
    auto number = TRY(vm.argument(0).to_number(vm));
904
905
    // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
906
0
    if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
907
0
        return number;
908
909
    // 3. If n is +∞𝔽, return 1𝔽.
910
0
    if (number.is_positive_infinity())
911
0
        return Value(1);
912
913
    // 4. If n is -∞𝔽, return -1𝔽.
914
0
    if (number.is_negative_infinity())
915
0
        return Value(-1);
916
917
    // 5. Return an implementation-approximated Number value representing the result of the hyperbolic tangent of ℝ(n).
918
0
    return Value(::tanh(number.as_double()));
919
0
}
920
921
// 21.3.2.35 Math.trunc ( x ), https://tc39.es/ecma262/#sec-math.trunc
922
JS_DEFINE_NATIVE_FUNCTION(MathObject::trunc)
923
0
{
924
    // 1. Let n be ? ToNumber(x).
925
0
    auto number = TRY(vm.argument(0).to_number(vm));
926
927
    // 2. If n is not finite or n is either +0𝔽 or -0𝔽, return n.
928
0
    if (number.is_nan() || number.is_infinity() || number.as_double() == 0)
929
0
        return number;
930
931
    // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
932
    // 4. If n < -0𝔽 and n > -1𝔽, return -0𝔽.
933
    // 5. Return the integral Number nearest n in the direction of +0𝔽.
934
0
    return Value(number.as_double() < 0
935
0
            ? ::ceil(number.as_double())
936
0
            : ::floor(number.as_double()));
937
0
}
938
939
}