Bug 1832850 - Part 5: Move the allocateObject definition into gc/Nursery.h r=jandem
[gecko.git] / js / src / jsmath.cpp
blobe01951bc7ff211300c017a5977a4b84f220a5bec
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"
32 #include "vm/WellKnownAtom.h" // js_*_str
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 static mozilla::Atomic<bool, mozilla::Relaxed> sUseFdlibmForSinCosTan;
53 JS_PUBLIC_API void JS::SetUseFdlibmForSinCosTan(bool value) {
54 sUseFdlibmForSinCosTan = value;
57 bool js::math_use_fdlibm_for_sin_cos_tan() { return sUseFdlibmForSinCosTan; }
59 static inline bool UseFdlibmForSinCosTan(const CallArgs& args) {
60 return sUseFdlibmForSinCosTan ||
61 args.callee().nonCCWRealm()->behaviors().shouldResistFingerprinting();
64 template <UnaryMathFunctionType F>
65 static bool math_function(JSContext* cx, CallArgs& args) {
66 if (args.length() == 0) {
67 args.rval().setNaN();
68 return true;
71 double x;
72 if (!ToNumber(cx, args[0], &x)) {
73 return false;
76 // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always
77 // type the value as a double.
79 // NB: Always stored as a double so the math function can be inlined
80 // through MMathFunction.
81 double z = F(x);
82 args.rval().setDouble(z);
83 return true;
86 double js::math_abs_impl(double x) { return mozilla::Abs(x); }
88 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
89 CallArgs args = CallArgsFromVp(argc, vp);
91 if (args.length() == 0) {
92 args.rval().setNaN();
93 return true;
96 double x;
97 if (!ToNumber(cx, args[0], &x)) {
98 return false;
101 args.rval().setNumber(math_abs_impl(x));
102 return true;
105 double js::math_acos_impl(double x) {
106 AutoUnsafeCallWithABI unsafe;
107 return fdlibm::acos(x);
110 static bool math_acos(JSContext* cx, unsigned argc, Value* vp) {
111 CallArgs args = CallArgsFromVp(argc, vp);
112 return math_function<math_acos_impl>(cx, args);
115 double js::math_asin_impl(double x) {
116 AutoUnsafeCallWithABI unsafe;
117 return fdlibm::asin(x);
120 static bool math_asin(JSContext* cx, unsigned argc, Value* vp) {
121 CallArgs args = CallArgsFromVp(argc, vp);
122 return math_function<math_asin_impl>(cx, args);
125 double js::math_atan_impl(double x) {
126 AutoUnsafeCallWithABI unsafe;
127 return fdlibm::atan(x);
130 static bool math_atan(JSContext* cx, unsigned argc, Value* vp) {
131 CallArgs args = CallArgsFromVp(argc, vp);
132 return math_function<math_atan_impl>(cx, args);
135 double js::ecmaAtan2(double y, double x) {
136 AutoUnsafeCallWithABI unsafe;
137 return fdlibm::atan2(y, x);
140 static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) {
141 CallArgs args = CallArgsFromVp(argc, vp);
143 double y;
144 if (!ToNumber(cx, args.get(0), &y)) {
145 return false;
148 double x;
149 if (!ToNumber(cx, args.get(1), &x)) {
150 return false;
153 double z = ecmaAtan2(y, x);
154 args.rval().setDouble(z);
155 return true;
158 double js::math_ceil_impl(double x) {
159 AutoUnsafeCallWithABI unsafe;
160 return fdlibm::ceil(x);
163 static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) {
164 CallArgs args = CallArgsFromVp(argc, vp);
166 if (args.length() == 0) {
167 args.rval().setNaN();
168 return true;
171 double x;
172 if (!ToNumber(cx, args[0], &x)) {
173 return false;
176 args.rval().setNumber(math_ceil_impl(x));
177 return true;
180 static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) {
181 CallArgs args = CallArgsFromVp(argc, vp);
183 if (args.length() == 0) {
184 args.rval().setInt32(32);
185 return true;
188 uint32_t n;
189 if (!ToUint32(cx, args[0], &n)) {
190 return false;
193 if (n == 0) {
194 args.rval().setInt32(32);
195 return true;
198 args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
199 return true;
202 double js::math_cos_fdlibm_impl(double x) {
203 AutoUnsafeCallWithABI unsafe;
204 return fdlibm::cos(x);
207 double js::math_cos_native_impl(double x) {
208 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
209 AutoUnsafeCallWithABI unsafe;
210 return std::cos(x);
213 static bool math_cos(JSContext* cx, unsigned argc, Value* vp) {
214 CallArgs args = CallArgsFromVp(argc, vp);
215 if (UseFdlibmForSinCosTan(args)) {
216 return math_function<math_cos_fdlibm_impl>(cx, args);
218 return math_function<math_cos_native_impl>(cx, args);
221 double js::math_exp_impl(double x) {
222 AutoUnsafeCallWithABI unsafe;
223 return fdlibm::exp(x);
226 static bool math_exp(JSContext* cx, unsigned argc, Value* vp) {
227 CallArgs args = CallArgsFromVp(argc, vp);
228 return math_function<math_exp_impl>(cx, args);
231 double js::math_floor_impl(double x) {
232 AutoUnsafeCallWithABI unsafe;
233 return fdlibm::floor(x);
236 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
237 CallArgs args = CallArgsFromVp(argc, vp);
239 if (args.length() == 0) {
240 args.rval().setNaN();
241 return true;
244 double x;
245 if (!ToNumber(cx, args[0], &x)) {
246 return false;
249 args.rval().setNumber(math_floor_impl(x));
250 return true;
253 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
254 MutableHandleValue res) {
255 int32_t a = 0, b = 0;
256 if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
257 return false;
259 if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
260 return false;
263 res.setInt32(WrappingMultiply(a, b));
264 return true;
267 static bool math_imul(JSContext* cx, unsigned argc, Value* vp) {
268 CallArgs args = CallArgsFromVp(argc, vp);
270 return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
273 // Implements Math.fround (20.2.2.16) up to step 3
274 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
275 double d;
276 bool success = ToNumber(cx, v, &d);
277 *out = static_cast<float>(d);
278 return success;
281 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
282 float f;
283 if (!RoundFloat32(cx, arg, &f)) {
284 return false;
287 res.setDouble(static_cast<double>(f));
288 return true;
291 double js::RoundFloat32(double d) {
292 return static_cast<double>(static_cast<float>(d));
295 static bool math_fround(JSContext* cx, unsigned argc, Value* vp) {
296 CallArgs args = CallArgsFromVp(argc, vp);
298 if (args.length() == 0) {
299 args.rval().setNaN();
300 return true;
303 return RoundFloat32(cx, args[0], args.rval());
306 double js::math_log_impl(double x) {
307 AutoUnsafeCallWithABI unsafe;
308 return fdlibm::log(x);
311 static bool math_log(JSContext* cx, unsigned argc, Value* vp) {
312 CallArgs args = CallArgsFromVp(argc, vp);
313 return math_function<math_log_impl>(cx, args);
316 double js::math_max_impl(double x, double y) {
317 AutoUnsafeCallWithABI unsafe;
319 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
320 if (x > y || std::isnan(x) || (x == y && IsNegative(y))) {
321 return x;
323 return y;
326 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
327 CallArgs args = CallArgsFromVp(argc, vp);
329 double maxval = NegativeInfinity<double>();
330 for (unsigned i = 0; i < args.length(); i++) {
331 double x;
332 if (!ToNumber(cx, args[i], &x)) {
333 return false;
335 maxval = math_max_impl(x, maxval);
337 args.rval().setNumber(maxval);
338 return true;
341 double js::math_min_impl(double x, double y) {
342 AutoUnsafeCallWithABI unsafe;
344 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
345 if (x < y || std::isnan(x) || (x == y && IsNegativeZero(x))) {
346 return x;
348 return y;
351 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
352 CallArgs args = CallArgsFromVp(argc, vp);
354 double minval = PositiveInfinity<double>();
355 for (unsigned i = 0; i < args.length(); i++) {
356 double x;
357 if (!ToNumber(cx, args[i], &x)) {
358 return false;
360 minval = math_min_impl(x, minval);
362 args.rval().setNumber(minval);
363 return true;
366 double js::powi(double x, int32_t y) {
367 AutoUnsafeCallWithABI unsafe;
369 // It's only safe to optimize this when we can compute with integer values or
370 // the exponent is a small, positive constant.
371 if (y >= 0) {
372 uint32_t n = uint32_t(y);
374 // NB: Have to take fast-path for n <= 4 to match |MPow::foldsTo|. Otherwise
375 // we risk causing differential testing issues.
376 if (n == 0) {
377 return 1;
379 if (n == 1) {
380 return x;
382 if (n == 2) {
383 return x * x;
385 if (n == 3) {
386 return x * x * x;
388 if (n == 4) {
389 double z = x * x;
390 return z * z;
393 int64_t i;
394 if (NumberEqualsInt64(x, &i)) {
395 // Special-case: |-0 ** odd| is -0.
396 if (i == 0) {
397 return (n & 1) ? x : 0;
400 // Use int64 to cover cases like |Math.pow(2, 53)|.
401 mozilla::CheckedInt64 runningSquare = i;
402 mozilla::CheckedInt64 result = 1;
403 while (true) {
404 if ((n & 1) != 0) {
405 result *= runningSquare;
406 if (!result.isValid()) {
407 break;
411 n >>= 1;
412 if (n == 0) {
413 return static_cast<double>(result.value());
416 runningSquare *= runningSquare;
417 if (!runningSquare.isValid()) {
418 break;
423 // Fall-back to use std::pow to reduce floating point precision errors.
426 return std::pow(x, static_cast<double>(y)); // Avoid pow(double, int).
429 double js::ecmaPow(double x, double y) {
430 AutoUnsafeCallWithABI unsafe;
433 * Use powi if the exponent is an integer-valued double. We don't have to
434 * check for NaN since a comparison with NaN is always false.
436 int32_t yi;
437 if (NumberEqualsInt32(y, &yi)) {
438 return powi(x, yi);
442 * Because C99 and ECMA specify different behavior for pow(),
443 * we need to wrap the libm call to make it ECMA compliant.
445 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
446 return GenericNaN();
449 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
450 if (y == 0) {
451 return 1;
455 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
456 * when x = -0.0, so we have to guard for this.
458 if (std::isfinite(x) && x != 0.0) {
459 if (y == 0.5) {
460 return std::sqrt(x);
462 if (y == -0.5) {
463 return 1.0 / std::sqrt(x);
466 return std::pow(x, y);
469 static bool math_pow(JSContext* cx, unsigned argc, Value* vp) {
470 CallArgs args = CallArgsFromVp(argc, vp);
472 double x;
473 if (!ToNumber(cx, args.get(0), &x)) {
474 return false;
477 double y;
478 if (!ToNumber(cx, args.get(1), &y)) {
479 return false;
482 double z = ecmaPow(x, y);
483 args.rval().setNumber(z);
484 return true;
487 uint64_t js::GenerateRandomSeed() {
488 Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
490 return maybeSeed.valueOrFrom([] {
491 // Use PRMJ_Now() in case we couldn't read random bits from the OS.
492 uint64_t timestamp = PRMJ_Now();
493 return timestamp ^ (timestamp << 32);
497 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
498 // XorShift128PlusRNG must be initialized with a non-zero seed.
499 do {
500 seed[0] = GenerateRandomSeed();
501 seed[1] = GenerateRandomSeed();
502 } while (seed[0] == 0 && seed[1] == 0);
505 mozilla::non_crypto::XorShift128PlusRNG&
506 Realm::getOrCreateRandomNumberGenerator() {
507 if (randomNumberGenerator_.isNothing()) {
508 mozilla::Array<uint64_t, 2> seed;
509 GenerateXorShift128PlusSeed(seed);
510 randomNumberGenerator_.emplace(seed[0], seed[1]);
513 return randomNumberGenerator_.ref();
516 double js::math_random_impl(JSContext* cx) {
517 return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
520 static bool math_random(JSContext* cx, unsigned argc, Value* vp) {
521 CallArgs args = CallArgsFromVp(argc, vp);
522 if (js::SupportDifferentialTesting()) {
523 args.rval().setDouble(0);
524 } else {
525 args.rval().setDouble(math_random_impl(cx));
527 return true;
530 template <typename T>
531 T js::GetBiggestNumberLessThan(T x) {
532 MOZ_ASSERT(!IsNegative(x));
533 MOZ_ASSERT(std::isfinite(x));
534 using Bits = typename mozilla::FloatingPoint<T>::Bits;
535 Bits bits = mozilla::BitwiseCast<Bits>(x);
536 MOZ_ASSERT(bits > 0, "will underflow");
537 return mozilla::BitwiseCast<T>(bits - 1);
540 template double js::GetBiggestNumberLessThan<>(double x);
541 template float js::GetBiggestNumberLessThan<>(float x);
543 double js::math_round_impl(double x) {
544 AutoUnsafeCallWithABI unsafe;
546 int32_t ignored;
547 if (NumberEqualsInt32(x, &ignored)) {
548 return x;
551 /* Some numbers are so big that adding 0.5 would give the wrong number. */
552 if (ExponentComponent(x) >=
553 int_fast16_t(FloatingPoint<double>::kExponentShift)) {
554 return x;
557 double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
558 return std::copysign(fdlibm::floor(x + add), x);
561 float js::math_roundf_impl(float x) {
562 AutoUnsafeCallWithABI unsafe;
564 int32_t ignored;
565 if (NumberEqualsInt32(x, &ignored)) {
566 return x;
569 /* Some numbers are so big that adding 0.5 would give the wrong number. */
570 if (ExponentComponent(x) >=
571 int_fast16_t(FloatingPoint<float>::kExponentShift)) {
572 return x;
575 float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
576 return std::copysign(fdlibm::floorf(x + add), x);
579 /* ES5 15.8.2.15. */
580 static bool math_round(JSContext* cx, unsigned argc, Value* vp) {
581 CallArgs args = CallArgsFromVp(argc, vp);
583 if (args.length() == 0) {
584 args.rval().setNaN();
585 return true;
588 double x;
589 if (!ToNumber(cx, args[0], &x)) {
590 return false;
593 args.rval().setNumber(math_round_impl(x));
594 return true;
597 double js::math_sin_fdlibm_impl(double x) {
598 AutoUnsafeCallWithABI unsafe;
599 return fdlibm::sin(x);
602 double js::math_sin_native_impl(double x) {
603 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
604 AutoUnsafeCallWithABI unsafe;
605 return std::sin(x);
608 static bool math_sin(JSContext* cx, unsigned argc, Value* vp) {
609 CallArgs args = CallArgsFromVp(argc, vp);
610 if (UseFdlibmForSinCosTan(args)) {
611 return math_function<math_sin_fdlibm_impl>(cx, args);
613 return math_function<math_sin_native_impl>(cx, args);
616 double js::math_sqrt_impl(double x) {
617 AutoUnsafeCallWithABI unsafe;
618 return std::sqrt(x);
621 static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
622 CallArgs args = CallArgsFromVp(argc, vp);
623 return math_function<math_sqrt_impl>(cx, args);
626 double js::math_tan_fdlibm_impl(double x) {
627 AutoUnsafeCallWithABI unsafe;
628 return fdlibm::tan(x);
631 double js::math_tan_native_impl(double x) {
632 MOZ_ASSERT(!sUseFdlibmForSinCosTan);
633 AutoUnsafeCallWithABI unsafe;
634 return std::tan(x);
637 static bool math_tan(JSContext* cx, unsigned argc, Value* vp) {
638 CallArgs args = CallArgsFromVp(argc, vp);
639 if (UseFdlibmForSinCosTan(args)) {
640 return math_function<math_tan_fdlibm_impl>(cx, args);
642 return math_function<math_tan_native_impl>(cx, args);
645 double js::math_log10_impl(double x) {
646 AutoUnsafeCallWithABI unsafe;
647 return fdlibm::log10(x);
650 static bool math_log10(JSContext* cx, unsigned argc, Value* vp) {
651 CallArgs args = CallArgsFromVp(argc, vp);
652 return math_function<math_log10_impl>(cx, args);
655 double js::math_log2_impl(double x) {
656 AutoUnsafeCallWithABI unsafe;
657 return fdlibm::log2(x);
660 static bool math_log2(JSContext* cx, unsigned argc, Value* vp) {
661 CallArgs args = CallArgsFromVp(argc, vp);
662 return math_function<math_log2_impl>(cx, args);
665 double js::math_log1p_impl(double x) {
666 AutoUnsafeCallWithABI unsafe;
667 return fdlibm::log1p(x);
670 static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) {
671 CallArgs args = CallArgsFromVp(argc, vp);
672 return math_function<math_log1p_impl>(cx, args);
675 double js::math_expm1_impl(double x) {
676 AutoUnsafeCallWithABI unsafe;
677 return fdlibm::expm1(x);
680 static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) {
681 CallArgs args = CallArgsFromVp(argc, vp);
682 return math_function<math_expm1_impl>(cx, args);
685 double js::math_cosh_impl(double x) {
686 AutoUnsafeCallWithABI unsafe;
687 return fdlibm::cosh(x);
690 static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) {
691 CallArgs args = CallArgsFromVp(argc, vp);
692 return math_function<math_cosh_impl>(cx, args);
695 double js::math_sinh_impl(double x) {
696 AutoUnsafeCallWithABI unsafe;
697 return fdlibm::sinh(x);
700 static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) {
701 CallArgs args = CallArgsFromVp(argc, vp);
702 return math_function<math_sinh_impl>(cx, args);
705 double js::math_tanh_impl(double x) {
706 AutoUnsafeCallWithABI unsafe;
707 return fdlibm::tanh(x);
710 static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) {
711 CallArgs args = CallArgsFromVp(argc, vp);
712 return math_function<math_tanh_impl>(cx, args);
715 double js::math_acosh_impl(double x) {
716 AutoUnsafeCallWithABI unsafe;
717 return fdlibm::acosh(x);
720 static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) {
721 CallArgs args = CallArgsFromVp(argc, vp);
722 return math_function<math_acosh_impl>(cx, args);
725 double js::math_asinh_impl(double x) {
726 AutoUnsafeCallWithABI unsafe;
727 return fdlibm::asinh(x);
730 static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) {
731 CallArgs args = CallArgsFromVp(argc, vp);
732 return math_function<math_asinh_impl>(cx, args);
735 double js::math_atanh_impl(double x) {
736 AutoUnsafeCallWithABI unsafe;
737 return fdlibm::atanh(x);
740 static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) {
741 CallArgs args = CallArgsFromVp(argc, vp);
742 return math_function<math_atanh_impl>(cx, args);
745 double js::ecmaHypot(double x, double y) {
746 AutoUnsafeCallWithABI unsafe;
747 return fdlibm::hypot(x, y);
750 static inline void hypot_step(double& scale, double& sumsq, double x) {
751 double xabs = mozilla::Abs(x);
752 if (scale < xabs) {
753 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
754 scale = xabs;
755 } else if (scale != 0) {
756 sumsq += (xabs / scale) * (xabs / scale);
760 double js::hypot4(double x, double y, double z, double w) {
761 AutoUnsafeCallWithABI unsafe;
763 // Check for infinities or NaNs so that we can return immediately.
764 if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) {
765 return mozilla::PositiveInfinity<double>();
768 if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) {
769 return GenericNaN();
772 double scale = 0;
773 double sumsq = 1;
775 hypot_step(scale, sumsq, x);
776 hypot_step(scale, sumsq, y);
777 hypot_step(scale, sumsq, z);
778 hypot_step(scale, sumsq, w);
780 return scale * std::sqrt(sumsq);
783 double js::hypot3(double x, double y, double z) {
784 AutoUnsafeCallWithABI unsafe;
785 return hypot4(x, y, z, 0.0);
788 static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) {
789 CallArgs args = CallArgsFromVp(argc, vp);
790 return math_hypot_handle(cx, args, args.rval());
793 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
794 MutableHandleValue res) {
795 // IonMonkey calls the ecmaHypot function directly if two arguments are
796 // given. Do that here as well to get the same results.
797 if (args.length() == 2) {
798 double x, y;
799 if (!ToNumber(cx, args[0], &x)) {
800 return false;
802 if (!ToNumber(cx, args[1], &y)) {
803 return false;
806 double result = ecmaHypot(x, y);
807 res.setDouble(result);
808 return true;
811 bool isInfinite = false;
812 bool isNaN = false;
814 double scale = 0;
815 double sumsq = 1;
817 for (unsigned i = 0; i < args.length(); i++) {
818 double x;
819 if (!ToNumber(cx, args[i], &x)) {
820 return false;
823 isInfinite |= std::isinf(x);
824 isNaN |= std::isnan(x);
825 if (isInfinite || isNaN) {
826 continue;
829 hypot_step(scale, sumsq, x);
832 double result = isInfinite ? PositiveInfinity<double>()
833 : isNaN ? GenericNaN()
834 : scale * std::sqrt(sumsq);
835 res.setDouble(result);
836 return true;
839 double js::math_trunc_impl(double x) {
840 AutoUnsafeCallWithABI unsafe;
841 return fdlibm::trunc(x);
844 float js::math_truncf_impl(float x) {
845 AutoUnsafeCallWithABI unsafe;
846 return fdlibm::truncf(x);
849 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
850 CallArgs args = CallArgsFromVp(argc, vp);
851 if (args.length() == 0) {
852 args.rval().setNaN();
853 return true;
856 double x;
857 if (!ToNumber(cx, args[0], &x)) {
858 return false;
861 args.rval().setNumber(math_trunc_impl(x));
862 return true;
865 double js::math_sign_impl(double x) {
866 AutoUnsafeCallWithABI unsafe;
868 if (std::isnan(x)) {
869 return GenericNaN();
872 return x == 0 ? x : x < 0 ? -1 : 1;
875 static bool math_sign(JSContext* cx, unsigned argc, Value* vp) {
876 CallArgs args = CallArgsFromVp(argc, vp);
877 if (args.length() == 0) {
878 args.rval().setNaN();
879 return true;
882 double x;
883 if (!ToNumber(cx, args[0], &x)) {
884 return false;
887 args.rval().setNumber(math_sign_impl(x));
888 return true;
891 double js::math_cbrt_impl(double x) {
892 AutoUnsafeCallWithABI unsafe;
893 return fdlibm::cbrt(x);
896 static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
897 CallArgs args = CallArgsFromVp(argc, vp);
898 return math_function<math_cbrt_impl>(cx, args);
901 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
902 CallArgs args = CallArgsFromVp(argc, vp);
903 args.rval().setString(cx->names().Math);
904 return true;
907 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
908 switch (fun) {
909 case UnaryMathFunction::SinNative:
910 return math_sin_native_impl;
911 case UnaryMathFunction::SinFdlibm:
912 return math_sin_fdlibm_impl;
913 case UnaryMathFunction::CosNative:
914 return math_cos_native_impl;
915 case UnaryMathFunction::CosFdlibm:
916 return math_cos_fdlibm_impl;
917 case UnaryMathFunction::TanNative:
918 return math_tan_native_impl;
919 case UnaryMathFunction::TanFdlibm:
920 return math_tan_fdlibm_impl;
921 case UnaryMathFunction::Log:
922 return math_log_impl;
923 case UnaryMathFunction::Exp:
924 return math_exp_impl;
925 case UnaryMathFunction::ATan:
926 return math_atan_impl;
927 case UnaryMathFunction::ASin:
928 return math_asin_impl;
929 case UnaryMathFunction::ACos:
930 return math_acos_impl;
931 case UnaryMathFunction::Log10:
932 return math_log10_impl;
933 case UnaryMathFunction::Log2:
934 return math_log2_impl;
935 case UnaryMathFunction::Log1P:
936 return math_log1p_impl;
937 case UnaryMathFunction::ExpM1:
938 return math_expm1_impl;
939 case UnaryMathFunction::CosH:
940 return math_cosh_impl;
941 case UnaryMathFunction::SinH:
942 return math_sinh_impl;
943 case UnaryMathFunction::TanH:
944 return math_tanh_impl;
945 case UnaryMathFunction::ACosH:
946 return math_acosh_impl;
947 case UnaryMathFunction::ASinH:
948 return math_asinh_impl;
949 case UnaryMathFunction::ATanH:
950 return math_atanh_impl;
951 case UnaryMathFunction::Trunc:
952 return math_trunc_impl;
953 case UnaryMathFunction::Cbrt:
954 return math_cbrt_impl;
955 case UnaryMathFunction::Floor:
956 return math_floor_impl;
957 case UnaryMathFunction::Ceil:
958 return math_ceil_impl;
959 case UnaryMathFunction::Round:
960 return math_round_impl;
962 MOZ_CRASH("Unknown function");
965 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun) {
966 switch (fun) {
967 case UnaryMathFunction::SinNative:
968 return "Sin (native)";
969 case UnaryMathFunction::SinFdlibm:
970 return "Sin (fdlibm)";
971 case UnaryMathFunction::CosNative:
972 return "Cos (native)";
973 case UnaryMathFunction::CosFdlibm:
974 return "Cos (fdlibm)";
975 case UnaryMathFunction::TanNative:
976 return "Tan (native)";
977 case UnaryMathFunction::TanFdlibm:
978 return "Tan (fdlibm)";
979 case UnaryMathFunction::Log:
980 return "Log";
981 case UnaryMathFunction::Exp:
982 return "Exp";
983 case UnaryMathFunction::ACos:
984 return "ACos";
985 case UnaryMathFunction::ASin:
986 return "ASin";
987 case UnaryMathFunction::ATan:
988 return "ATan";
989 case UnaryMathFunction::Log10:
990 return "Log10";
991 case UnaryMathFunction::Log2:
992 return "Log2";
993 case UnaryMathFunction::Log1P:
994 return "Log1P";
995 case UnaryMathFunction::ExpM1:
996 return "ExpM1";
997 case UnaryMathFunction::CosH:
998 return "CosH";
999 case UnaryMathFunction::SinH:
1000 return "SinH";
1001 case UnaryMathFunction::TanH:
1002 return "TanH";
1003 case UnaryMathFunction::ACosH:
1004 return "ACosH";
1005 case UnaryMathFunction::ASinH:
1006 return "ASinH";
1007 case UnaryMathFunction::ATanH:
1008 return "ATanH";
1009 case UnaryMathFunction::Trunc:
1010 return "Trunc";
1011 case UnaryMathFunction::Cbrt:
1012 return "Cbrt";
1013 case UnaryMathFunction::Floor:
1014 return "Floor";
1015 case UnaryMathFunction::Ceil:
1016 return "Ceil";
1017 case UnaryMathFunction::Round:
1018 return "Round";
1020 MOZ_CRASH("Unknown function");
1023 static const JSFunctionSpec math_static_methods[] = {
1024 JS_FN(js_toSource_str, math_toSource, 0, 0),
1025 JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
1026 JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
1027 JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
1028 JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
1029 JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
1030 JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
1031 JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
1032 JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
1033 JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
1034 JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
1035 JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
1036 JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
1037 JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
1038 JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
1039 JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
1040 JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
1041 JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
1042 JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
1043 JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
1044 JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
1045 JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
1046 JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
1047 JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
1048 JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
1049 JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
1050 JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
1051 JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
1052 JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
1053 JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
1054 JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
1055 JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
1056 JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
1057 JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
1058 JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
1059 JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
1060 JS_FS_END};
1062 static const JSPropertySpec math_static_properties[] = {
1063 JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT),
1064 JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT),
1065 JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT),
1066 JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT),
1067 JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT),
1068 JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT),
1069 JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT),
1070 JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT),
1072 JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY),
1073 JS_PS_END};
1075 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
1076 RootedObject proto(cx, &cx->global()->getObjectPrototype());
1077 return NewTenuredObjectWithGivenProto(cx, &MathClass, proto);
1080 static const ClassSpec MathClassSpec = {CreateMathObject,
1081 nullptr,
1082 math_static_methods,
1083 math_static_properties,
1084 nullptr,
1085 nullptr,
1086 nullptr};
1088 const JSClass js::MathClass = {js_Math_str,
1089 JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
1090 JS_NULL_CLASS_OPS, &MathClassSpec};