Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / jsmath.cpp
blob41a9911b33d89d39828b6e2049cbc2b18e634f79
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/PropertySpec.h"
28 #include "util/DifferentialTesting.h"
29 #include "vm/JSContext.h"
30 #include "vm/Realm.h"
31 #include "vm/Time.h"
33 #include "vm/JSObject-inl.h"
35 using namespace js;
37 using JS::GenericNaN;
38 using JS::ToNumber;
39 using mozilla::ExponentComponent;
40 using mozilla::FloatingPoint;
41 using mozilla::IsNegative;
42 using mozilla::IsNegativeZero;
43 using mozilla::Maybe;
44 using mozilla::NegativeInfinity;
45 using mozilla::NumberEqualsInt32;
46 using mozilla::NumberEqualsInt64;
47 using mozilla::PositiveInfinity;
48 using mozilla::WrappingMultiply;
50 static mozilla::Atomic<bool, mozilla::Relaxed> sUseFdlibmForSinCosTan;
52 JS_PUBLIC_API void JS::SetUseFdlibmForSinCosTan(bool value) {
53 sUseFdlibmForSinCosTan = value;
56 bool js::math_use_fdlibm_for_sin_cos_tan() { return sUseFdlibmForSinCosTan; }
58 static inline bool UseFdlibmForSinCosTan(const CallArgs& args) {
59 return sUseFdlibmForSinCosTan ||
60 args.callee().nonCCWRealm()->creationOptions().alwaysUseFdlibm();
63 template <UnaryMathFunctionType F>
64 static bool math_function(JSContext* cx, CallArgs& args) {
65 if (args.length() == 0) {
66 args.rval().setNaN();
67 return true;
70 double x;
71 if (!ToNumber(cx, args[0], &x)) {
72 return false;
75 // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always
76 // type the value as a double.
78 // NB: Always stored as a double so the math function can be inlined
79 // through MMathFunction.
80 double z = F(x);
81 args.rval().setDouble(z);
82 return true;
85 double js::math_abs_impl(double x) { return mozilla::Abs(x); }
87 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
88 CallArgs args = CallArgsFromVp(argc, vp);
90 if (args.length() == 0) {
91 args.rval().setNaN();
92 return true;
95 double x;
96 if (!ToNumber(cx, args[0], &x)) {
97 return false;
100 args.rval().setNumber(math_abs_impl(x));
101 return true;
104 double js::math_acos_impl(double x) {
105 AutoUnsafeCallWithABI unsafe;
106 return fdlibm_acos(x);
109 static bool math_acos(JSContext* cx, unsigned argc, Value* vp) {
110 CallArgs args = CallArgsFromVp(argc, vp);
111 return math_function<math_acos_impl>(cx, args);
114 double js::math_asin_impl(double x) {
115 AutoUnsafeCallWithABI unsafe;
116 return fdlibm_asin(x);
119 static bool math_asin(JSContext* cx, unsigned argc, Value* vp) {
120 CallArgs args = CallArgsFromVp(argc, vp);
121 return math_function<math_asin_impl>(cx, args);
124 double js::math_atan_impl(double x) {
125 AutoUnsafeCallWithABI unsafe;
126 return fdlibm_atan(x);
129 static bool math_atan(JSContext* cx, unsigned argc, Value* vp) {
130 CallArgs args = CallArgsFromVp(argc, vp);
131 return math_function<math_atan_impl>(cx, args);
134 double js::ecmaAtan2(double y, double x) {
135 AutoUnsafeCallWithABI unsafe;
136 return fdlibm_atan2(y, x);
139 static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) {
140 CallArgs args = CallArgsFromVp(argc, vp);
142 double y;
143 if (!ToNumber(cx, args.get(0), &y)) {
144 return false;
147 double x;
148 if (!ToNumber(cx, args.get(1), &x)) {
149 return false;
152 double z = ecmaAtan2(y, x);
153 args.rval().setDouble(z);
154 return true;
157 double js::math_ceil_impl(double x) {
158 AutoUnsafeCallWithABI unsafe;
159 return fdlibm_ceil(x);
162 static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) {
163 CallArgs args = CallArgsFromVp(argc, vp);
165 if (args.length() == 0) {
166 args.rval().setNaN();
167 return true;
170 double x;
171 if (!ToNumber(cx, args[0], &x)) {
172 return false;
175 args.rval().setNumber(math_ceil_impl(x));
176 return true;
179 static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) {
180 CallArgs args = CallArgsFromVp(argc, vp);
182 if (args.length() == 0) {
183 args.rval().setInt32(32);
184 return true;
187 uint32_t n;
188 if (!ToUint32(cx, args[0], &n)) {
189 return false;
192 if (n == 0) {
193 args.rval().setInt32(32);
194 return true;
197 args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
198 return true;
201 double js::math_cos_fdlibm_impl(double x) {
202 AutoUnsafeCallWithABI unsafe;
203 return fdlibm_cos(x);
206 double js::math_cos_native_impl(double x) {
207 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
208 AutoUnsafeCallWithABI unsafe;
209 return std::cos(x);
212 static bool math_cos(JSContext* cx, unsigned argc, Value* vp) {
213 CallArgs args = CallArgsFromVp(argc, vp);
214 if (UseFdlibmForSinCosTan(args)) {
215 return math_function<math_cos_fdlibm_impl>(cx, args);
217 return math_function<math_cos_native_impl>(cx, args);
220 double js::math_exp_impl(double x) {
221 AutoUnsafeCallWithABI unsafe;
222 return fdlibm_exp(x);
225 static bool math_exp(JSContext* cx, unsigned argc, Value* vp) {
226 CallArgs args = CallArgsFromVp(argc, vp);
227 return math_function<math_exp_impl>(cx, args);
230 double js::math_floor_impl(double x) {
231 AutoUnsafeCallWithABI unsafe;
232 return fdlibm_floor(x);
235 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
236 CallArgs args = CallArgsFromVp(argc, vp);
238 if (args.length() == 0) {
239 args.rval().setNaN();
240 return true;
243 double x;
244 if (!ToNumber(cx, args[0], &x)) {
245 return false;
248 args.rval().setNumber(math_floor_impl(x));
249 return true;
252 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
253 MutableHandleValue res) {
254 int32_t a = 0, b = 0;
255 if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
256 return false;
258 if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
259 return false;
262 res.setInt32(WrappingMultiply(a, b));
263 return true;
266 static bool math_imul(JSContext* cx, unsigned argc, Value* vp) {
267 CallArgs args = CallArgsFromVp(argc, vp);
269 return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
272 // Implements Math.fround (20.2.2.16) up to step 3
273 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
274 double d;
275 bool success = ToNumber(cx, v, &d);
276 *out = static_cast<float>(d);
277 return success;
280 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
281 float f;
282 if (!RoundFloat32(cx, arg, &f)) {
283 return false;
286 res.setDouble(static_cast<double>(f));
287 return true;
290 double js::RoundFloat32(double d) {
291 return static_cast<double>(static_cast<float>(d));
294 static bool math_fround(JSContext* cx, unsigned argc, Value* vp) {
295 CallArgs args = CallArgsFromVp(argc, vp);
297 if (args.length() == 0) {
298 args.rval().setNaN();
299 return true;
302 return RoundFloat32(cx, args[0], args.rval());
305 double js::math_log_impl(double x) {
306 AutoUnsafeCallWithABI unsafe;
307 return fdlibm_log(x);
310 static bool math_log(JSContext* cx, unsigned argc, Value* vp) {
311 CallArgs args = CallArgsFromVp(argc, vp);
312 return math_function<math_log_impl>(cx, args);
315 double js::math_max_impl(double x, double y) {
316 AutoUnsafeCallWithABI unsafe;
318 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
319 if (x > y || std::isnan(x) || (x == y && IsNegative(y))) {
320 return x;
322 return y;
325 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
326 CallArgs args = CallArgsFromVp(argc, vp);
328 double maxval = NegativeInfinity<double>();
329 for (unsigned i = 0; i < args.length(); i++) {
330 double x;
331 if (!ToNumber(cx, args[i], &x)) {
332 return false;
334 maxval = math_max_impl(x, maxval);
336 args.rval().setNumber(maxval);
337 return true;
340 double js::math_min_impl(double x, double y) {
341 AutoUnsafeCallWithABI unsafe;
343 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
344 if (x < y || std::isnan(x) || (x == y && IsNegativeZero(x))) {
345 return x;
347 return y;
350 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
351 CallArgs args = CallArgsFromVp(argc, vp);
353 double minval = PositiveInfinity<double>();
354 for (unsigned i = 0; i < args.length(); i++) {
355 double x;
356 if (!ToNumber(cx, args[i], &x)) {
357 return false;
359 minval = math_min_impl(x, minval);
361 args.rval().setNumber(minval);
362 return true;
365 double js::powi(double x, int32_t y) {
366 AutoUnsafeCallWithABI unsafe;
368 // It's only safe to optimize this when we can compute with integer values or
369 // the exponent is a small, positive constant.
370 if (y >= 0) {
371 uint32_t n = uint32_t(y);
373 // NB: Have to take fast-path for n <= 4 to match |MPow::foldsTo|. Otherwise
374 // we risk causing differential testing issues.
375 if (n == 0) {
376 return 1;
378 if (n == 1) {
379 return x;
381 if (n == 2) {
382 return x * x;
384 if (n == 3) {
385 return x * x * x;
387 if (n == 4) {
388 double z = x * x;
389 return z * z;
392 int64_t i;
393 if (NumberEqualsInt64(x, &i)) {
394 // Special-case: |-0 ** odd| is -0.
395 if (i == 0) {
396 return (n & 1) ? x : 0;
399 // Use int64 to cover cases like |Math.pow(2, 53)|.
400 mozilla::CheckedInt64 runningSquare = i;
401 mozilla::CheckedInt64 result = 1;
402 while (true) {
403 if ((n & 1) != 0) {
404 result *= runningSquare;
405 if (!result.isValid()) {
406 break;
410 n >>= 1;
411 if (n == 0) {
412 return static_cast<double>(result.value());
415 runningSquare *= runningSquare;
416 if (!runningSquare.isValid()) {
417 break;
422 // Fall-back to use std::pow to reduce floating point precision errors.
425 return std::pow(x, static_cast<double>(y)); // Avoid pow(double, int).
428 double js::ecmaPow(double x, double y) {
429 AutoUnsafeCallWithABI unsafe;
432 * Use powi if the exponent is an integer-valued double. We don't have to
433 * check for NaN since a comparison with NaN is always false.
435 int32_t yi;
436 if (NumberEqualsInt32(y, &yi)) {
437 return powi(x, yi);
441 * Because C99 and ECMA specify different behavior for pow(),
442 * we need to wrap the libm call to make it ECMA compliant.
444 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
445 return GenericNaN();
448 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
449 if (y == 0) {
450 return 1;
454 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
455 * when x = -0.0, so we have to guard for this.
457 if (std::isfinite(x) && x != 0.0) {
458 if (y == 0.5) {
459 return std::sqrt(x);
461 if (y == -0.5) {
462 return 1.0 / std::sqrt(x);
465 return std::pow(x, y);
468 static bool math_pow(JSContext* cx, unsigned argc, Value* vp) {
469 CallArgs args = CallArgsFromVp(argc, vp);
471 double x;
472 if (!ToNumber(cx, args.get(0), &x)) {
473 return false;
476 double y;
477 if (!ToNumber(cx, args.get(1), &y)) {
478 return false;
481 double z = ecmaPow(x, y);
482 args.rval().setNumber(z);
483 return true;
486 uint64_t js::GenerateRandomSeed() {
487 Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
489 return maybeSeed.valueOrFrom([] {
490 // Use PRMJ_Now() in case we couldn't read random bits from the OS.
491 uint64_t timestamp = PRMJ_Now();
492 return timestamp ^ (timestamp << 32);
496 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
497 // XorShift128PlusRNG must be initialized with a non-zero seed.
498 do {
499 seed[0] = GenerateRandomSeed();
500 seed[1] = GenerateRandomSeed();
501 } while (seed[0] == 0 && seed[1] == 0);
504 mozilla::non_crypto::XorShift128PlusRNG&
505 Realm::getOrCreateRandomNumberGenerator() {
506 if (randomNumberGenerator_.isNothing()) {
507 mozilla::Array<uint64_t, 2> seed;
508 GenerateXorShift128PlusSeed(seed);
509 randomNumberGenerator_.emplace(seed[0], seed[1]);
512 return randomNumberGenerator_.ref();
515 double js::math_random_impl(JSContext* cx) {
516 return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
519 static bool math_random(JSContext* cx, unsigned argc, Value* vp) {
520 CallArgs args = CallArgsFromVp(argc, vp);
521 if (js::SupportDifferentialTesting()) {
522 args.rval().setDouble(0);
523 } else {
524 args.rval().setDouble(math_random_impl(cx));
526 return true;
529 template <typename T>
530 T js::GetBiggestNumberLessThan(T x) {
531 MOZ_ASSERT(!IsNegative(x));
532 MOZ_ASSERT(std::isfinite(x));
533 using Bits = typename mozilla::FloatingPoint<T>::Bits;
534 Bits bits = mozilla::BitwiseCast<Bits>(x);
535 MOZ_ASSERT(bits > 0, "will underflow");
536 return mozilla::BitwiseCast<T>(bits - 1);
539 template double js::GetBiggestNumberLessThan<>(double x);
540 template float js::GetBiggestNumberLessThan<>(float x);
542 double js::math_round_impl(double x) {
543 AutoUnsafeCallWithABI unsafe;
545 int32_t ignored;
546 if (NumberEqualsInt32(x, &ignored)) {
547 return x;
550 /* Some numbers are so big that adding 0.5 would give the wrong number. */
551 if (ExponentComponent(x) >=
552 int_fast16_t(FloatingPoint<double>::kExponentShift)) {
553 return x;
556 double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
557 return std::copysign(fdlibm_floor(x + add), x);
560 float js::math_roundf_impl(float x) {
561 AutoUnsafeCallWithABI unsafe;
563 int32_t ignored;
564 if (NumberEqualsInt32(x, &ignored)) {
565 return x;
568 /* Some numbers are so big that adding 0.5 would give the wrong number. */
569 if (ExponentComponent(x) >=
570 int_fast16_t(FloatingPoint<float>::kExponentShift)) {
571 return x;
574 float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
575 return std::copysign(fdlibm_floorf(x + add), x);
578 /* ES5 15.8.2.15. */
579 static bool math_round(JSContext* cx, unsigned argc, Value* vp) {
580 CallArgs args = CallArgsFromVp(argc, vp);
582 if (args.length() == 0) {
583 args.rval().setNaN();
584 return true;
587 double x;
588 if (!ToNumber(cx, args[0], &x)) {
589 return false;
592 args.rval().setNumber(math_round_impl(x));
593 return true;
596 double js::math_sin_fdlibm_impl(double x) {
597 AutoUnsafeCallWithABI unsafe;
598 return fdlibm_sin(x);
601 double js::math_sin_native_impl(double x) {
602 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
603 AutoUnsafeCallWithABI unsafe;
604 return std::sin(x);
607 static bool math_sin(JSContext* cx, unsigned argc, Value* vp) {
608 CallArgs args = CallArgsFromVp(argc, vp);
609 if (UseFdlibmForSinCosTan(args)) {
610 return math_function<math_sin_fdlibm_impl>(cx, args);
612 return math_function<math_sin_native_impl>(cx, args);
615 double js::math_sqrt_impl(double x) {
616 AutoUnsafeCallWithABI unsafe;
617 return std::sqrt(x);
620 static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
621 CallArgs args = CallArgsFromVp(argc, vp);
622 return math_function<math_sqrt_impl>(cx, args);
625 double js::math_tan_fdlibm_impl(double x) {
626 AutoUnsafeCallWithABI unsafe;
627 return fdlibm_tan(x);
630 double js::math_tan_native_impl(double x) {
631 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
632 AutoUnsafeCallWithABI unsafe;
633 return std::tan(x);
636 static bool math_tan(JSContext* cx, unsigned argc, Value* vp) {
637 CallArgs args = CallArgsFromVp(argc, vp);
638 if (UseFdlibmForSinCosTan(args)) {
639 return math_function<math_tan_fdlibm_impl>(cx, args);
641 return math_function<math_tan_native_impl>(cx, args);
644 double js::math_log10_impl(double x) {
645 AutoUnsafeCallWithABI unsafe;
646 return fdlibm_log10(x);
649 static bool math_log10(JSContext* cx, unsigned argc, Value* vp) {
650 CallArgs args = CallArgsFromVp(argc, vp);
651 return math_function<math_log10_impl>(cx, args);
654 double js::math_log2_impl(double x) {
655 AutoUnsafeCallWithABI unsafe;
656 return fdlibm_log2(x);
659 static bool math_log2(JSContext* cx, unsigned argc, Value* vp) {
660 CallArgs args = CallArgsFromVp(argc, vp);
661 return math_function<math_log2_impl>(cx, args);
664 double js::math_log1p_impl(double x) {
665 AutoUnsafeCallWithABI unsafe;
666 return fdlibm_log1p(x);
669 static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) {
670 CallArgs args = CallArgsFromVp(argc, vp);
671 return math_function<math_log1p_impl>(cx, args);
674 double js::math_expm1_impl(double x) {
675 AutoUnsafeCallWithABI unsafe;
676 return fdlibm_expm1(x);
679 static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) {
680 CallArgs args = CallArgsFromVp(argc, vp);
681 return math_function<math_expm1_impl>(cx, args);
684 double js::math_cosh_impl(double x) {
685 AutoUnsafeCallWithABI unsafe;
686 return fdlibm_cosh(x);
689 static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) {
690 CallArgs args = CallArgsFromVp(argc, vp);
691 return math_function<math_cosh_impl>(cx, args);
694 double js::math_sinh_impl(double x) {
695 AutoUnsafeCallWithABI unsafe;
696 return fdlibm_sinh(x);
699 static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) {
700 CallArgs args = CallArgsFromVp(argc, vp);
701 return math_function<math_sinh_impl>(cx, args);
704 double js::math_tanh_impl(double x) {
705 AutoUnsafeCallWithABI unsafe;
706 return fdlibm_tanh(x);
709 static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) {
710 CallArgs args = CallArgsFromVp(argc, vp);
711 return math_function<math_tanh_impl>(cx, args);
714 double js::math_acosh_impl(double x) {
715 AutoUnsafeCallWithABI unsafe;
716 return fdlibm_acosh(x);
719 static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) {
720 CallArgs args = CallArgsFromVp(argc, vp);
721 return math_function<math_acosh_impl>(cx, args);
724 double js::math_asinh_impl(double x) {
725 AutoUnsafeCallWithABI unsafe;
726 return fdlibm_asinh(x);
729 static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) {
730 CallArgs args = CallArgsFromVp(argc, vp);
731 return math_function<math_asinh_impl>(cx, args);
734 double js::math_atanh_impl(double x) {
735 AutoUnsafeCallWithABI unsafe;
736 return fdlibm_atanh(x);
739 static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) {
740 CallArgs args = CallArgsFromVp(argc, vp);
741 return math_function<math_atanh_impl>(cx, args);
744 double js::ecmaHypot(double x, double y) {
745 AutoUnsafeCallWithABI unsafe;
746 return fdlibm_hypot(x, y);
749 static inline void hypot_step(double& scale, double& sumsq, double x) {
750 double xabs = mozilla::Abs(x);
751 if (scale < xabs) {
752 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
753 scale = xabs;
754 } else if (scale != 0) {
755 sumsq += (xabs / scale) * (xabs / scale);
759 double js::hypot4(double x, double y, double z, double w) {
760 AutoUnsafeCallWithABI unsafe;
762 // Check for infinities or NaNs so that we can return immediately.
763 if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) {
764 return mozilla::PositiveInfinity<double>();
767 if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) {
768 return GenericNaN();
771 double scale = 0;
772 double sumsq = 1;
774 hypot_step(scale, sumsq, x);
775 hypot_step(scale, sumsq, y);
776 hypot_step(scale, sumsq, z);
777 hypot_step(scale, sumsq, w);
779 return scale * std::sqrt(sumsq);
782 double js::hypot3(double x, double y, double z) {
783 AutoUnsafeCallWithABI unsafe;
784 return hypot4(x, y, z, 0.0);
787 static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) {
788 CallArgs args = CallArgsFromVp(argc, vp);
789 return math_hypot_handle(cx, args, args.rval());
792 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
793 MutableHandleValue res) {
794 // IonMonkey calls the ecmaHypot function directly if two arguments are
795 // given. Do that here as well to get the same results.
796 if (args.length() == 2) {
797 double x, y;
798 if (!ToNumber(cx, args[0], &x)) {
799 return false;
801 if (!ToNumber(cx, args[1], &y)) {
802 return false;
805 double result = ecmaHypot(x, y);
806 res.setDouble(result);
807 return true;
810 bool isInfinite = false;
811 bool isNaN = false;
813 double scale = 0;
814 double sumsq = 1;
816 for (unsigned i = 0; i < args.length(); i++) {
817 double x;
818 if (!ToNumber(cx, args[i], &x)) {
819 return false;
822 isInfinite |= std::isinf(x);
823 isNaN |= std::isnan(x);
824 if (isInfinite || isNaN) {
825 continue;
828 hypot_step(scale, sumsq, x);
831 double result = isInfinite ? PositiveInfinity<double>()
832 : isNaN ? GenericNaN()
833 : scale * std::sqrt(sumsq);
834 res.setDouble(result);
835 return true;
838 double js::math_trunc_impl(double x) {
839 AutoUnsafeCallWithABI unsafe;
840 return fdlibm_trunc(x);
843 float js::math_truncf_impl(float x) {
844 AutoUnsafeCallWithABI unsafe;
845 return fdlibm_truncf(x);
848 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
849 CallArgs args = CallArgsFromVp(argc, vp);
850 if (args.length() == 0) {
851 args.rval().setNaN();
852 return true;
855 double x;
856 if (!ToNumber(cx, args[0], &x)) {
857 return false;
860 args.rval().setNumber(math_trunc_impl(x));
861 return true;
864 double js::math_sign_impl(double x) {
865 AutoUnsafeCallWithABI unsafe;
867 if (std::isnan(x)) {
868 return GenericNaN();
871 return x == 0 ? x : x < 0 ? -1 : 1;
874 static bool math_sign(JSContext* cx, unsigned argc, Value* vp) {
875 CallArgs args = CallArgsFromVp(argc, vp);
876 if (args.length() == 0) {
877 args.rval().setNaN();
878 return true;
881 double x;
882 if (!ToNumber(cx, args[0], &x)) {
883 return false;
886 args.rval().setNumber(math_sign_impl(x));
887 return true;
890 double js::math_cbrt_impl(double x) {
891 AutoUnsafeCallWithABI unsafe;
892 return fdlibm_cbrt(x);
895 static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
896 CallArgs args = CallArgsFromVp(argc, vp);
897 return math_function<math_cbrt_impl>(cx, args);
900 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
901 CallArgs args = CallArgsFromVp(argc, vp);
902 args.rval().setString(cx->names().Math);
903 return true;
906 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
907 switch (fun) {
908 case UnaryMathFunction::SinNative:
909 return math_sin_native_impl;
910 case UnaryMathFunction::SinFdlibm:
911 return math_sin_fdlibm_impl;
912 case UnaryMathFunction::CosNative:
913 return math_cos_native_impl;
914 case UnaryMathFunction::CosFdlibm:
915 return math_cos_fdlibm_impl;
916 case UnaryMathFunction::TanNative:
917 return math_tan_native_impl;
918 case UnaryMathFunction::TanFdlibm:
919 return math_tan_fdlibm_impl;
920 case UnaryMathFunction::Log:
921 return math_log_impl;
922 case UnaryMathFunction::Exp:
923 return math_exp_impl;
924 case UnaryMathFunction::ATan:
925 return math_atan_impl;
926 case UnaryMathFunction::ASin:
927 return math_asin_impl;
928 case UnaryMathFunction::ACos:
929 return math_acos_impl;
930 case UnaryMathFunction::Log10:
931 return math_log10_impl;
932 case UnaryMathFunction::Log2:
933 return math_log2_impl;
934 case UnaryMathFunction::Log1P:
935 return math_log1p_impl;
936 case UnaryMathFunction::ExpM1:
937 return math_expm1_impl;
938 case UnaryMathFunction::CosH:
939 return math_cosh_impl;
940 case UnaryMathFunction::SinH:
941 return math_sinh_impl;
942 case UnaryMathFunction::TanH:
943 return math_tanh_impl;
944 case UnaryMathFunction::ACosH:
945 return math_acosh_impl;
946 case UnaryMathFunction::ASinH:
947 return math_asinh_impl;
948 case UnaryMathFunction::ATanH:
949 return math_atanh_impl;
950 case UnaryMathFunction::Trunc:
951 return math_trunc_impl;
952 case UnaryMathFunction::Cbrt:
953 return math_cbrt_impl;
954 case UnaryMathFunction::Floor:
955 return math_floor_impl;
956 case UnaryMathFunction::Ceil:
957 return math_ceil_impl;
958 case UnaryMathFunction::Round:
959 return math_round_impl;
961 MOZ_CRASH("Unknown function");
964 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun) {
965 switch (fun) {
966 case UnaryMathFunction::SinNative:
967 return "Sin (native)";
968 case UnaryMathFunction::SinFdlibm:
969 return "Sin (fdlibm)";
970 case UnaryMathFunction::CosNative:
971 return "Cos (native)";
972 case UnaryMathFunction::CosFdlibm:
973 return "Cos (fdlibm)";
974 case UnaryMathFunction::TanNative:
975 return "Tan (native)";
976 case UnaryMathFunction::TanFdlibm:
977 return "Tan (fdlibm)";
978 case UnaryMathFunction::Log:
979 return "Log";
980 case UnaryMathFunction::Exp:
981 return "Exp";
982 case UnaryMathFunction::ACos:
983 return "ACos";
984 case UnaryMathFunction::ASin:
985 return "ASin";
986 case UnaryMathFunction::ATan:
987 return "ATan";
988 case UnaryMathFunction::Log10:
989 return "Log10";
990 case UnaryMathFunction::Log2:
991 return "Log2";
992 case UnaryMathFunction::Log1P:
993 return "Log1P";
994 case UnaryMathFunction::ExpM1:
995 return "ExpM1";
996 case UnaryMathFunction::CosH:
997 return "CosH";
998 case UnaryMathFunction::SinH:
999 return "SinH";
1000 case UnaryMathFunction::TanH:
1001 return "TanH";
1002 case UnaryMathFunction::ACosH:
1003 return "ACosH";
1004 case UnaryMathFunction::ASinH:
1005 return "ASinH";
1006 case UnaryMathFunction::ATanH:
1007 return "ATanH";
1008 case UnaryMathFunction::Trunc:
1009 return "Trunc";
1010 case UnaryMathFunction::Cbrt:
1011 return "Cbrt";
1012 case UnaryMathFunction::Floor:
1013 return "Floor";
1014 case UnaryMathFunction::Ceil:
1015 return "Ceil";
1016 case UnaryMathFunction::Round:
1017 return "Round";
1019 MOZ_CRASH("Unknown function");
1022 static const JSFunctionSpec math_static_methods[] = {
1023 JS_FN("toSource", math_toSource, 0, 0),
1024 JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
1025 JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
1026 JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
1027 JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
1028 JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
1029 JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
1030 JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
1031 JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
1032 JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
1033 JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
1034 JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
1035 JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
1036 JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
1037 JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
1038 JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
1039 JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
1040 JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
1041 JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
1042 JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
1043 JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
1044 JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
1045 JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
1046 JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
1047 JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
1048 JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
1049 JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
1050 JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
1051 JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
1052 JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
1053 JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
1054 JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
1055 JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
1056 JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
1057 JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
1058 JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
1059 JS_FS_END};
1061 static const JSPropertySpec math_static_properties[] = {
1062 JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT),
1063 JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT),
1064 JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT),
1065 JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT),
1066 JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT),
1067 JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT),
1068 JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT),
1069 JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT),
1071 JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY),
1072 JS_PS_END};
1074 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
1075 RootedObject proto(cx, &cx->global()->getObjectPrototype());
1076 return NewTenuredObjectWithGivenProto(cx, &MathClass, proto);
1079 static const ClassSpec MathClassSpec = {CreateMathObject,
1080 nullptr,
1081 math_static_methods,
1082 math_static_properties,
1083 nullptr,
1084 nullptr,
1085 nullptr};
1087 const JSClass js::MathClass = {"Math", JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
1088 JS_NULL_CLASS_OPS, &MathClassSpec};