Bug 1890750 - Part 1: Include NATIVE_JIT_ENTRY in FunctionFlags::HasJitEntryFlags...
[gecko.git] / js / src / jsmath.cpp
blob2bbfb04d92445db66bd248ff4081593afbbd428e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS math package.
9 */
11 #include "jsmath.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/MathAlgorithms.h"
16 #include "mozilla/RandomNum.h"
17 #include "mozilla/WrappingOperations.h"
19 #include <cmath>
21 #include "fdlibm.h"
22 #include "jsapi.h"
23 #include "jstypes.h"
25 #include "jit/InlinableNatives.h"
26 #include "js/Class.h"
27 #include "js/Prefs.h"
28 #include "js/PropertySpec.h"
29 #include "util/DifferentialTesting.h"
30 #include "vm/JSContext.h"
31 #include "vm/Realm.h"
32 #include "vm/Time.h"
34 #include "vm/JSObject-inl.h"
36 using namespace js;
38 using JS::GenericNaN;
39 using JS::ToNumber;
40 using mozilla::ExponentComponent;
41 using mozilla::FloatingPoint;
42 using mozilla::IsNegative;
43 using mozilla::IsNegativeZero;
44 using mozilla::Maybe;
45 using mozilla::NegativeInfinity;
46 using mozilla::NumberEqualsInt32;
47 using mozilla::NumberEqualsInt64;
48 using mozilla::PositiveInfinity;
49 using mozilla::WrappingMultiply;
51 bool js::math_use_fdlibm_for_sin_cos_tan() {
52 return JS::Prefs::use_fdlibm_for_sin_cos_tan();
55 static inline bool UseFdlibmForSinCosTan(const CallArgs& args) {
56 return math_use_fdlibm_for_sin_cos_tan() ||
57 args.callee().nonCCWRealm()->creationOptions().alwaysUseFdlibm();
60 template <UnaryMathFunctionType F>
61 static bool math_function(JSContext* cx, CallArgs& args) {
62 if (args.length() == 0) {
63 args.rval().setNaN();
64 return true;
67 double x;
68 if (!ToNumber(cx, args[0], &x)) {
69 return false;
72 // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always
73 // type the value as a double.
75 // NB: Always stored as a double so the math function can be inlined
76 // through MMathFunction.
77 double z = F(x);
78 args.rval().setDouble(z);
79 return true;
82 double js::math_abs_impl(double x) { return mozilla::Abs(x); }
84 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
85 CallArgs args = CallArgsFromVp(argc, vp);
87 if (args.length() == 0) {
88 args.rval().setNaN();
89 return true;
92 double x;
93 if (!ToNumber(cx, args[0], &x)) {
94 return false;
97 args.rval().setNumber(math_abs_impl(x));
98 return true;
101 double js::math_acos_impl(double x) {
102 AutoUnsafeCallWithABI unsafe;
103 return fdlibm_acos(x);
106 static bool math_acos(JSContext* cx, unsigned argc, Value* vp) {
107 CallArgs args = CallArgsFromVp(argc, vp);
108 return math_function<math_acos_impl>(cx, args);
111 double js::math_asin_impl(double x) {
112 AutoUnsafeCallWithABI unsafe;
113 return fdlibm_asin(x);
116 static bool math_asin(JSContext* cx, unsigned argc, Value* vp) {
117 CallArgs args = CallArgsFromVp(argc, vp);
118 return math_function<math_asin_impl>(cx, args);
121 double js::math_atan_impl(double x) {
122 AutoUnsafeCallWithABI unsafe;
123 return fdlibm_atan(x);
126 static bool math_atan(JSContext* cx, unsigned argc, Value* vp) {
127 CallArgs args = CallArgsFromVp(argc, vp);
128 return math_function<math_atan_impl>(cx, args);
131 double js::ecmaAtan2(double y, double x) {
132 AutoUnsafeCallWithABI unsafe;
133 return fdlibm_atan2(y, x);
136 static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) {
137 CallArgs args = CallArgsFromVp(argc, vp);
139 double y;
140 if (!ToNumber(cx, args.get(0), &y)) {
141 return false;
144 double x;
145 if (!ToNumber(cx, args.get(1), &x)) {
146 return false;
149 double z = ecmaAtan2(y, x);
150 args.rval().setDouble(z);
151 return true;
154 double js::math_ceil_impl(double x) {
155 AutoUnsafeCallWithABI unsafe;
156 return fdlibm_ceil(x);
159 static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) {
160 CallArgs args = CallArgsFromVp(argc, vp);
162 if (args.length() == 0) {
163 args.rval().setNaN();
164 return true;
167 double x;
168 if (!ToNumber(cx, args[0], &x)) {
169 return false;
172 args.rval().setNumber(math_ceil_impl(x));
173 return true;
176 static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) {
177 CallArgs args = CallArgsFromVp(argc, vp);
179 if (args.length() == 0) {
180 args.rval().setInt32(32);
181 return true;
184 uint32_t n;
185 if (!ToUint32(cx, args[0], &n)) {
186 return false;
189 if (n == 0) {
190 args.rval().setInt32(32);
191 return true;
194 args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
195 return true;
198 double js::math_cos_fdlibm_impl(double x) {
199 AutoUnsafeCallWithABI unsafe;
200 return fdlibm_cos(x);
203 double js::math_cos_native_impl(double x) {
204 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
205 AutoUnsafeCallWithABI unsafe;
206 return std::cos(x);
209 static bool math_cos(JSContext* cx, unsigned argc, Value* vp) {
210 CallArgs args = CallArgsFromVp(argc, vp);
211 if (UseFdlibmForSinCosTan(args)) {
212 return math_function<math_cos_fdlibm_impl>(cx, args);
214 return math_function<math_cos_native_impl>(cx, args);
217 double js::math_exp_impl(double x) {
218 AutoUnsafeCallWithABI unsafe;
219 return fdlibm_exp(x);
222 static bool math_exp(JSContext* cx, unsigned argc, Value* vp) {
223 CallArgs args = CallArgsFromVp(argc, vp);
224 return math_function<math_exp_impl>(cx, args);
227 double js::math_floor_impl(double x) {
228 AutoUnsafeCallWithABI unsafe;
229 return fdlibm_floor(x);
232 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
233 CallArgs args = CallArgsFromVp(argc, vp);
235 if (args.length() == 0) {
236 args.rval().setNaN();
237 return true;
240 double x;
241 if (!ToNumber(cx, args[0], &x)) {
242 return false;
245 args.rval().setNumber(math_floor_impl(x));
246 return true;
249 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
250 MutableHandleValue res) {
251 int32_t a = 0, b = 0;
252 if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
253 return false;
255 if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
256 return false;
259 res.setInt32(WrappingMultiply(a, b));
260 return true;
263 static bool math_imul(JSContext* cx, unsigned argc, Value* vp) {
264 CallArgs args = CallArgsFromVp(argc, vp);
266 return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
269 // Implements Math.fround (20.2.2.16) up to step 3
270 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
271 double d;
272 bool success = ToNumber(cx, v, &d);
273 *out = static_cast<float>(d);
274 return success;
277 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
278 float f;
279 if (!RoundFloat32(cx, arg, &f)) {
280 return false;
283 res.setDouble(static_cast<double>(f));
284 return true;
287 double js::RoundFloat32(double d) {
288 return static_cast<double>(static_cast<float>(d));
291 static bool math_fround(JSContext* cx, unsigned argc, Value* vp) {
292 CallArgs args = CallArgsFromVp(argc, vp);
294 if (args.length() == 0) {
295 args.rval().setNaN();
296 return true;
299 return RoundFloat32(cx, args[0], args.rval());
302 double js::math_log_impl(double x) {
303 AutoUnsafeCallWithABI unsafe;
304 return fdlibm_log(x);
307 static bool math_log(JSContext* cx, unsigned argc, Value* vp) {
308 CallArgs args = CallArgsFromVp(argc, vp);
309 return math_function<math_log_impl>(cx, args);
312 double js::math_max_impl(double x, double y) {
313 AutoUnsafeCallWithABI unsafe;
315 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
316 if (x > y || std::isnan(x) || (x == y && IsNegative(y))) {
317 return x;
319 return y;
322 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
323 CallArgs args = CallArgsFromVp(argc, vp);
325 double maxval = NegativeInfinity<double>();
326 for (unsigned i = 0; i < args.length(); i++) {
327 double x;
328 if (!ToNumber(cx, args[i], &x)) {
329 return false;
331 maxval = math_max_impl(x, maxval);
333 args.rval().setNumber(maxval);
334 return true;
337 double js::math_min_impl(double x, double y) {
338 AutoUnsafeCallWithABI unsafe;
340 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
341 if (x < y || std::isnan(x) || (x == y && IsNegativeZero(x))) {
342 return x;
344 return y;
347 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
348 CallArgs args = CallArgsFromVp(argc, vp);
350 double minval = PositiveInfinity<double>();
351 for (unsigned i = 0; i < args.length(); i++) {
352 double x;
353 if (!ToNumber(cx, args[i], &x)) {
354 return false;
356 minval = math_min_impl(x, minval);
358 args.rval().setNumber(minval);
359 return true;
362 double js::powi(double x, int32_t y) {
363 AutoUnsafeCallWithABI unsafe;
365 // It's only safe to optimize this when we can compute with integer values or
366 // the exponent is a small, positive constant.
367 if (y >= 0) {
368 uint32_t n = uint32_t(y);
370 // NB: Have to take fast-path for n <= 4 to match |MPow::foldsTo|. Otherwise
371 // we risk causing differential testing issues.
372 if (n == 0) {
373 return 1;
375 if (n == 1) {
376 return x;
378 if (n == 2) {
379 return x * x;
381 if (n == 3) {
382 return x * x * x;
384 if (n == 4) {
385 double z = x * x;
386 return z * z;
389 int64_t i;
390 if (NumberEqualsInt64(x, &i)) {
391 // Special-case: |-0 ** odd| is -0.
392 if (i == 0) {
393 return (n & 1) ? x : 0;
396 // Use int64 to cover cases like |Math.pow(2, 53)|.
397 mozilla::CheckedInt64 runningSquare = i;
398 mozilla::CheckedInt64 result = 1;
399 while (true) {
400 if ((n & 1) != 0) {
401 result *= runningSquare;
402 if (!result.isValid()) {
403 break;
407 n >>= 1;
408 if (n == 0) {
409 return static_cast<double>(result.value());
412 runningSquare *= runningSquare;
413 if (!runningSquare.isValid()) {
414 break;
419 // Fall-back to use std::pow to reduce floating point precision errors.
422 return std::pow(x, static_cast<double>(y)); // Avoid pow(double, int).
425 double js::ecmaPow(double x, double y) {
426 AutoUnsafeCallWithABI unsafe;
429 * Use powi if the exponent is an integer-valued double. We don't have to
430 * check for NaN since a comparison with NaN is always false.
432 int32_t yi;
433 if (NumberEqualsInt32(y, &yi)) {
434 return powi(x, yi);
438 * Because C99 and ECMA specify different behavior for pow(),
439 * we need to wrap the libm call to make it ECMA compliant.
441 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
442 return GenericNaN();
445 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
446 if (y == 0) {
447 return 1;
451 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
452 * when x = -0.0, so we have to guard for this.
454 if (std::isfinite(x) && x != 0.0) {
455 if (y == 0.5) {
456 return std::sqrt(x);
458 if (y == -0.5) {
459 return 1.0 / std::sqrt(x);
462 return std::pow(x, y);
465 static bool math_pow(JSContext* cx, unsigned argc, Value* vp) {
466 CallArgs args = CallArgsFromVp(argc, vp);
468 double x;
469 if (!ToNumber(cx, args.get(0), &x)) {
470 return false;
473 double y;
474 if (!ToNumber(cx, args.get(1), &y)) {
475 return false;
478 double z = ecmaPow(x, y);
479 args.rval().setNumber(z);
480 return true;
483 uint64_t js::GenerateRandomSeed() {
484 Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
486 return maybeSeed.valueOrFrom([] {
487 // Use PRMJ_Now() in case we couldn't read random bits from the OS.
488 uint64_t timestamp = PRMJ_Now();
489 return timestamp ^ (timestamp << 32);
493 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
494 // XorShift128PlusRNG must be initialized with a non-zero seed.
495 do {
496 seed[0] = GenerateRandomSeed();
497 seed[1] = GenerateRandomSeed();
498 } while (seed[0] == 0 && seed[1] == 0);
501 mozilla::non_crypto::XorShift128PlusRNG&
502 Realm::getOrCreateRandomNumberGenerator() {
503 if (randomNumberGenerator_.isNothing()) {
504 mozilla::Array<uint64_t, 2> seed;
505 GenerateXorShift128PlusSeed(seed);
506 randomNumberGenerator_.emplace(seed[0], seed[1]);
509 return randomNumberGenerator_.ref();
512 double js::math_random_impl(JSContext* cx) {
513 return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
516 static bool math_random(JSContext* cx, unsigned argc, Value* vp) {
517 CallArgs args = CallArgsFromVp(argc, vp);
518 if (js::SupportDifferentialTesting()) {
519 args.rval().setDouble(0);
520 } else {
521 args.rval().setDouble(math_random_impl(cx));
523 return true;
526 template <typename T>
527 T js::GetBiggestNumberLessThan(T x) {
528 MOZ_ASSERT(!IsNegative(x));
529 MOZ_ASSERT(std::isfinite(x));
530 using Bits = typename mozilla::FloatingPoint<T>::Bits;
531 Bits bits = mozilla::BitwiseCast<Bits>(x);
532 MOZ_ASSERT(bits > 0, "will underflow");
533 return mozilla::BitwiseCast<T>(bits - 1);
536 template double js::GetBiggestNumberLessThan<>(double x);
537 template float js::GetBiggestNumberLessThan<>(float x);
539 double js::math_round_impl(double x) {
540 AutoUnsafeCallWithABI unsafe;
542 int32_t ignored;
543 if (NumberEqualsInt32(x, &ignored)) {
544 return x;
547 /* Some numbers are so big that adding 0.5 would give the wrong number. */
548 if (ExponentComponent(x) >=
549 int_fast16_t(FloatingPoint<double>::kExponentShift)) {
550 return x;
553 double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
554 return std::copysign(fdlibm_floor(x + add), x);
557 float js::math_roundf_impl(float x) {
558 AutoUnsafeCallWithABI unsafe;
560 int32_t ignored;
561 if (NumberEqualsInt32(x, &ignored)) {
562 return x;
565 /* Some numbers are so big that adding 0.5 would give the wrong number. */
566 if (ExponentComponent(x) >=
567 int_fast16_t(FloatingPoint<float>::kExponentShift)) {
568 return x;
571 float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
572 return std::copysign(fdlibm_floorf(x + add), x);
575 /* ES5 15.8.2.15. */
576 static bool math_round(JSContext* cx, unsigned argc, Value* vp) {
577 CallArgs args = CallArgsFromVp(argc, vp);
579 if (args.length() == 0) {
580 args.rval().setNaN();
581 return true;
584 double x;
585 if (!ToNumber(cx, args[0], &x)) {
586 return false;
589 args.rval().setNumber(math_round_impl(x));
590 return true;
593 double js::math_sin_fdlibm_impl(double x) {
594 AutoUnsafeCallWithABI unsafe;
595 return fdlibm_sin(x);
598 double js::math_sin_native_impl(double x) {
599 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
600 AutoUnsafeCallWithABI unsafe;
601 return std::sin(x);
604 static bool math_sin(JSContext* cx, unsigned argc, Value* vp) {
605 CallArgs args = CallArgsFromVp(argc, vp);
606 if (UseFdlibmForSinCosTan(args)) {
607 return math_function<math_sin_fdlibm_impl>(cx, args);
609 return math_function<math_sin_native_impl>(cx, args);
612 double js::math_sqrt_impl(double x) {
613 AutoUnsafeCallWithABI unsafe;
614 return std::sqrt(x);
617 static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
618 CallArgs args = CallArgsFromVp(argc, vp);
619 return math_function<math_sqrt_impl>(cx, args);
622 double js::math_tan_fdlibm_impl(double x) {
623 AutoUnsafeCallWithABI unsafe;
624 return fdlibm_tan(x);
627 double js::math_tan_native_impl(double x) {
628 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
629 AutoUnsafeCallWithABI unsafe;
630 return std::tan(x);
633 static bool math_tan(JSContext* cx, unsigned argc, Value* vp) {
634 CallArgs args = CallArgsFromVp(argc, vp);
635 if (UseFdlibmForSinCosTan(args)) {
636 return math_function<math_tan_fdlibm_impl>(cx, args);
638 return math_function<math_tan_native_impl>(cx, args);
641 double js::math_log10_impl(double x) {
642 AutoUnsafeCallWithABI unsafe;
643 return fdlibm_log10(x);
646 static bool math_log10(JSContext* cx, unsigned argc, Value* vp) {
647 CallArgs args = CallArgsFromVp(argc, vp);
648 return math_function<math_log10_impl>(cx, args);
651 double js::math_log2_impl(double x) {
652 AutoUnsafeCallWithABI unsafe;
653 return fdlibm_log2(x);
656 static bool math_log2(JSContext* cx, unsigned argc, Value* vp) {
657 CallArgs args = CallArgsFromVp(argc, vp);
658 return math_function<math_log2_impl>(cx, args);
661 double js::math_log1p_impl(double x) {
662 AutoUnsafeCallWithABI unsafe;
663 return fdlibm_log1p(x);
666 static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) {
667 CallArgs args = CallArgsFromVp(argc, vp);
668 return math_function<math_log1p_impl>(cx, args);
671 double js::math_expm1_impl(double x) {
672 AutoUnsafeCallWithABI unsafe;
673 return fdlibm_expm1(x);
676 static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) {
677 CallArgs args = CallArgsFromVp(argc, vp);
678 return math_function<math_expm1_impl>(cx, args);
681 double js::math_cosh_impl(double x) {
682 AutoUnsafeCallWithABI unsafe;
683 return fdlibm_cosh(x);
686 static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) {
687 CallArgs args = CallArgsFromVp(argc, vp);
688 return math_function<math_cosh_impl>(cx, args);
691 double js::math_sinh_impl(double x) {
692 AutoUnsafeCallWithABI unsafe;
693 return fdlibm_sinh(x);
696 static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) {
697 CallArgs args = CallArgsFromVp(argc, vp);
698 return math_function<math_sinh_impl>(cx, args);
701 double js::math_tanh_impl(double x) {
702 AutoUnsafeCallWithABI unsafe;
703 return fdlibm_tanh(x);
706 static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) {
707 CallArgs args = CallArgsFromVp(argc, vp);
708 return math_function<math_tanh_impl>(cx, args);
711 double js::math_acosh_impl(double x) {
712 AutoUnsafeCallWithABI unsafe;
713 return fdlibm_acosh(x);
716 static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) {
717 CallArgs args = CallArgsFromVp(argc, vp);
718 return math_function<math_acosh_impl>(cx, args);
721 double js::math_asinh_impl(double x) {
722 AutoUnsafeCallWithABI unsafe;
723 return fdlibm_asinh(x);
726 static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) {
727 CallArgs args = CallArgsFromVp(argc, vp);
728 return math_function<math_asinh_impl>(cx, args);
731 double js::math_atanh_impl(double x) {
732 AutoUnsafeCallWithABI unsafe;
733 return fdlibm_atanh(x);
736 static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) {
737 CallArgs args = CallArgsFromVp(argc, vp);
738 return math_function<math_atanh_impl>(cx, args);
741 double js::ecmaHypot(double x, double y) {
742 AutoUnsafeCallWithABI unsafe;
743 return fdlibm_hypot(x, y);
746 static inline void hypot_step(double& scale, double& sumsq, double x) {
747 double xabs = mozilla::Abs(x);
748 if (scale < xabs) {
749 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
750 scale = xabs;
751 } else if (scale != 0) {
752 sumsq += (xabs / scale) * (xabs / scale);
756 double js::hypot4(double x, double y, double z, double w) {
757 AutoUnsafeCallWithABI unsafe;
759 // Check for infinities or NaNs so that we can return immediately.
760 if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) {
761 return mozilla::PositiveInfinity<double>();
764 if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) {
765 return GenericNaN();
768 double scale = 0;
769 double sumsq = 1;
771 hypot_step(scale, sumsq, x);
772 hypot_step(scale, sumsq, y);
773 hypot_step(scale, sumsq, z);
774 hypot_step(scale, sumsq, w);
776 return scale * std::sqrt(sumsq);
779 double js::hypot3(double x, double y, double z) {
780 AutoUnsafeCallWithABI unsafe;
781 return hypot4(x, y, z, 0.0);
784 static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) {
785 CallArgs args = CallArgsFromVp(argc, vp);
786 return math_hypot_handle(cx, args, args.rval());
789 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
790 MutableHandleValue res) {
791 // IonMonkey calls the ecmaHypot function directly if two arguments are
792 // given. Do that here as well to get the same results.
793 if (args.length() == 2) {
794 double x, y;
795 if (!ToNumber(cx, args[0], &x)) {
796 return false;
798 if (!ToNumber(cx, args[1], &y)) {
799 return false;
802 double result = ecmaHypot(x, y);
803 res.setDouble(result);
804 return true;
807 bool isInfinite = false;
808 bool isNaN = false;
810 double scale = 0;
811 double sumsq = 1;
813 for (unsigned i = 0; i < args.length(); i++) {
814 double x;
815 if (!ToNumber(cx, args[i], &x)) {
816 return false;
819 isInfinite |= std::isinf(x);
820 isNaN |= std::isnan(x);
821 if (isInfinite || isNaN) {
822 continue;
825 hypot_step(scale, sumsq, x);
828 double result = isInfinite ? PositiveInfinity<double>()
829 : isNaN ? GenericNaN()
830 : scale * std::sqrt(sumsq);
831 res.setDouble(result);
832 return true;
835 double js::math_trunc_impl(double x) {
836 AutoUnsafeCallWithABI unsafe;
837 return fdlibm_trunc(x);
840 float js::math_truncf_impl(float x) {
841 AutoUnsafeCallWithABI unsafe;
842 return fdlibm_truncf(x);
845 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
846 CallArgs args = CallArgsFromVp(argc, vp);
847 if (args.length() == 0) {
848 args.rval().setNaN();
849 return true;
852 double x;
853 if (!ToNumber(cx, args[0], &x)) {
854 return false;
857 args.rval().setNumber(math_trunc_impl(x));
858 return true;
861 double js::math_sign_impl(double x) {
862 AutoUnsafeCallWithABI unsafe;
864 if (std::isnan(x)) {
865 return GenericNaN();
868 return x == 0 ? x : x < 0 ? -1 : 1;
871 static bool math_sign(JSContext* cx, unsigned argc, Value* vp) {
872 CallArgs args = CallArgsFromVp(argc, vp);
873 if (args.length() == 0) {
874 args.rval().setNaN();
875 return true;
878 double x;
879 if (!ToNumber(cx, args[0], &x)) {
880 return false;
883 args.rval().setNumber(math_sign_impl(x));
884 return true;
887 double js::math_cbrt_impl(double x) {
888 AutoUnsafeCallWithABI unsafe;
889 return fdlibm_cbrt(x);
892 static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
893 CallArgs args = CallArgsFromVp(argc, vp);
894 return math_function<math_cbrt_impl>(cx, args);
897 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
898 CallArgs args = CallArgsFromVp(argc, vp);
899 args.rval().setString(cx->names().Math);
900 return true;
903 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
904 switch (fun) {
905 case UnaryMathFunction::SinNative:
906 return math_sin_native_impl;
907 case UnaryMathFunction::SinFdlibm:
908 return math_sin_fdlibm_impl;
909 case UnaryMathFunction::CosNative:
910 return math_cos_native_impl;
911 case UnaryMathFunction::CosFdlibm:
912 return math_cos_fdlibm_impl;
913 case UnaryMathFunction::TanNative:
914 return math_tan_native_impl;
915 case UnaryMathFunction::TanFdlibm:
916 return math_tan_fdlibm_impl;
917 case UnaryMathFunction::Log:
918 return math_log_impl;
919 case UnaryMathFunction::Exp:
920 return math_exp_impl;
921 case UnaryMathFunction::ATan:
922 return math_atan_impl;
923 case UnaryMathFunction::ASin:
924 return math_asin_impl;
925 case UnaryMathFunction::ACos:
926 return math_acos_impl;
927 case UnaryMathFunction::Log10:
928 return math_log10_impl;
929 case UnaryMathFunction::Log2:
930 return math_log2_impl;
931 case UnaryMathFunction::Log1P:
932 return math_log1p_impl;
933 case UnaryMathFunction::ExpM1:
934 return math_expm1_impl;
935 case UnaryMathFunction::CosH:
936 return math_cosh_impl;
937 case UnaryMathFunction::SinH:
938 return math_sinh_impl;
939 case UnaryMathFunction::TanH:
940 return math_tanh_impl;
941 case UnaryMathFunction::ACosH:
942 return math_acosh_impl;
943 case UnaryMathFunction::ASinH:
944 return math_asinh_impl;
945 case UnaryMathFunction::ATanH:
946 return math_atanh_impl;
947 case UnaryMathFunction::Trunc:
948 return math_trunc_impl;
949 case UnaryMathFunction::Cbrt:
950 return math_cbrt_impl;
951 case UnaryMathFunction::Floor:
952 return math_floor_impl;
953 case UnaryMathFunction::Ceil:
954 return math_ceil_impl;
955 case UnaryMathFunction::Round:
956 return math_round_impl;
958 MOZ_CRASH("Unknown function");
961 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun) {
962 switch (fun) {
963 case UnaryMathFunction::SinNative:
964 return "Sin (native)";
965 case UnaryMathFunction::SinFdlibm:
966 return "Sin (fdlibm)";
967 case UnaryMathFunction::CosNative:
968 return "Cos (native)";
969 case UnaryMathFunction::CosFdlibm:
970 return "Cos (fdlibm)";
971 case UnaryMathFunction::TanNative:
972 return "Tan (native)";
973 case UnaryMathFunction::TanFdlibm:
974 return "Tan (fdlibm)";
975 case UnaryMathFunction::Log:
976 return "Log";
977 case UnaryMathFunction::Exp:
978 return "Exp";
979 case UnaryMathFunction::ACos:
980 return "ACos";
981 case UnaryMathFunction::ASin:
982 return "ASin";
983 case UnaryMathFunction::ATan:
984 return "ATan";
985 case UnaryMathFunction::Log10:
986 return "Log10";
987 case UnaryMathFunction::Log2:
988 return "Log2";
989 case UnaryMathFunction::Log1P:
990 return "Log1P";
991 case UnaryMathFunction::ExpM1:
992 return "ExpM1";
993 case UnaryMathFunction::CosH:
994 return "CosH";
995 case UnaryMathFunction::SinH:
996 return "SinH";
997 case UnaryMathFunction::TanH:
998 return "TanH";
999 case UnaryMathFunction::ACosH:
1000 return "ACosH";
1001 case UnaryMathFunction::ASinH:
1002 return "ASinH";
1003 case UnaryMathFunction::ATanH:
1004 return "ATanH";
1005 case UnaryMathFunction::Trunc:
1006 return "Trunc";
1007 case UnaryMathFunction::Cbrt:
1008 return "Cbrt";
1009 case UnaryMathFunction::Floor:
1010 return "Floor";
1011 case UnaryMathFunction::Ceil:
1012 return "Ceil";
1013 case UnaryMathFunction::Round:
1014 return "Round";
1016 MOZ_CRASH("Unknown function");
1019 static const JSFunctionSpec math_static_methods[] = {
1020 JS_FN("toSource", math_toSource, 0, 0),
1021 JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
1022 JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
1023 JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
1024 JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
1025 JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
1026 JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
1027 JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
1028 JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
1029 JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
1030 JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
1031 JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
1032 JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
1033 JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
1034 JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
1035 JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
1036 JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
1037 JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
1038 JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
1039 JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
1040 JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
1041 JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
1042 JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
1043 JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
1044 JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
1045 JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
1046 JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
1047 JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
1048 JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
1049 JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
1050 JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
1051 JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
1052 JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
1053 JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
1054 JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
1055 JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
1056 JS_FS_END};
1058 static const JSPropertySpec math_static_properties[] = {
1059 JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT),
1060 JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT),
1061 JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT),
1062 JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT),
1063 JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT),
1064 JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT),
1065 JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT),
1066 JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT),
1068 JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY),
1069 JS_PS_END};
1071 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
1072 RootedObject proto(cx, &cx->global()->getObjectPrototype());
1073 return NewTenuredObjectWithGivenProto(cx, &MathClass, proto);
1076 static const ClassSpec MathClassSpec = {CreateMathObject,
1077 nullptr,
1078 math_static_methods,
1079 math_static_properties,
1080 nullptr,
1081 nullptr,
1082 nullptr};
1084 const JSClass js::MathClass = {"Math", JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
1085 JS_NULL_CLASS_OPS, &MathClassSpec};