2020-03-21 17:52:12 +01:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2020 - 2023 , Andreas Kling < andreas @ ladybird . org >
2023-04-13 00:47:15 +02:00
* Copyright ( c ) 2020 - 2023 , Linus Groh < linusg @ serenityos . org >
2021-06-05 03:40:28 +03:00
* Copyright ( c ) 2021 , Idan Horowitz < idan . horowitz @ serenityos . org >
2023-07-15 22:33:22 +12:00
* Copyright ( c ) 2023 , Shannon Booth < shannon @ serenityos . org >
2020-03-21 17:52:12 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-03-21 17:52:12 +01:00
*/
2021-12-19 15:46:55 -06:00
# include <AK/BuiltinWrappers.h>
2020-03-23 13:14:04 +01:00
# include <AK/Function.h>
2021-05-14 17:27:38 +02:00
# include <AK/Random.h>
2025-04-11 22:12:04 +02:00
# include <LibCrypto/SecureRandom.h>
2025-03-02 20:04:38 +01:00
# include <LibJS/Runtime/AbstractOperations.h>
2020-04-18 13:18:06 +02:00
# include <LibJS/Runtime/GlobalObject.h>
2025-03-02 20:04:38 +01:00
# include <LibJS/Runtime/Iterator.h>
2020-03-21 17:52:12 +01:00
# include <LibJS/Runtime/MathObject.h>
2023-10-06 17:54:21 +02:00
# include <LibJS/Runtime/ValueInlines.h>
2020-04-04 22:44:48 +02:00
# include <math.h>
2020-03-21 17:52:12 +01:00
namespace JS {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( MathObject ) ;
2023-11-19 09:45:05 +01:00
2022-08-16 00:20:49 +01:00
MathObject : : MathObject ( Realm & realm )
2023-04-13 00:47:15 +02:00
: Object ( ConstructWithPrototypeTag : : Tag , realm . intrinsics ( ) . object_prototype ( ) )
2020-06-20 17:11:11 +02:00
{
}
2023-08-07 08:41:28 +02:00
void MathObject : : initialize ( Realm & realm )
2020-03-21 17:52:12 +01:00
{
2020-10-13 23:49:19 +02:00
auto & vm = this - > vm ( ) ;
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2025-04-28 19:24:01 -04:00
2020-04-27 23:05:02 -07:00
u8 attr = Attribute : : Writable | Attribute : : Configurable ;
2023-11-17 11:56:40 +01:00
define_native_function ( realm , vm . names . abs , abs , 1 , attr , Bytecode : : Builtin : : MathAbs ) ;
2025-04-03 12:01:31 +02:00
define_native_function ( realm , vm . names . random , random , 0 , attr , Bytecode : : Builtin : : MathRandom ) ;
2023-11-24 09:35:37 +01:00
define_native_function ( realm , vm . names . sqrt , sqrt , 1 , attr , Bytecode : : Builtin : : MathSqrt ) ;
2023-11-24 09:44:00 +01:00
define_native_function ( realm , vm . names . floor , floor , 1 , attr , Bytecode : : Builtin : : MathFloor ) ;
2023-11-24 09:46:21 +01:00
define_native_function ( realm , vm . names . ceil , ceil , 1 , attr , Bytecode : : Builtin : : MathCeil ) ;
2023-11-24 09:50:11 +01:00
define_native_function ( realm , vm . names . round , round , 1 , attr , Bytecode : : Builtin : : MathRound ) ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . max , max , 2 , attr ) ;
define_native_function ( realm , vm . names . min , min , 2 , attr ) ;
define_native_function ( realm , vm . names . trunc , trunc , 1 , attr ) ;
2025-05-26 13:54:46 +03:00
define_native_function ( realm , vm . names . sin , sin , 1 , attr , Bytecode : : Builtin : : MathSin ) ;
2025-05-26 14:03:37 +03:00
define_native_function ( realm , vm . names . cos , cos , 1 , attr , Bytecode : : Builtin : : MathCos ) ;
2025-05-26 14:07:59 +03:00
define_native_function ( realm , vm . names . tan , tan , 1 , attr , Bytecode : : Builtin : : MathTan ) ;
2023-11-24 09:38:28 +01:00
define_native_function ( realm , vm . names . pow , pow , 2 , attr , Bytecode : : Builtin : : MathPow ) ;
2023-11-24 09:53:52 +01:00
define_native_function ( realm , vm . names . exp , exp , 1 , attr , Bytecode : : Builtin : : MathExp ) ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . expm1 , expm1 , 1 , attr ) ;
define_native_function ( realm , vm . names . sign , sign , 1 , attr ) ;
define_native_function ( realm , vm . names . clz32 , clz32 , 1 , attr ) ;
define_native_function ( realm , vm . names . acos , acos , 1 , attr ) ;
define_native_function ( realm , vm . names . acosh , acosh , 1 , attr ) ;
define_native_function ( realm , vm . names . asin , asin , 1 , attr ) ;
define_native_function ( realm , vm . names . asinh , asinh , 1 , attr ) ;
define_native_function ( realm , vm . names . atan , atan , 1 , attr ) ;
define_native_function ( realm , vm . names . atanh , atanh , 1 , attr ) ;
define_native_function ( realm , vm . names . log1p , log1p , 1 , attr ) ;
define_native_function ( realm , vm . names . cbrt , cbrt , 1 , attr ) ;
define_native_function ( realm , vm . names . atan2 , atan2 , 2 , attr ) ;
define_native_function ( realm , vm . names . fround , fround , 1 , attr ) ;
2024-11-09 15:29:03 -06:00
define_native_function ( realm , vm . names . f16round , f16round , 1 , attr ) ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . hypot , hypot , 2 , attr ) ;
2025-04-03 11:58:30 +02:00
define_native_function ( realm , vm . names . imul , imul , 2 , attr , Bytecode : : Builtin : : MathImul ) ;
2023-11-24 09:31:23 +01:00
define_native_function ( realm , vm . names . log , log , 1 , attr , Bytecode : : Builtin : : MathLog ) ;
2022-08-22 21:47:35 +01:00
define_native_function ( realm , vm . names . log2 , log2 , 1 , attr ) ;
define_native_function ( realm , vm . names . log10 , log10 , 1 , attr ) ;
define_native_function ( realm , vm . names . sinh , sinh , 1 , attr ) ;
define_native_function ( realm , vm . names . cosh , cosh , 1 , attr ) ;
define_native_function ( realm , vm . names . tanh , tanh , 1 , attr ) ;
2025-03-02 20:04:38 +01:00
define_native_function ( realm , vm . names . sumPrecise , sumPrecise , 1 , attr ) ;
2020-03-29 16:09:03 +01:00
2021-06-13 00:22:35 +01:00
// 21.3.1 Value Properties of the Math Object, https://tc39.es/ecma262/#sec-value-properties-of-the-math-object
2021-07-06 02:15:08 +03:00
define_direct_property ( vm . names . E , Value ( M_E ) , 0 ) ;
define_direct_property ( vm . names . LN2 , Value ( M_LN2 ) , 0 ) ;
define_direct_property ( vm . names . LN10 , Value ( M_LN10 ) , 0 ) ;
define_direct_property ( vm . names . LOG2E , Value ( : : log2 ( M_E ) ) , 0 ) ;
define_direct_property ( vm . names . LOG10E , Value ( : : log10 ( M_E ) ) , 0 ) ;
define_direct_property ( vm . names . PI , Value ( M_PI ) , 0 ) ;
define_direct_property ( vm . names . SQRT1_2 , Value ( M_SQRT1_2 ) , 0 ) ;
define_direct_property ( vm . names . SQRT2 , Value ( M_SQRT2 ) , 0 ) ;
2020-07-11 10:27:00 -07:00
2021-06-13 00:22:35 +01:00
// 21.3.1.9 Math [ @@toStringTag ], https://tc39.es/ecma262/#sec-math-@@tostringtag
2023-04-13 01:14:45 +02:00
define_direct_property ( vm . well_known_symbol_to_string_tag ( ) , PrimitiveString : : create ( vm , vm . names . Math . as_string ( ) ) , Attribute : : Configurable ) ;
2020-03-21 17:52:12 +01:00
}
2021-06-13 00:22:35 +01:00
// 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
2023-11-30 19:49:29 +01:00
ThrowCompletionOr < Value > MathObject : : abs_impl ( VM & vm , Value x )
2020-04-05 21:21:33 +01:00
{
2023-10-04 15:31:48 +02:00
// OPTIMIZATION: Fast path for Int32 values.
if ( x . is_int32 ( ) )
return Value ( AK : : abs ( x . as_i32 ( ) ) ) ;
2022-08-10 11:57:46 +02:00
// Let n be ? ToNumber(x).
2023-10-04 15:31:48 +02:00
auto number = TRY ( x . to_number ( vm ) ) ;
2022-08-10 11:57:46 +02:00
2023-04-14 17:04:00 +02:00
// 2. If n is NaN, return NaN.
2020-05-18 15:21:37 +01:00
if ( number . is_nan ( ) )
return js_nan ( ) ;
2023-04-14 17:04:00 +02:00
// 3. If n is -0𝔽 , return +0𝔽 .
2020-05-18 14:35:41 +01:00
if ( number . is_negative_zero ( ) )
2023-04-14 17:04:00 +02:00
return Value ( 0 ) ;
2020-05-18 14:35:41 +01:00
2023-04-14 17:04:00 +02:00
// 4. If n is -∞𝔽, return +∞𝔽.
if ( number . is_negative_infinity ( ) )
return js_infinity ( ) ;
// 5. If n < -0𝔽 , return -n.
// 6. Return n.
return Value ( number . as_double ( ) < 0 ? - number . as_double ( ) : number . as_double ( ) ) ;
2020-05-18 16:04:38 +01:00
}
2023-11-30 19:49:29 +01:00
// 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : abs )
{
return abs_impl ( vm , vm . argument ( 0 ) ) ;
}
2021-06-13 00:22:35 +01:00
// 21.3.2.2 Math.acos ( x ), https://tc39.es/ecma262/#sec-math.acos
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : acos )
2020-12-08 16:22:07 +01:00
{
2023-04-14 17:04:00 +02:00
// 1. Let n be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is NaN, n > 1𝔽 , or n < -1𝔽 , return NaN.
2020-12-08 16:22:07 +01:00
if ( number . is_nan ( ) | | number . as_double ( ) > 1 | | number . as_double ( ) < - 1 )
return js_nan ( ) ;
2023-04-14 17:04:00 +02:00
// 3. If n is 1𝔽 , return +0𝔽 .
2020-12-08 16:22:07 +01:00
if ( number . as_double ( ) = = 1 )
return Value ( 0 ) ;
2023-04-14 17:04:00 +02:00
// 4. Return an implementation-approximated Number value representing the result of the inverse cosine of ℝ (n).
2020-12-08 16:22:07 +01:00
return Value ( : : acos ( number . as_double ( ) ) ) ;
}
2021-06-13 00:22:35 +01:00
// 21.3.2.3 Math.acosh ( x ), https://tc39.es/ecma262/#sec-math.acosh
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : acosh )
2020-06-21 11:34:00 +02:00
{
2022-11-28 11:58:21 +01:00
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN or n is +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is 1𝔽 , return +0𝔽 .
if ( number . as_double ( ) = = 1.0 )
return Value ( 0.0 ) ;
// 4. If n < 1𝔽 , return NaN.
if ( number . as_double ( ) < 1 )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-11-28 11:58:21 +01:00
// 5. Return an implementation-approximated Number value representing the result of the inverse hyperbolic cosine of ℝ (n).
return Value ( : : acosh ( number . as_double ( ) ) ) ;
2020-06-21 11:34:00 +02:00
}
2021-06-13 00:22:35 +01:00
// 21.3.2.4 Math.asin ( x ), https://tc39.es/ecma262/#sec-math.asin
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : asin )
2020-12-08 16:22:07 +01:00
{
2022-11-28 12:00:09 +01:00
// 1. Let n be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2022-11-28 12:00:09 +01:00
// 2. If n is NaN, n is +0𝔽 , or n is -0𝔽 , return n.
2020-12-08 16:22:07 +01:00
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
2022-11-28 12:00:09 +01:00
// 3. If n > 1𝔽 or n < -1𝔽 , return NaN.
if ( number . as_double ( ) > 1 | | number . as_double ( ) < - 1 )
return js_nan ( ) ;
// 4. Return an implementation-approximated Number value representing the result of the inverse sine of ℝ (n).
2020-12-08 16:22:07 +01:00
return Value ( : : asin ( number . as_double ( ) ) ) ;
}
2021-06-13 00:22:35 +01:00
// 21.3.2.5 Math.asinh ( x ), https://tc39.es/ecma262/#sec-math.asinh
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : asinh )
2020-06-21 11:34:00 +02:00
{
2022-11-28 12:01:29 +01:00
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( ! number . is_finite_number ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. Return an implementation-approximated Number value representing the result of the inverse hyperbolic sine of ℝ (n).
return Value ( : : asinh ( number . as_double ( ) ) ) ;
2020-06-21 11:34:00 +02:00
}
2021-06-13 00:22:35 +01:00
// 21.3.2.6 Math.atan ( x ), https://tc39.es/ecma262/#sec-math.atan
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : atan )
2020-12-08 09:52:33 +01:00
{
2023-04-14 17:04:00 +02:00
// Let n be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is one of NaN, +0𝔽 , or -0𝔽 , return n.
if ( number . is_nan ( ) | | number . as_double ( ) = = 0 )
2020-12-08 09:52:33 +01:00
return number ;
2023-04-14 17:04:00 +02:00
// 3. If n is +∞𝔽, return an implementation-approximated Number value representing π / 2.
2020-12-08 09:52:33 +01:00
if ( number . is_positive_infinity ( ) )
return Value ( M_PI_2 ) ;
2023-04-14 17:04:00 +02:00
// 4. If n is -∞𝔽, return an implementation-approximated Number value representing -π / 2.
2020-12-08 09:52:33 +01:00
if ( number . is_negative_infinity ( ) )
return Value ( - M_PI_2 ) ;
2023-04-14 17:04:00 +02:00
// 5. Return an implementation-approximated Number value representing the result of the inverse tangent of ℝ (n).
2020-12-08 09:52:33 +01:00
return Value ( : : atan ( number . as_double ( ) ) ) ;
}
2021-06-13 00:22:35 +01:00
// 21.3.2.7 Math.atanh ( x ), https://tc39.es/ecma262/#sec-math.atanh
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : atanh )
2020-06-21 11:34:00 +02:00
{
2022-11-28 12:02:45 +01:00
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, n is +0𝔽 , or n is -0𝔽 , return n.
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. If n > 1𝔽 or n < -1𝔽 , return NaN.
if ( number . as_double ( ) > 1. | | number . as_double ( ) < - 1. )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-11-28 12:02:45 +01:00
// 4. If n is 1𝔽 , return +∞𝔽.
if ( number . as_double ( ) = = 1. )
return js_infinity ( ) ;
// 5. If n is -1𝔽 , return -∞𝔽.
if ( number . as_double ( ) = = - 1. )
return js_negative_infinity ( ) ;
// 6. Return an implementation-approximated Number value representing the result of the inverse hyperbolic tangent of ℝ (n).
return Value ( : : atanh ( number . as_double ( ) ) ) ;
2020-06-21 11:34:00 +02:00
}
2021-06-13 00:22:35 +01:00
// 21.3.2.8 Math.atan2 ( y, x ), https://tc39.es/ecma262/#sec-math.atan2
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : atan2 )
2020-12-28 17:34:51 +03:00
{
2021-06-05 03:40:28 +03:00
auto constexpr three_quarters_pi = M_PI_4 + M_PI_2 ;
2023-05-28 23:32:33 +12:00
// 1. Let ny be ? ToNumber(y).
2022-08-21 14:00:56 +01:00
auto y = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2023-05-28 23:32:33 +12:00
// 2. Let nx be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto x = TRY ( vm . argument ( 1 ) . to_number ( vm ) ) ;
2021-06-05 03:40:28 +03:00
2023-05-28 23:32:33 +12:00
// 3. If ny is NaN or nx is NaN, return NaN.
2021-06-05 03:40:28 +03:00
if ( y . is_nan ( ) | | x . is_nan ( ) )
return js_nan ( ) ;
2023-05-28 23:32:33 +12:00
// 4. If ny is +∞𝔽, then
2021-06-05 03:40:28 +03:00
if ( y . is_positive_infinity ( ) ) {
2023-05-28 23:32:33 +12:00
// a. If nx is +∞𝔽, return an implementation-approximated Number value representing π / 4.
2021-06-05 03:40:28 +03:00
if ( x . is_positive_infinity ( ) )
return Value ( M_PI_4 ) ;
2023-05-28 23:32:33 +12:00
// b. If nx is -∞𝔽, return an implementation-approximated Number value representing 3π / 4.
if ( x . is_negative_infinity ( ) )
2021-06-05 03:40:28 +03:00
return Value ( three_quarters_pi ) ;
2023-05-28 23:32:33 +12:00
// c. Return an implementation-approximated Number value representing π / 2.
return Value ( M_PI_2 ) ;
2020-12-28 17:34:51 +03:00
}
2023-05-28 23:32:33 +12:00
// 5. If ny is -∞𝔽, then
2021-06-05 03:40:28 +03:00
if ( y . is_negative_infinity ( ) ) {
2023-05-28 23:32:33 +12:00
// a. If nx is +∞𝔽, return an implementation-approximated Number value representing -π / 4.
2021-06-05 03:40:28 +03:00
if ( x . is_positive_infinity ( ) )
return Value ( - M_PI_4 ) ;
2023-05-28 23:32:33 +12:00
// b. If nx is -∞𝔽, return an implementation-approximated Number value representing -3π / 4.
if ( x . is_negative_infinity ( ) )
2021-06-05 03:40:28 +03:00
return Value ( - three_quarters_pi ) ;
2023-05-28 23:32:33 +12:00
// c. Return an implementation-approximated Number value representing -π / 2.
return Value ( - M_PI_2 ) ;
2020-12-28 17:34:51 +03:00
}
2023-05-28 23:32:33 +12:00
// 6. If ny is +0𝔽 , then
2021-06-05 03:40:28 +03:00
if ( y . is_positive_zero ( ) ) {
2023-05-28 23:32:33 +12:00
// a. If nx > +0𝔽 or nx is +0𝔽 , return +0𝔽 .
2021-06-05 03:40:28 +03:00
if ( x . as_double ( ) > 0 | | x . is_positive_zero ( ) )
return Value ( 0.0 ) ;
2023-05-28 23:32:33 +12:00
// b. Return an implementation-approximated Number value representing π.
return Value ( M_PI ) ;
2020-12-28 17:34:51 +03:00
}
2023-05-28 23:32:33 +12:00
// 7. If ny is -0𝔽 , then
2021-06-05 03:40:28 +03:00
if ( y . is_negative_zero ( ) ) {
2023-05-28 23:32:33 +12:00
// a. If nx > +0𝔽 or nx is +0𝔽 , return -0𝔽
2021-06-05 03:40:28 +03:00
if ( x . as_double ( ) > 0 | | x . is_positive_zero ( ) )
return Value ( - 0.0 ) ;
2023-05-28 23:32:33 +12:00
// b. Return an implementation-approximated Number value representing -π.
return Value ( - M_PI ) ;
2020-12-28 17:34:51 +03:00
}
2023-05-28 23:32:33 +12:00
// 8. Assert: ny is finite and is neither +0𝔽 nor -0𝔽 .
2021-06-05 03:40:28 +03:00
VERIFY ( y . is_finite_number ( ) & & ! y . is_positive_zero ( ) & & ! y . is_negative_zero ( ) ) ;
2023-05-28 23:32:33 +12:00
// 9. If ny > +0𝔽 , then
2021-06-05 03:40:28 +03:00
if ( y . as_double ( ) > 0 ) {
2023-05-28 23:32:33 +12:00
// a. If nx is +∞𝔽, return +0𝔽 .
2021-06-05 03:40:28 +03:00
if ( x . is_positive_infinity ( ) )
return Value ( 0 ) ;
2023-05-28 23:32:33 +12:00
// b. If nx is -∞𝔽, return an implementation-approximated Number value representing π.
if ( x . is_negative_infinity ( ) )
2021-06-05 03:40:28 +03:00
return Value ( M_PI ) ;
2023-05-28 23:32:33 +12:00
// c. If nx is either +0𝔽 or -0𝔽 , return an implementation-approximated Number value representing π / 2.
if ( x . is_positive_zero ( ) | | x . is_negative_zero ( ) )
2021-06-05 03:40:28 +03:00
return Value ( M_PI_2 ) ;
}
2023-05-28 23:32:33 +12:00
// 10. If ny < -0𝔽 , then
2023-05-27 14:25:43 +12:00
if ( y . as_double ( ) < - 0 ) {
2023-05-28 23:32:33 +12:00
// a. If nx is +∞𝔽, return -0𝔽 .
2021-06-05 03:40:28 +03:00
if ( x . is_positive_infinity ( ) )
return Value ( - 0.0 ) ;
2023-05-28 23:32:33 +12:00
// b. If nx is -∞𝔽, return an implementation-approximated Number value representing -π.
if ( x . is_negative_infinity ( ) )
2021-06-05 03:40:28 +03:00
return Value ( - M_PI ) ;
2023-05-28 23:32:33 +12:00
// c. If nx is either +0𝔽 or -0𝔽 , return an implementation-approximated Number value representing -π / 2.
if ( x . is_positive_zero ( ) | | x . is_negative_zero ( ) )
2021-06-05 03:40:28 +03:00
return Value ( - M_PI_2 ) ;
}
2023-05-28 23:32:33 +12:00
// 11. Assert: nx is finite and is neither +0𝔽 nor -0𝔽 .
2021-06-05 03:40:28 +03:00
VERIFY ( x . is_finite_number ( ) & & ! x . is_positive_zero ( ) & & ! x . is_negative_zero ( ) ) ;
2023-05-28 23:32:33 +12:00
// 12. Return an implementation-approximated Number value representing the result of the inverse tangent of the quotient ℝ (ny) / ℝ (nx).
2020-12-28 17:34:51 +03:00
return Value ( : : atan2 ( y . as_double ( ) , x . as_double ( ) ) ) ;
}
2023-04-14 17:04:00 +02:00
// 21.3.2.9 Math.cbrt ( x ), https://tc39.es/ecma262/#sec-math.cbrt
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : cbrt )
2020-12-28 17:34:51 +03:00
{
2023-04-14 17:04:00 +02:00
// 1. Let n be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2021-06-05 03:20:00 +03:00
2023-04-14 17:04:00 +02:00
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( ! number . is_finite_number ( ) | | number . as_double ( ) = = 0 )
return number ;
2021-06-05 03:20:00 +03:00
2023-04-14 17:04:00 +02:00
// 3. Return an implementation-approximated Number value representing the result of the cube root of ℝ (n).
return Value ( : : cbrt ( number . as_double ( ) ) ) ;
2020-12-28 17:34:51 +03:00
}
2023-04-14 17:04:00 +02:00
// 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
2023-11-24 09:46:21 +01:00
ThrowCompletionOr < Value > MathObject : : ceil_impl ( VM & vm , Value x )
2021-06-05 01:43:10 +03:00
{
2023-04-14 17:04:00 +02:00
// 1. Let n be ? ToNumber(x).
2023-11-24 09:46:21 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( ! number . is_finite_number ( ) | | number . as_double ( ) = = 0 )
return number ;
// 3. If n < -0𝔽 and n > -1𝔽 , return -0𝔽 .
if ( number . as_double ( ) < 0 & & number . as_double ( ) > - 1 )
return Value ( - 0.f ) ;
// 4. If n is an integral Number, return n.
// 5. Return the smallest (closest to -∞) integral Number value that is not less than n.
return Value ( : : ceil ( number . as_double ( ) ) ) ;
}
2023-11-24 09:46:21 +01:00
// 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : ceil )
{
return ceil_impl ( vm , vm . argument ( 0 ) ) ;
}
2023-04-14 17:04:00 +02:00
// 21.3.2.11 Math.clz32 ( x ), https://tc39.es/ecma262/#sec-math.clz32
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : clz32 )
{
// 1. Let n be ? ToUint32(x).
auto number = TRY ( vm . argument ( 0 ) . to_u32 ( vm ) ) ;
// 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
// 3. Return 𝔽 (p).
return Value ( count_leading_zeroes_safe ( number ) ) ;
}
// 21.3.2.12 Math.cos ( x ), https://tc39.es/ecma262/#sec-math.cos
2025-05-26 14:03:37 +03:00
ThrowCompletionOr < Value > MathObject : : cos_impl ( VM & vm , Value value )
2023-04-14 17:04:00 +02:00
{
// 1. Let n be ? ToNumber(x).
2025-05-26 14:03:37 +03:00
auto number = TRY ( value . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is NaN, n is +∞𝔽, or n is -∞𝔽, return NaN.
if ( number . is_nan ( ) | | number . is_infinity ( ) )
return js_nan ( ) ;
// 3. If n is +0𝔽 or n is -0𝔽 , return 1𝔽 .
if ( number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return Value ( 1 ) ;
// 4. Return an implementation-approximated Number value representing the result of the cosine of ℝ (n).
return Value ( : : cos ( number . as_double ( ) ) ) ;
}
2025-05-26 14:03:37 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : cos )
{
return cos_impl ( vm , vm . argument ( 0 ) ) ;
}
2023-04-14 17:04:00 +02:00
// 21.3.2.13 Math.cosh ( x ), https://tc39.es/ecma262/#sec-math.cosh
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : cosh )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, return NaN.
if ( number . is_nan ( ) )
return js_nan ( ) ;
// 3. If n is +∞𝔽 or n is -∞𝔽, return +∞𝔽.
if ( number . is_positive_infinity ( ) | | number . is_negative_infinity ( ) )
return js_infinity ( ) ;
// 4. If n is +0𝔽 or n is -0𝔽 , return 1𝔽 .
if ( number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return Value ( 1 ) ;
// 5. Return an implementation-approximated Number value representing the result of the hyperbolic cosine of ℝ (n).
return Value ( : : cosh ( number . as_double ( ) ) ) ;
}
// 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
2023-11-24 09:53:52 +01:00
ThrowCompletionOr < Value > MathObject : : exp_impl ( VM & vm , Value x )
2023-04-14 17:04:00 +02:00
{
// 1. Let n be ? ToNumber(x).
2023-11-24 09:53:52 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is either NaN or +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is either +0𝔽 or -0𝔽 , return 1𝔽 .
if ( number . as_double ( ) = = 0 )
return Value ( 1 ) ;
// 4. If n is -∞𝔽, return +0𝔽 .
if ( number . is_negative_infinity ( ) )
return Value ( 0 ) ;
// 5. Return an implementation-approximated Number value representing the result of the exponential function of ℝ (n).
return Value ( : : exp ( number . as_double ( ) ) ) ;
}
2023-11-24 09:53:52 +01:00
// 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : exp )
{
return exp_impl ( vm , vm . argument ( 0 ) ) ;
}
2023-04-14 17:04:00 +02:00
// 21.3.2.15 Math.expm1 ( x ), https://tc39.es/ecma262/#sec-math.expm1
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : expm1 )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is one of NaN, +0𝔽 , -0𝔽 , or +∞𝔽, return n.
if ( number . is_nan ( ) | | number . as_double ( ) = = 0 | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is -∞𝔽, return -1𝔽 .
if ( number . is_negative_infinity ( ) )
return Value ( - 1 ) ;
// 4. Return an implementation-approximated Number value representing the result of subtracting 1 from the exponential function of ℝ (n).
return Value ( : : expm1 ( number . as_double ( ) ) ) ;
}
// 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
2023-11-24 09:44:00 +01:00
ThrowCompletionOr < Value > MathObject : : floor_impl ( VM & vm , Value x )
2023-04-14 17:04:00 +02:00
{
// 1. Let n be ? ToNumber(x).
2023-11-24 09:44:00 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( ! number . is_finite_number ( ) | | number . as_double ( ) = = 0 )
return number ;
// 3. If n < 1𝔽 and n > +0𝔽 , return +0𝔽 .
// 4. If n is an integral Number, return n.
// 5. Return the greatest (closest to +∞) integral Number value that is not greater than n.
return Value ( : : floor ( number . as_double ( ) ) ) ;
}
2023-11-24 09:44:00 +01:00
// 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : floor )
{
return floor_impl ( vm , vm . argument ( 0 ) ) ;
}
2023-04-14 17:04:00 +02:00
// 21.3.2.17 Math.fround ( x ), https://tc39.es/ecma262/#sec-math.fround
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : fround )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, return NaN.
if ( number . is_nan ( ) )
return js_nan ( ) ;
// 3. If n is one of +0𝔽 , -0𝔽 , +∞𝔽, or -∞𝔽, return n.
if ( number . as_double ( ) = = 0 | | number . is_infinity ( ) )
return number ;
// 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
// 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
// 6. Return the ECMAScript Number value corresponding to n64.
return Value ( ( float ) number . as_double ( ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.18 Math.f16round ( x ), https://tc39.es/ecma262/#sec-math.f16round
2024-11-09 15:29:03 -06:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : f16round )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, return NaN.
if ( number . is_nan ( ) )
return js_nan ( ) ;
// 3. If n is one of +0𝔽 , -0𝔽 , +∞𝔽, or -∞𝔽, return n.
if ( number . as_double ( ) = = 0 | | number . is_infinity ( ) )
return number ;
// 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.
// 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.
// 6. Return the ECMAScript Number value corresponding to n64.
return Value ( static_cast < f16 > ( number . as_double ( ) ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.19 Math.hypot ( ...args ), https://tc39.es/ecma262/#sec-math.hypot
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : hypot )
{
// 1. Let coerced be a new empty List.
Vector < Value > coerced ;
// 2. For each element arg of args, do
for ( size_t i = 0 ; i < vm . argument_count ( ) ; + + i ) {
// a. Let n be ? ToNumber(arg).
auto number = TRY ( vm . argument ( i ) . to_number ( vm ) ) ;
// b. Append n to coerced.
coerced . append ( number ) ;
}
// 3. For each element number of coerced, do
for ( auto & number : coerced ) {
// a. If number is either +∞𝔽 or -∞𝔽, return +∞𝔽.
if ( number . is_infinity ( ) )
return js_infinity ( ) ;
}
// 4. Let onlyZero be true.
auto only_zero = true ;
double sum_of_squares = 0 ;
// 5. For each element number of coerced, do
for ( auto & number : coerced ) {
// a. If number is NaN, return NaN.
// OPTIMIZATION: For infinities, the result will be infinity with the same sign, so we can return early.
if ( number . is_nan ( ) | | number . is_infinity ( ) )
return number ;
// b. If number is neither +0𝔽 nor -0𝔽 , set onlyZero to false.
if ( number . as_double ( ) ! = 0 )
only_zero = false ;
sum_of_squares + = number . as_double ( ) * number . as_double ( ) ;
}
// 6. If onlyZero is true, return +0𝔽 .
if ( only_zero )
return Value ( 0 ) ;
// 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.
return Value ( : : sqrt ( sum_of_squares ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.20 Math.imul ( x, y ), https://tc39.es/ecma262/#sec-math.imul
2025-04-03 11:58:30 +02:00
ThrowCompletionOr < Value > MathObject : : imul_impl ( VM & vm , Value arg_a , Value arg_b )
2023-04-14 17:04:00 +02:00
{
// 1. Let a be ℝ (? ToUint32(x)).
2025-04-03 11:58:30 +02:00
auto const a = TRY ( arg_a . to_u32 ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. Let b be ℝ (? ToUint32(y)).
2025-04-03 11:58:30 +02:00
auto const b = TRY ( arg_b . to_u32 ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 3. Let product be (a × b) modulo 2^32.
// 4. If product ≥ 2^31, return 𝔽 (product - 2^32); otherwise return 𝔽 (product).
2021-06-05 01:43:10 +03:00
return Value ( static_cast < i32 > ( a * b ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.20 Math.imul ( x, y ), https://tc39.es/ecma262/#sec-math.imul
2025-04-03 11:58:30 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : imul )
{
return imul_impl ( vm , vm . argument ( 0 ) , vm . argument ( 1 ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.21 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
2023-11-24 09:31:23 +01:00
ThrowCompletionOr < Value > MathObject : : log_impl ( VM & vm , Value x )
2020-12-28 17:34:51 +03:00
{
2022-11-28 12:05:01 +01:00
// 1. Let n be ? ToNumber(x).
2023-11-24 09:31:23 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2022-11-28 12:05:01 +01:00
// 2. If n is NaN or n is +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is 1𝔽 , return +0𝔽 .
if ( number . as_double ( ) = = 1. )
return Value ( 0 ) ;
// 4. If n is +0𝔽 or n is -0𝔽 , return -∞𝔽.
if ( number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return js_negative_infinity ( ) ;
// 5. If n < -0𝔽 , return NaN.
if ( number . as_double ( ) < - 0. )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-11-28 12:05:01 +01:00
// 6. Return an implementation-approximated Number value representing the result of the natural logarithm of ℝ (n).
return Value ( : : log ( number . as_double ( ) ) ) ;
2020-12-28 17:34:51 +03:00
}
2025-04-28 19:24:01 -04:00
// 21.3.2.21 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
2023-11-24 09:31:23 +01:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : log )
{
return log_impl ( vm , vm . argument ( 0 ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.22 Math.log1p ( x ), https://tc39.es/ecma262/#sec-math.log1p
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : log1p )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, n is +0𝔽 , n is -0𝔽 , or n is +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is -1𝔽 , return -∞𝔽.
if ( number . as_double ( ) = = - 1. )
return js_negative_infinity ( ) ;
// 4. If n < -1𝔽 , return NaN.
if ( number . as_double ( ) < - 1. )
return js_nan ( ) ;
// 5. Return an implementation-approximated Number value representing the result of the natural logarithm of 1 + ℝ (n).
return Value ( : : log1p ( number . as_double ( ) ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.23 Math.log10 ( x ), https://tc39.es/ecma262/#sec-math.log10
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : log10 )
2020-12-28 17:34:51 +03:00
{
2022-11-28 12:05:58 +01:00
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN or n is +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is 1𝔽 , return +0𝔽 .
if ( number . as_double ( ) = = 1. )
return Value ( 0 ) ;
// 4. If n is +0𝔽 or n is -0𝔽 , return -∞𝔽.
if ( number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return js_negative_infinity ( ) ;
// 5. If n < -0𝔽 , return NaN.
if ( number . as_double ( ) < - 0. )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-11-28 12:05:58 +01:00
2023-04-14 17:04:00 +02:00
// 6. Return an implementation-approximated Number value representing the result of the base 10 logarithm of ℝ (n).
return Value ( : : log10 ( number . as_double ( ) ) ) ;
2020-12-28 17:34:51 +03:00
}
2025-04-28 19:24:01 -04:00
// 21.3.2.24 Math.log2 ( x ), https://tc39.es/ecma262/#sec-math.log2
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : log2 )
2020-12-28 17:34:51 +03:00
{
2022-11-28 12:06:50 +01:00
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN or n is +∞𝔽, return n.
if ( number . is_nan ( ) | | number . is_positive_infinity ( ) )
return number ;
// 3. If n is 1𝔽 , return +0𝔽 .
if ( number . as_double ( ) = = 1. )
return Value ( 0 ) ;
// 4. If n is +0𝔽 or n is -0𝔽 , return -∞𝔽.
if ( number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return js_negative_infinity ( ) ;
// 5. If n < -0𝔽 , return NaN.
if ( number . as_double ( ) < - 0. )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-11-28 12:06:50 +01:00
2023-04-14 17:04:00 +02:00
// 6. Return an implementation-approximated Number value representing the result of the base 2 logarithm of ℝ (n).
return Value ( : : log2 ( number . as_double ( ) ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.25 Math.max ( ...args ), https://tc39.es/ecma262/#sec-math.max
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : max )
{
// 1. Let coerced be a new empty List.
Vector < Value > coerced ;
// 2. For each element arg of args, do
for ( size_t i = 0 ; i < vm . argument_count ( ) ; + + i ) {
// a. Let n be ? ToNumber(arg).
auto number = TRY ( vm . argument ( i ) . to_number ( vm ) ) ;
// b. Append n to coerced.
coerced . append ( number ) ;
}
// 3. Let highest be -∞𝔽.
auto highest = js_negative_infinity ( ) ;
// 4. For each element number of coerced, do
for ( auto & number : coerced ) {
// a. If number is NaN, return NaN.
if ( number . is_nan ( ) )
return js_nan ( ) ;
// b. If number is +0𝔽 and highest is -0𝔽 , set highest to +0𝔽 .
// c. If number > highest, set highest to number.
if ( ( number . is_positive_zero ( ) & & highest . is_negative_zero ( ) ) | | number . as_double ( ) > highest . as_double ( ) )
highest = number ;
}
// 5. Return highest.
return highest ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.26 Math.min ( ...args ), https://tc39.es/ecma262/#sec-math.min
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : min )
{
// 1. Let coerced be a new empty List.
Vector < Value > coerced ;
// 2. For each element arg of args, do
for ( size_t i = 0 ; i < vm . argument_count ( ) ; + + i ) {
// a. Let n be ? ToNumber(arg).
auto number = TRY ( vm . argument ( i ) . to_number ( vm ) ) ;
// b. Append n to coerced.
coerced . append ( number ) ;
}
// 3. Let lowest be +∞𝔽.
auto lowest = js_infinity ( ) ;
// 4. For each element number of coerced, do
for ( auto & number : coerced ) {
// a. If number is NaN, return NaN.
if ( number . is_nan ( ) )
return js_nan ( ) ;
// b. If number is -0𝔽 and lowest is +0𝔽 , set lowest to -0𝔽 .
// c. If number < lowest, set lowest to number.
if ( ( number . is_negative_zero ( ) & & lowest . is_positive_zero ( ) ) | | number . as_double ( ) < lowest . as_double ( ) )
lowest = number ;
}
// 5. Return lowest.
return lowest ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.27 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
2023-11-24 09:38:28 +01:00
ThrowCompletionOr < Value > MathObject : : pow_impl ( VM & vm , Value base , Value exponent )
2023-04-14 17:04:00 +02:00
{
// Set base to ? ToNumber(base).
2023-11-24 09:38:28 +01:00
base = TRY ( base . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. Set exponent to ? ToNumber(exponent).
2023-11-24 09:38:28 +01:00
exponent = TRY ( exponent . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 3. Return Number::exponentiate(base, exponent).
return JS : : exp ( vm , base , exponent ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.27 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
2023-11-24 09:38:28 +01:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : pow )
{
return pow_impl ( vm , vm . argument ( 0 ) , vm . argument ( 1 ) ) ;
}
2025-04-11 22:12:04 +02:00
// http://vigna.di.unimi.it/ftp/papers/xorshiftplus.pdf
class XorShift128PlusRNG {
2025-04-01 12:33:17 +02:00
public :
2025-04-11 22:12:04 +02:00
XorShift128PlusRNG ( )
2025-04-01 12:33:17 +02:00
{
2025-04-11 22:12:04 +02:00
// Splitmix64 is used as xorshift is sensitive to being seeded with all 0s
u64 seed = Crypto : : get_secure_random < u64 > ( ) ;
2025-04-01 12:33:17 +02:00
m_low = splitmix64 ( seed ) ;
2025-04-11 22:12:04 +02:00
seed = Crypto : : get_secure_random < u64 > ( ) ;
2025-04-01 12:33:17 +02:00
m_high = splitmix64 ( seed ) ;
}
double get ( )
{
u64 value = advance ( ) & ( ( 1ULL < < 53 ) - 1 ) ;
return value * ( 1.0 / ( 1ULL < < 53 ) ) ;
}
private :
u64 splitmix64 ( u64 & state )
{
u64 z = ( state + = 0x9e3779b97f4a7c15ULL ) ;
z = ( z ^ ( z > > 30 ) ) * 0xbf58476d1ce4e5b9ULL ;
z = ( z ^ ( z > > 27 ) ) * 0x94d049bb133111ebULL ;
return z ^ ( z > > 31 ) ;
}
2025-04-11 22:12:04 +02:00
// Apparently this set of constants is better: https://stackoverflow.com/a/34432126
2025-04-01 12:33:17 +02:00
u64 advance ( )
{
u64 s1 = m_low ;
u64 const s0 = m_high ;
u64 const result = s0 + s1 ;
m_low = s0 ;
s1 ^ = s1 < < 23 ;
2025-04-11 22:12:04 +02:00
s1 ^ = s1 > > 18 ;
s1 ^ = s0 ^ ( s0 > > 5 ) ;
2025-04-01 12:33:17 +02:00
m_high = s1 ;
return result + s1 ;
}
u64 m_low { 0 } ;
u64 m_high { 0 } ;
} ;
2025-04-03 12:01:31 +02:00
Value MathObject : : random_impl ( )
2023-04-14 17:04:00 +02:00
{
// This function returns a Number value with positive sign, greater than or equal to +0𝔽 but strictly less than 1𝔽 ,
// chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an
// implementation-defined algorithm or strategy.
2025-04-11 22:12:04 +02:00
static XorShift128PlusRNG rng ;
2025-04-03 12:01:31 +02:00
return Value ( rng . get ( ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.28 Math.random ( ), https://tc39.es/ecma262/#sec-math.random
2025-04-03 12:01:31 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : random )
{
return random_impl ( ) ;
2023-04-14 17:04:00 +02:00
}
2025-04-28 19:24:01 -04:00
// 21.3.2.29 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
2023-11-24 09:50:11 +01:00
ThrowCompletionOr < Value > MathObject : : round_impl ( VM & vm , Value x )
2023-04-14 17:04:00 +02:00
{
// 1. Let n be ? ToNumber(x).
2023-11-24 09:50:11 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is not finite or n is an integral Number, return n.
if ( ! number . is_finite_number ( ) | | number . as_double ( ) = = : : trunc ( number . as_double ( ) ) )
return number ;
// 3. If n < 0.5𝔽 and n > +0𝔽 , return +0𝔽 .
// 4. If n < -0𝔽 and n ≥ -0.5𝔽 , return -0𝔽 .
// 5. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.
double integer = : : ceil ( number . as_double ( ) ) ;
if ( integer - 0.5 > number . as_double ( ) )
integer - - ;
return Value ( integer ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.29 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
2023-11-24 09:50:11 +01:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : round )
{
return round_impl ( vm , vm . argument ( 0 ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.30 Math.sign ( x ), https://tc39.es/ecma262/#sec-math.sign
2023-04-14 17:04:00 +02:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : sign )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is one of NaN, +0𝔽 , or -0𝔽 , return n.
if ( number . is_nan ( ) | | number . as_double ( ) = = 0 )
return number ;
// 3. If n < -0𝔽 , return -1𝔽 .
if ( number . as_double ( ) < 0 )
return Value ( - 1 ) ;
// 4. Return 1𝔽 .
return Value ( 1 ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.31 Math.sin ( x ), https://tc39.es/ecma262/#sec-math.sin
2025-05-26 13:54:46 +03:00
ThrowCompletionOr < Value > MathObject : : sin_impl ( VM & vm , Value value )
2023-04-14 17:04:00 +02:00
{
// 1. Let n be ? ToNumber(x).
2025-05-26 13:54:46 +03:00
auto number = TRY ( value . to_number ( vm ) ) ;
2023-04-14 17:04:00 +02:00
// 2. If n is NaN, n is +0𝔽 , or n is -0𝔽 , return n.
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. If n is +∞𝔽 or n is -∞𝔽, return NaN.
if ( number . is_infinity ( ) )
return js_nan ( ) ;
// 4. Return an implementation-approximated Number value representing the result of the sine of ℝ (n).
return Value ( : : sin ( number . as_double ( ) ) ) ;
2020-12-28 17:34:51 +03:00
}
2025-05-26 13:54:46 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : sin )
{
return sin_impl ( vm , vm . argument ( 0 ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.32 Math.sinh ( x ), https://tc39.es/ecma262/#sec-math.sinh
2021-10-29 01:00:05 +03:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : sinh )
2020-12-28 17:34:51 +03:00
{
2022-11-28 12:08:01 +01:00
// 1. Let n be ? ToNumber(x).
2022-08-21 14:00:56 +01:00
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
2022-11-28 12:08:01 +01:00
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( ! number . is_finite_number ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. Return an implementation-approximated Number value representing the result of the hyperbolic sine of ℝ (n).
2020-12-28 17:34:51 +03:00
return Value ( : : sinh ( number . as_double ( ) ) ) ;
}
2025-04-28 19:24:01 -04:00
// 21.3.2.33 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
2023-11-24 09:35:37 +01:00
ThrowCompletionOr < Value > MathObject : : sqrt_impl ( VM & vm , Value x )
2020-12-28 17:34:51 +03:00
{
2023-04-14 17:04:00 +02:00
// Let n be ? ToNumber(x).
2023-11-24 09:35:37 +01:00
auto number = TRY ( x . to_number ( vm ) ) ;
2022-08-20 16:24:44 +02:00
2023-04-14 17:04:00 +02:00
// 2. If n is one of NaN, +0𝔽 , -0𝔽 , or +∞𝔽, return n.
if ( number . is_nan ( ) | | number . as_double ( ) = = 0 | | number . is_positive_infinity ( ) )
return number ;
// 3. If n < -0𝔽 , return NaN.
if ( number . as_double ( ) < 0 )
2020-12-28 17:34:51 +03:00
return js_nan ( ) ;
2022-08-20 16:24:44 +02:00
2023-04-14 17:04:00 +02:00
// 4. Return an implementation-approximated Number value representing the result of the square root of ℝ (n).
return Value ( : : sqrt ( number . as_double ( ) ) ) ;
}
2022-08-20 16:24:44 +02:00
2025-04-28 19:24:01 -04:00
// 21.3.2.33 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
2023-11-24 09:35:37 +01:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : sqrt )
{
return sqrt_impl ( vm , vm . argument ( 0 ) ) ;
}
2025-03-02 20:04:38 +01:00
struct TwoSumResult {
2025-10-01 12:14:14 -04:00
double hi { 0 } ;
double lo { 0 } ;
2025-03-02 20:04:38 +01:00
} ;
2025-10-01 12:14:14 -04:00
static constexpr TwoSumResult two_sum ( double x , double y )
2025-03-02 20:04:38 +01:00
{
double hi = x + y ;
double lo = y - ( hi - x ) ;
return { hi , lo } ;
}
2025-10-01 12:14:14 -04:00
// 21.3.2.34 Math.sumPrecise ( items ), https://tc39.es/ecma262/#sec-math.sumprecise
2025-04-28 19:19:59 -04:00
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : sumPrecise )
2025-03-02 20:04:38 +01:00
{
2025-10-01 12:14:14 -04:00
static constexpr double MAX_DOUBLE = NumericLimits < double > : : max ( ) ;
static double MAX_ULP = MAX_DOUBLE - nextafter ( MAX_DOUBLE , 0.0 ) ;
static constexpr double POW_2_1023 = 8.98846567431158e+307 ;
2025-03-02 20:04:38 +01:00
2025-04-28 19:19:59 -04:00
enum class State {
2025-03-02 20:04:38 +01:00
MinusZero ,
PlusInfinity ,
MinusInfinity ,
NotANumber ,
Finite
} ;
2025-04-28 19:19:59 -04:00
auto items = vm . argument ( 0 ) ;
// 1. Perform ? RequireObjectCoercible(items).
TRY ( require_object_coercible ( vm , items ) ) ;
// 2. Let iteratorRecord be ? GetIterator(items, SYNC).
auto iterator_record = TRY ( get_iterator ( vm , items , IteratorHint : : Sync ) ) ;
// 3. Let state be MINUS-ZERO.
2025-03-02 20:04:38 +01:00
State state = State : : MinusZero ;
// 4. Let sum be 0.
// 5. Let count be 0.
double overflow = 0.0 ;
u64 count = 0 ;
Vector < double > partials ;
2025-04-28 19:19:59 -04:00
// 6. Let next be NOT-STARTED.
// 7. Repeat, while next is not DONE
2025-03-02 20:04:38 +01:00
for ( ; ; ) {
// a. Set next to ? IteratorStepValue(iteratorRecord).
2025-04-28 19:19:59 -04:00
auto next = TRY ( iterator_step_value ( vm , iterator_record ) ) ;
2025-03-02 20:04:38 +01:00
if ( ! next . has_value ( ) )
break ;
2025-04-28 19:19:59 -04:00
auto next_value = next . value ( ) ;
// b. If next is not DONE, then
2025-10-01 12:14:14 -04:00
// i. If count ≥ 2**53 - 1, then
if ( count > = ( 1ULL < < 53 ) - 1 ) {
// 1. NOTE: This step is not expected to be reached in practice and is included only so that implementations
// may rely on inputs being "reasonably sized" without violating this specification.
// 2. Let error be ThrowCompletion(a newly created RangeError object).
2025-04-28 19:19:59 -04:00
auto error = vm . throw_completion < RangeError > ( ErrorType : : ArrayMaxSize ) ;
2025-03-02 20:04:38 +01:00
2025-10-01 12:14:14 -04:00
// 3. Return ? IteratorClose(iteratorRecord, error).
2025-04-28 19:19:59 -04:00
return iterator_close ( vm , iterator_record , error ) ;
}
2025-10-01 12:14:14 -04:00
// ii. If next is not a Number, then
2025-04-28 19:19:59 -04:00
if ( ! next_value . is_number ( ) ) {
2025-03-02 20:04:38 +01:00
// 1. Let error be ThrowCompletion(a newly created TypeError object).
2025-04-28 19:19:59 -04:00
auto error = vm . throw_completion < TypeError > ( ErrorType : : IsNotA , next_value , " number " ) ;
2025-03-02 20:04:38 +01:00
// 2. Return ? IteratorClose(iteratorRecord, error).
2025-04-28 19:19:59 -04:00
return iterator_close ( vm , iterator_record , error ) ;
}
2025-03-02 20:04:38 +01:00
2025-10-01 12:14:14 -04:00
// iii. Let n be next.
2025-04-28 19:19:59 -04:00
auto n = next_value . as_double ( ) ;
2025-03-02 20:04:38 +01:00
2025-10-01 12:14:14 -04:00
// iv. If state is not NOT-A-NUMBER, then
2025-03-02 20:04:38 +01:00
if ( state ! = State : : NotANumber ) {
// 1. If n is NaN, then
2025-04-28 19:19:59 -04:00
if ( next_value . is_nan ( ) ) {
// a. Set state to NOT-A-NUMBER.
2025-03-02 20:04:38 +01:00
state = State : : NotANumber ;
2025-04-28 19:19:59 -04:00
}
// 2. Else if n is +∞𝔽, then
else if ( next_value . is_positive_infinity ( ) ) {
// a. If state is MINUS-INFINITY, set state to NOT-A-NUMBER.
// b. Else, set state to PLUS-INFINITY.
state = state = = State : : MinusInfinity ? State : : NotANumber : State : : PlusInfinity ;
}
// 3. Else if n is -∞𝔽, then
else if ( next_value . is_negative_infinity ( ) ) {
// a. If state is PLUS-INFINITY, set state to NOT-A-NUMBER.
// b. Else, set state to MINUS-INFINITY.
state = state = = State : : PlusInfinity ? State : : NotANumber : State : : MinusInfinity ;
}
// 4. Else if n is not -0𝔽 and state is either MINUS-ZERO or FINITE, then
else if ( ! next_value . is_negative_zero ( ) & & ( state = = State : : MinusZero | | state = = State : : Finite ) ) {
// a. Set state to FINITE.
2025-03-02 20:04:38 +01:00
state = State : : Finite ;
// b. Set sum to sum + ℝ (n).
double x = n ;
size_t used_partials = 0 ;
for ( size_t i = 0 ; i < partials . size ( ) ; i + + ) {
double y = partials [ i ] ;
if ( AK : : abs ( x ) < AK : : abs ( y ) )
swap ( x , y ) ;
TwoSumResult result = two_sum ( x , y ) ;
double hi = result . hi ;
double lo = result . lo ;
if ( isinf ( hi ) ) {
double sign = signbit ( hi ) ? - 1.0 : 1.0 ;
overflow + = sign ;
if ( AK : : abs ( overflow ) > = ( 1ULL < < 53 ) )
return vm . throw_completion < RangeError > ( ErrorType : : MathSumPreciseOverflow ) ;
x = ( x - sign * POW_2_1023 ) - sign * POW_2_1023 ;
if ( AK : : abs ( x ) < AK : : abs ( y ) )
swap ( x , y ) ;
result = two_sum ( x , y ) ;
hi = result . hi ;
lo = result . lo ;
}
if ( lo ! = 0.0 ) {
partials [ used_partials + + ] = lo ;
}
x = hi ;
}
partials . resize ( used_partials ) ;
if ( x ! = 0.0 ) {
partials . append ( x ) ;
}
}
}
2025-10-01 12:14:14 -04:00
// v. Set count to count + 1.
+ + count ;
2025-03-02 20:04:38 +01:00
}
2025-04-28 19:19:59 -04:00
// 8. If state is NOT-A-NUMBER, return NaN.
2025-03-02 20:04:38 +01:00
if ( state = = State : : NotANumber )
return js_nan ( ) ;
2025-04-28 19:19:59 -04:00
// 9. If state is PLUS-INFINITY, return +∞𝔽.
2025-03-02 20:04:38 +01:00
if ( state = = State : : PlusInfinity )
return js_infinity ( ) ;
2025-04-28 19:19:59 -04:00
// 10. If state is MINUS-INFINITY, return -∞𝔽.
2025-03-02 20:04:38 +01:00
if ( state = = State : : MinusInfinity )
return js_negative_infinity ( ) ;
2025-04-28 19:19:59 -04:00
// 11. If state is MINUS-ZERO, return -0𝔽 .
2025-03-02 20:04:38 +01:00
if ( state = = State : : MinusZero )
return Value ( - 0.0 ) ;
// 12. Return 𝔽 (sum).
int n = partials . size ( ) - 1 ;
double hi = 0.0 ;
double lo = 0.0 ;
if ( overflow ! = 0.0 ) {
double next = n > = 0 ? partials [ n ] : 0.0 ;
n - - ;
if ( AK : : abs ( overflow ) > 1.0 | | ( overflow > 0.0 & & next > 0.0 ) | | ( overflow < 0.0 & & next < 0.0 ) ) {
return overflow > 0.0 ? js_infinity ( ) : js_negative_infinity ( ) ;
}
TwoSumResult result = two_sum ( overflow * POW_2_1023 , next / 2.0 ) ;
hi = result . hi ;
lo = result . lo * 2.0 ;
if ( isinf ( hi * 2.0 ) ) {
if ( hi > 0.0 ) {
if ( hi = = POW_2_1023 & & lo = = - ( MAX_ULP / 2.0 ) & & n > = 0 & & partials [ n ] < 0.0 ) {
return Value ( MAX_DOUBLE ) ;
}
return js_infinity ( ) ;
} else {
if ( hi = = - POW_2_1023 & & lo = = ( MAX_ULP / 2.0 ) & & n > = 0 & & partials [ n ] > 0.0 ) {
return Value ( - MAX_DOUBLE ) ;
}
return js_negative_infinity ( ) ;
}
}
if ( lo ! = 0.0 ) {
partials [ n + 1 ] = lo ;
n + + ;
lo = 0.0 ;
}
hi * = 2.0 ;
}
while ( n > = 0 ) {
double x = hi ;
double y = partials [ n ] ;
n - - ;
TwoSumResult result = two_sum ( x , y ) ;
hi = result . hi ;
lo = result . lo ;
if ( lo ! = 0.0 ) {
break ;
}
}
if ( n > = 0 & & ( ( lo < 0.0 & & partials [ n ] < 0.0 ) | | ( lo > 0.0 & & partials [ n ] > 0.0 ) ) ) {
double y = lo * 2.0 ;
double x = hi + y ;
double yr = x - hi ;
if ( y = = yr ) {
hi = x ;
}
}
2025-04-28 19:19:59 -04:00
return hi ;
2025-03-02 20:04:38 +01:00
}
2025-10-01 12:14:14 -04:00
// 21.3.2.35 Math.tan ( x ), https://tc39.es/ecma262/#sec-math.tan
ThrowCompletionOr < Value > MathObject : : tan_impl ( VM & vm , Value value )
{
// Let n be ? ToNumber(x).
auto number = TRY ( value . to_number ( vm ) ) ;
// 2. If n is NaN, n is +0𝔽 , or n is -0𝔽 , return n.
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. If n is +∞𝔽, or n is -∞𝔽, return NaN.
if ( number . is_infinity ( ) )
return js_nan ( ) ;
// 4. Return an implementation-approximated Number value representing the result of the tangent of ℝ (n).
return Value ( : : tan ( number . as_double ( ) ) ) ;
}
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : tan )
{
return tan_impl ( vm , vm . argument ( 0 ) ) ;
}
// 21.3.2.36 Math.tanh ( x ), https://tc39.es/ecma262/#sec-math.tanh
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : tanh )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is NaN, n is +0𝔽 , or n is -0𝔽 , return n.
if ( number . is_nan ( ) | | number . is_positive_zero ( ) | | number . is_negative_zero ( ) )
return number ;
// 3. If n is +∞𝔽, return 1𝔽 .
if ( number . is_positive_infinity ( ) )
return Value ( 1 ) ;
// 4. If n is -∞𝔽, return -1𝔽 .
if ( number . is_negative_infinity ( ) )
return Value ( - 1 ) ;
// 5. Return an implementation-approximated Number value representing the result of the hyperbolic tangent of ℝ (n).
return Value ( : : tanh ( number . as_double ( ) ) ) ;
}
// 21.3.2.37 Math.trunc ( x ), https://tc39.es/ecma262/#sec-math.trunc
JS_DEFINE_NATIVE_FUNCTION ( MathObject : : trunc )
{
// 1. Let n be ? ToNumber(x).
auto number = TRY ( vm . argument ( 0 ) . to_number ( vm ) ) ;
// 2. If n is not finite or n is either +0𝔽 or -0𝔽 , return n.
if ( number . is_nan ( ) | | number . is_infinity ( ) | | number . as_double ( ) = = 0 )
return number ;
// 3. If n < 1𝔽 and n > +0𝔽 , return +0𝔽 .
// 4. If n < -0𝔽 and n > -1𝔽 , return -0𝔽 .
// 5. Return the integral Number nearest n in the direction of +0𝔽 .
return Value ( number . as_double ( ) < 0
? : : ceil ( number . as_double ( ) )
: : : floor ( number . as_double ( ) ) ) ;
}
2020-03-21 17:52:12 +01:00
}