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/. */
6 
7 /*
8  * JS math package.
9  */
10 
11 #include "jsmath.h"
12 
13 #include "mozilla/FloatingPoint.h"
14 #include "mozilla/MathAlgorithms.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/RandomNum.h"
17 #include "mozilla/WrappingOperations.h"
18 
19 #include <cmath>
20 
21 #include "fdlibm.h"
22 #include "jsapi.h"
23 #include "jstypes.h"
24 
25 #include "jit/InlinableNatives.h"
26 #include "js/Class.h"
27 #include "js/PropertySpec.h"
28 #include "util/Windows.h"
29 #include "vm/JSAtom.h"
30 #include "vm/JSContext.h"
31 #include "vm/Realm.h"
32 #include "vm/Time.h"
33 #include "vm/WellKnownAtom.h"  // js_*_str
34 
35 #include "vm/JSObject-inl.h"
36 
37 using namespace js;
38 
39 using JS::GenericNaN;
40 using JS::ToNumber;
41 using mozilla::Abs;
42 using mozilla::ExponentComponent;
43 using mozilla::FloatingPoint;
44 using mozilla::IsFinite;
45 using mozilla::IsInfinite;
46 using mozilla::IsNaN;
47 using mozilla::IsNegative;
48 using mozilla::IsNegativeZero;
49 using mozilla::Maybe;
50 using mozilla::NegativeInfinity;
51 using mozilla::NumberEqualsInt32;
52 using mozilla::PositiveInfinity;
53 using mozilla::WrappingMultiply;
54 
55 static mozilla::Atomic<bool, mozilla::Relaxed> sUseFdlibmForSinCosTan;
56 
SetUseFdlibmForSinCosTan(bool value)57 JS_PUBLIC_API void JS::SetUseFdlibmForSinCosTan(bool value) {
58   sUseFdlibmForSinCosTan = value;
59 }
60 
61 template <UnaryMathFunctionType F>
math_function(JSContext * cx,HandleValue val,MutableHandleValue res)62 static bool math_function(JSContext* cx, HandleValue val,
63                           MutableHandleValue res) {
64   double x;
65   if (!ToNumber(cx, val, &x)) {
66     return false;
67   }
68 
69   // NB: Always stored as a double so the math function can be inlined
70   // through MMathFunction. We also rely on this to avoid type monitoring
71   // in CallIRGenerator::tryAttachMathSqrt.
72   double z = F(x);
73   res.setDouble(z);
74   return true;
75 }
76 
77 template <UnaryMathFunctionType F>
math_function(JSContext * cx,unsigned argc,Value * vp)78 static bool math_function(JSContext* cx, unsigned argc, Value* vp) {
79   CallArgs args = CallArgsFromVp(argc, vp);
80   if (args.length() == 0) {
81     args.rval().setNaN();
82     return true;
83   }
84 
85   return math_function<F>(cx, args[0], args.rval());
86 }
87 
math_abs_handle(JSContext * cx,js::HandleValue v,js::MutableHandleValue r)88 bool js::math_abs_handle(JSContext* cx, js::HandleValue v,
89                          js::MutableHandleValue r) {
90   double x;
91   if (!ToNumber(cx, v, &x)) {
92     return false;
93   }
94 
95   double z = Abs(x);
96   r.setNumber(z);
97 
98   return true;
99 }
100 
math_abs(JSContext * cx,unsigned argc,Value * vp)101 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
102   CallArgs args = CallArgsFromVp(argc, vp);
103 
104   if (args.length() == 0) {
105     args.rval().setNaN();
106     return true;
107   }
108 
109   return math_abs_handle(cx, args[0], args.rval());
110 }
111 
math_acos_impl(double x)112 double js::math_acos_impl(double x) {
113   AutoUnsafeCallWithABI unsafe;
114   return fdlibm::acos(x);
115 }
116 
math_acos(JSContext * cx,unsigned argc,Value * vp)117 bool js::math_acos(JSContext* cx, unsigned argc, Value* vp) {
118   return math_function<math_acos_impl>(cx, argc, vp);
119 }
120 
math_asin_impl(double x)121 double js::math_asin_impl(double x) {
122   AutoUnsafeCallWithABI unsafe;
123   return fdlibm::asin(x);
124 }
125 
math_asin(JSContext * cx,unsigned argc,Value * vp)126 bool js::math_asin(JSContext* cx, unsigned argc, Value* vp) {
127   return math_function<math_asin_impl>(cx, argc, vp);
128 }
129 
math_atan_impl(double x)130 double js::math_atan_impl(double x) {
131   AutoUnsafeCallWithABI unsafe;
132   return fdlibm::atan(x);
133 }
134 
math_atan(JSContext * cx,unsigned argc,Value * vp)135 bool js::math_atan(JSContext* cx, unsigned argc, Value* vp) {
136   return math_function<math_atan_impl>(cx, argc, vp);
137 }
138 
ecmaAtan2(double y,double x)139 double js::ecmaAtan2(double y, double x) {
140   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
141   return fdlibm::atan2(y, x);
142 }
143 
math_atan2_handle(JSContext * cx,HandleValue y,HandleValue x,MutableHandleValue res)144 bool js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x,
145                            MutableHandleValue res) {
146   double dy;
147   if (!ToNumber(cx, y, &dy)) {
148     return false;
149   }
150 
151   double dx;
152   if (!ToNumber(cx, x, &dx)) {
153     return false;
154   }
155 
156   double z = ecmaAtan2(dy, dx);
157   res.setDouble(z);
158   return true;
159 }
160 
math_atan2(JSContext * cx,unsigned argc,Value * vp)161 bool js::math_atan2(JSContext* cx, unsigned argc, Value* vp) {
162   CallArgs args = CallArgsFromVp(argc, vp);
163 
164   return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
165 }
166 
math_ceil_impl(double x)167 double js::math_ceil_impl(double x) {
168   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
169   return fdlibm::ceil(x);
170 }
171 
math_ceil_handle(JSContext * cx,HandleValue v,MutableHandleValue res)172 bool js::math_ceil_handle(JSContext* cx, HandleValue v,
173                           MutableHandleValue res) {
174   double d;
175   if (!ToNumber(cx, v, &d)) return false;
176 
177   double result = math_ceil_impl(d);
178   res.setNumber(result);
179   return true;
180 }
181 
math_ceil(JSContext * cx,unsigned argc,Value * vp)182 bool js::math_ceil(JSContext* cx, unsigned argc, Value* vp) {
183   CallArgs args = CallArgsFromVp(argc, vp);
184 
185   if (args.length() == 0) {
186     args.rval().setNaN();
187     return true;
188   }
189 
190   return math_ceil_handle(cx, args[0], args.rval());
191 }
192 
math_clz32(JSContext * cx,unsigned argc,Value * vp)193 bool js::math_clz32(JSContext* cx, unsigned argc, Value* vp) {
194   CallArgs args = CallArgsFromVp(argc, vp);
195 
196   if (args.length() == 0) {
197     args.rval().setInt32(32);
198     return true;
199   }
200 
201   uint32_t n;
202   if (!ToUint32(cx, args[0], &n)) {
203     return false;
204   }
205 
206   if (n == 0) {
207     args.rval().setInt32(32);
208     return true;
209   }
210 
211   args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
212   return true;
213 }
214 
math_use_fdlibm_for_sin_cos_tan()215 bool js::math_use_fdlibm_for_sin_cos_tan() { return sUseFdlibmForSinCosTan; }
216 
math_cos_fdlibm_impl(double x)217 double js::math_cos_fdlibm_impl(double x) {
218   MOZ_ASSERT(sUseFdlibmForSinCosTan);
219   AutoUnsafeCallWithABI unsafe;
220   return fdlibm::cos(x);
221 }
222 
math_cos_native_impl(double x)223 double js::math_cos_native_impl(double x) {
224   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
225   AutoUnsafeCallWithABI unsafe;
226   return cos(x);
227 }
228 
math_cos(JSContext * cx,unsigned argc,Value * vp)229 bool js::math_cos(JSContext* cx, unsigned argc, Value* vp) {
230   if (sUseFdlibmForSinCosTan) {
231     return math_function<math_cos_fdlibm_impl>(cx, argc, vp);
232   }
233   return math_function<math_cos_native_impl>(cx, argc, vp);
234 }
235 
math_exp_impl(double x)236 double js::math_exp_impl(double x) {
237   AutoUnsafeCallWithABI unsafe;
238   return fdlibm::exp(x);
239 }
240 
math_exp(JSContext * cx,unsigned argc,Value * vp)241 bool js::math_exp(JSContext* cx, unsigned argc, Value* vp) {
242   return math_function<math_exp_impl>(cx, argc, vp);
243 }
244 
math_floor_impl(double x)245 double js::math_floor_impl(double x) {
246   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
247   return fdlibm::floor(x);
248 }
249 
math_floor_handle(JSContext * cx,HandleValue v,MutableHandleValue r)250 bool js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
251   double d;
252   if (!ToNumber(cx, v, &d)) {
253     return false;
254   }
255 
256   double z = math_floor_impl(d);
257   r.setNumber(z);
258 
259   return true;
260 }
261 
math_floor(JSContext * cx,unsigned argc,Value * vp)262 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
263   CallArgs args = CallArgsFromVp(argc, vp);
264 
265   if (args.length() == 0) {
266     args.rval().setNaN();
267     return true;
268   }
269 
270   return math_floor_handle(cx, args[0], args.rval());
271 }
272 
math_imul_handle(JSContext * cx,HandleValue lhs,HandleValue rhs,MutableHandleValue res)273 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
274                           MutableHandleValue res) {
275   int32_t a = 0, b = 0;
276   if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
277     return false;
278   }
279   if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
280     return false;
281   }
282 
283   res.setInt32(WrappingMultiply(a, b));
284   return true;
285 }
286 
math_imul(JSContext * cx,unsigned argc,Value * vp)287 bool js::math_imul(JSContext* cx, unsigned argc, Value* vp) {
288   CallArgs args = CallArgsFromVp(argc, vp);
289 
290   return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
291 }
292 
293 // Implements Math.fround (20.2.2.16) up to step 3
RoundFloat32(JSContext * cx,HandleValue v,float * out)294 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
295   double d;
296   bool success = ToNumber(cx, v, &d);
297   *out = static_cast<float>(d);
298   return success;
299 }
300 
RoundFloat32(JSContext * cx,HandleValue arg,MutableHandleValue res)301 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
302   float f;
303   if (!RoundFloat32(cx, arg, &f)) {
304     return false;
305   }
306 
307   res.setDouble(static_cast<double>(f));
308   return true;
309 }
310 
math_fround(JSContext * cx,unsigned argc,Value * vp)311 bool js::math_fround(JSContext* cx, unsigned argc, Value* vp) {
312   CallArgs args = CallArgsFromVp(argc, vp);
313 
314   if (args.length() == 0) {
315     args.rval().setNaN();
316     return true;
317   }
318 
319   return RoundFloat32(cx, args[0], args.rval());
320 }
321 
math_log_impl(double x)322 double js::math_log_impl(double x) {
323   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
324   return fdlibm::log(x);
325 }
326 
math_log_handle(JSContext * cx,HandleValue val,MutableHandleValue res)327 bool js::math_log_handle(JSContext* cx, HandleValue val,
328                          MutableHandleValue res) {
329   return math_function<math_log_impl>(cx, val, res);
330 }
331 
math_log(JSContext * cx,unsigned argc,Value * vp)332 bool js::math_log(JSContext* cx, unsigned argc, Value* vp) {
333   return math_function<math_log_impl>(cx, argc, vp);
334 }
335 
math_max_impl(double x,double y)336 double js::math_max_impl(double x, double y) {
337   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
338 
339   // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
340   if (x > y || IsNaN(x) || (x == y && IsNegative(y))) {
341     return x;
342   }
343   return y;
344 }
345 
math_max(JSContext * cx,unsigned argc,Value * vp)346 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
347   CallArgs args = CallArgsFromVp(argc, vp);
348 
349   double maxval = NegativeInfinity<double>();
350   for (unsigned i = 0; i < args.length(); i++) {
351     double x;
352     if (!ToNumber(cx, args[i], &x)) {
353       return false;
354     }
355     maxval = math_max_impl(x, maxval);
356   }
357   args.rval().setNumber(maxval);
358   return true;
359 }
360 
math_min_impl(double x,double y)361 double js::math_min_impl(double x, double y) {
362   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
363 
364   // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
365   if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x))) {
366     return x;
367   }
368   return y;
369 }
370 
math_min(JSContext * cx,unsigned argc,Value * vp)371 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
372   CallArgs args = CallArgsFromVp(argc, vp);
373 
374   double minval = PositiveInfinity<double>();
375   for (unsigned i = 0; i < args.length(); i++) {
376     double x;
377     if (!ToNumber(cx, args[i], &x)) {
378       return false;
379     }
380     minval = math_min_impl(x, minval);
381   }
382   args.rval().setNumber(minval);
383   return true;
384 }
385 
minmax_impl(JSContext * cx,bool max,HandleValue a,HandleValue b,MutableHandleValue res)386 bool js::minmax_impl(JSContext* cx, bool max, HandleValue a, HandleValue b,
387                      MutableHandleValue res) {
388   double x, y;
389 
390   if (!ToNumber(cx, a, &x)) {
391     return false;
392   }
393   if (!ToNumber(cx, b, &y)) {
394     return false;
395   }
396 
397   if (max) {
398     res.setNumber(math_max_impl(x, y));
399   } else {
400     res.setNumber(math_min_impl(x, y));
401   }
402 
403   return true;
404 }
405 
powi(double x,int32_t y)406 double js::powi(double x, int32_t y) {
407   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
408   uint32_t n = Abs(y);
409   double m = x;
410   double p = 1;
411   while (true) {
412     if ((n & 1) != 0) p *= m;
413     n >>= 1;
414     if (n == 0) {
415       if (y < 0) {
416         // Unfortunately, we have to be careful when p has reached
417         // infinity in the computation, because sometimes the higher
418         // internal precision in the pow() implementation would have
419         // given us a finite p. This happens very rarely.
420 
421         double result = 1.0 / p;
422         return (result == 0 && IsInfinite(p))
423                    ? pow(x, static_cast<double>(y))  // Avoid pow(double, int).
424                    : result;
425       }
426 
427       return p;
428     }
429     m *= m;
430   }
431 }
432 
ecmaPow(double x,double y)433 double js::ecmaPow(double x, double y) {
434   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
435 
436   /*
437    * Use powi if the exponent is an integer-valued double. We don't have to
438    * check for NaN since a comparison with NaN is always false.
439    */
440   int32_t yi;
441   if (NumberEqualsInt32(y, &yi)) {
442     return powi(x, yi);
443   }
444 
445   /*
446    * Because C99 and ECMA specify different behavior for pow(),
447    * we need to wrap the libm call to make it ECMA compliant.
448    */
449   if (!IsFinite(y) && (x == 1.0 || x == -1.0)) {
450     return GenericNaN();
451   }
452 
453   /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
454   if (y == 0) {
455     return 1;
456   }
457 
458   /*
459    * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
460    * when x = -0.0, so we have to guard for this.
461    */
462   if (IsFinite(x) && x != 0.0) {
463     if (y == 0.5) {
464       return sqrt(x);
465     }
466     if (y == -0.5) {
467       return 1.0 / sqrt(x);
468     }
469   }
470   return pow(x, y);
471 }
472 
math_pow(JSContext * cx,unsigned argc,Value * vp)473 bool js::math_pow(JSContext* cx, unsigned argc, Value* vp) {
474   CallArgs args = CallArgsFromVp(argc, vp);
475 
476   double x;
477   if (!ToNumber(cx, args.get(0), &x)) {
478     return false;
479   }
480 
481   double y;
482   if (!ToNumber(cx, args.get(1), &y)) {
483     return false;
484   }
485 
486   double z = ecmaPow(x, y);
487   args.rval().setNumber(z);
488   return true;
489 }
490 
GenerateRandomSeed()491 uint64_t js::GenerateRandomSeed() {
492   Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
493 
494   return maybeSeed.valueOrFrom([] {
495     // Use PRMJ_Now() in case we couldn't read random bits from the OS.
496     uint64_t timestamp = PRMJ_Now();
497     return timestamp ^ (timestamp << 32);
498   });
499 }
500 
GenerateXorShift128PlusSeed(mozilla::Array<uint64_t,2> & seed)501 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
502   // XorShift128PlusRNG must be initialized with a non-zero seed.
503   do {
504     seed[0] = GenerateRandomSeed();
505     seed[1] = GenerateRandomSeed();
506   } while (seed[0] == 0 && seed[1] == 0);
507 }
508 
509 mozilla::non_crypto::XorShift128PlusRNG&
getOrCreateRandomNumberGenerator()510 Realm::getOrCreateRandomNumberGenerator() {
511   if (randomNumberGenerator_.isNothing()) {
512     mozilla::Array<uint64_t, 2> seed;
513     GenerateXorShift128PlusSeed(seed);
514     randomNumberGenerator_.emplace(seed[0], seed[1]);
515   }
516 
517   return randomNumberGenerator_.ref();
518 }
519 
math_random_impl(JSContext * cx)520 double js::math_random_impl(JSContext* cx) {
521   return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
522 }
523 
math_random(JSContext * cx,unsigned argc,Value * vp)524 bool js::math_random(JSContext* cx, unsigned argc, Value* vp) {
525   CallArgs args = CallArgsFromVp(argc, vp);
526   args.rval().setDouble(math_random_impl(cx));
527   return true;
528 }
529 
math_round_handle(JSContext * cx,HandleValue arg,MutableHandleValue res)530 bool js::math_round_handle(JSContext* cx, HandleValue arg,
531                            MutableHandleValue res) {
532   double d;
533   if (!ToNumber(cx, arg, &d)) {
534     return false;
535   }
536 
537   d = math_round_impl(d);
538   res.setNumber(d);
539   return true;
540 }
541 
542 template <typename T>
GetBiggestNumberLessThan(T x)543 T js::GetBiggestNumberLessThan(T x) {
544   MOZ_ASSERT(!IsNegative(x));
545   MOZ_ASSERT(IsFinite(x));
546   using Bits = typename mozilla::FloatingPoint<T>::Bits;
547   Bits bits = mozilla::BitwiseCast<Bits>(x);
548   MOZ_ASSERT(bits > 0, "will underflow");
549   return mozilla::BitwiseCast<T>(bits - 1);
550 }
551 
552 template double js::GetBiggestNumberLessThan<>(double x);
553 template float js::GetBiggestNumberLessThan<>(float x);
554 
math_round_impl(double x)555 double js::math_round_impl(double x) {
556   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
557 
558   int32_t ignored;
559   if (NumberEqualsInt32(x, &ignored)) {
560     return x;
561   }
562 
563   /* Some numbers are so big that adding 0.5 would give the wrong number. */
564   if (ExponentComponent(x) >=
565       int_fast16_t(FloatingPoint<double>::kExponentShift)) {
566     return x;
567   }
568 
569   double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
570   return std::copysign(fdlibm::floor(x + add), x);
571 }
572 
math_roundf_impl(float x)573 float js::math_roundf_impl(float x) {
574   AutoUnsafeCallWithABI unsafe;
575 
576   int32_t ignored;
577   if (NumberEqualsInt32(x, &ignored)) {
578     return x;
579   }
580 
581   /* Some numbers are so big that adding 0.5 would give the wrong number. */
582   if (ExponentComponent(x) >=
583       int_fast16_t(FloatingPoint<float>::kExponentShift)) {
584     return x;
585   }
586 
587   float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
588   return std::copysign(fdlibm::floorf(x + add), x);
589 }
590 
591 bool /* ES5 15.8.2.15. */
math_round(JSContext * cx,unsigned argc,Value * vp)592 js::math_round(JSContext* cx, unsigned argc, Value* vp) {
593   CallArgs args = CallArgsFromVp(argc, vp);
594 
595   if (args.length() == 0) {
596     args.rval().setNaN();
597     return true;
598   }
599 
600   return math_round_handle(cx, args[0], args.rval());
601 }
602 
math_sin_fdlibm_impl(double x)603 double js::math_sin_fdlibm_impl(double x) {
604   MOZ_ASSERT(sUseFdlibmForSinCosTan);
605   AutoUnsafeCallWithABI unsafe;
606   return fdlibm::sin(x);
607 }
608 
math_sin_native_impl(double x)609 double js::math_sin_native_impl(double x) {
610   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
611   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
612   return sin(x);
613 }
614 
math_sin_handle(JSContext * cx,HandleValue val,MutableHandleValue res)615 bool js::math_sin_handle(JSContext* cx, HandleValue val,
616                          MutableHandleValue res) {
617   if (sUseFdlibmForSinCosTan) {
618     return math_function<math_sin_fdlibm_impl>(cx, val, res);
619   }
620   return math_function<math_sin_native_impl>(cx, val, res);
621 }
622 
math_sin(JSContext * cx,unsigned argc,Value * vp)623 bool js::math_sin(JSContext* cx, unsigned argc, Value* vp) {
624   if (sUseFdlibmForSinCosTan) {
625     return math_function<math_sin_fdlibm_impl>(cx, argc, vp);
626   }
627   return math_function<math_sin_native_impl>(cx, argc, vp);
628 }
629 
math_sqrt_impl(double x)630 double js::math_sqrt_impl(double x) {
631   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
632   return sqrt(x);
633 }
634 
math_sqrt_handle(JSContext * cx,HandleValue number,MutableHandleValue result)635 bool js::math_sqrt_handle(JSContext* cx, HandleValue number,
636                           MutableHandleValue result) {
637   return math_function<math_sqrt_impl>(cx, number, result);
638 }
639 
math_sqrt(JSContext * cx,unsigned argc,Value * vp)640 bool js::math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
641   return math_function<math_sqrt_impl>(cx, argc, vp);
642 }
643 
math_tan_fdlibm_impl(double x)644 double js::math_tan_fdlibm_impl(double x) {
645   MOZ_ASSERT(sUseFdlibmForSinCosTan);
646   AutoUnsafeCallWithABI unsafe;
647   return fdlibm::tan(x);
648 }
649 
math_tan_native_impl(double x)650 double js::math_tan_native_impl(double x) {
651   MOZ_ASSERT(!sUseFdlibmForSinCosTan);
652   AutoUnsafeCallWithABI unsafe;
653   return tan(x);
654 }
655 
math_tan(JSContext * cx,unsigned argc,Value * vp)656 bool js::math_tan(JSContext* cx, unsigned argc, Value* vp) {
657   if (sUseFdlibmForSinCosTan) {
658     return math_function<math_tan_fdlibm_impl>(cx, argc, vp);
659   }
660   return math_function<math_tan_native_impl>(cx, argc, vp);
661 }
662 
math_log10_impl(double x)663 double js::math_log10_impl(double x) {
664   AutoUnsafeCallWithABI unsafe;
665   return fdlibm::log10(x);
666 }
667 
math_log10(JSContext * cx,unsigned argc,Value * vp)668 bool js::math_log10(JSContext* cx, unsigned argc, Value* vp) {
669   return math_function<math_log10_impl>(cx, argc, vp);
670 }
671 
math_log2_impl(double x)672 double js::math_log2_impl(double x) {
673   AutoUnsafeCallWithABI unsafe;
674   return fdlibm::log2(x);
675 }
676 
math_log2(JSContext * cx,unsigned argc,Value * vp)677 bool js::math_log2(JSContext* cx, unsigned argc, Value* vp) {
678   return math_function<math_log2_impl>(cx, argc, vp);
679 }
680 
math_log1p_impl(double x)681 double js::math_log1p_impl(double x) {
682   AutoUnsafeCallWithABI unsafe;
683   return fdlibm::log1p(x);
684 }
685 
math_log1p(JSContext * cx,unsigned argc,Value * vp)686 bool js::math_log1p(JSContext* cx, unsigned argc, Value* vp) {
687   return math_function<math_log1p_impl>(cx, argc, vp);
688 }
689 
math_expm1_impl(double x)690 double js::math_expm1_impl(double x) {
691   AutoUnsafeCallWithABI unsafe;
692   return fdlibm::expm1(x);
693 }
694 
math_expm1(JSContext * cx,unsigned argc,Value * vp)695 bool js::math_expm1(JSContext* cx, unsigned argc, Value* vp) {
696   return math_function<math_expm1_impl>(cx, argc, vp);
697 }
698 
math_cosh_impl(double x)699 double js::math_cosh_impl(double x) {
700   AutoUnsafeCallWithABI unsafe;
701   return fdlibm::cosh(x);
702 }
703 
math_cosh(JSContext * cx,unsigned argc,Value * vp)704 bool js::math_cosh(JSContext* cx, unsigned argc, Value* vp) {
705   return math_function<math_cosh_impl>(cx, argc, vp);
706 }
707 
math_sinh_impl(double x)708 double js::math_sinh_impl(double x) {
709   AutoUnsafeCallWithABI unsafe;
710   return fdlibm::sinh(x);
711 }
712 
math_sinh(JSContext * cx,unsigned argc,Value * vp)713 bool js::math_sinh(JSContext* cx, unsigned argc, Value* vp) {
714   return math_function<math_sinh_impl>(cx, argc, vp);
715 }
716 
math_tanh_impl(double x)717 double js::math_tanh_impl(double x) {
718   AutoUnsafeCallWithABI unsafe;
719   return fdlibm::tanh(x);
720 }
721 
math_tanh(JSContext * cx,unsigned argc,Value * vp)722 bool js::math_tanh(JSContext* cx, unsigned argc, Value* vp) {
723   return math_function<math_tanh_impl>(cx, argc, vp);
724 }
725 
math_acosh_impl(double x)726 double js::math_acosh_impl(double x) {
727   AutoUnsafeCallWithABI unsafe;
728   return fdlibm::acosh(x);
729 }
730 
math_acosh(JSContext * cx,unsigned argc,Value * vp)731 bool js::math_acosh(JSContext* cx, unsigned argc, Value* vp) {
732   return math_function<math_acosh_impl>(cx, argc, vp);
733 }
734 
math_asinh_impl(double x)735 double js::math_asinh_impl(double x) {
736   AutoUnsafeCallWithABI unsafe;
737   return fdlibm::asinh(x);
738 }
739 
math_asinh(JSContext * cx,unsigned argc,Value * vp)740 bool js::math_asinh(JSContext* cx, unsigned argc, Value* vp) {
741   return math_function<math_asinh_impl>(cx, argc, vp);
742 }
743 
math_atanh_impl(double x)744 double js::math_atanh_impl(double x) {
745   AutoUnsafeCallWithABI unsafe;
746   return fdlibm::atanh(x);
747 }
748 
math_atanh(JSContext * cx,unsigned argc,Value * vp)749 bool js::math_atanh(JSContext* cx, unsigned argc, Value* vp) {
750   return math_function<math_atanh_impl>(cx, argc, vp);
751 }
752 
ecmaHypot(double x,double y)753 double js::ecmaHypot(double x, double y) {
754   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
755   return fdlibm::hypot(x, y);
756 }
757 
hypot_step(double & scale,double & sumsq,double x)758 static inline void hypot_step(double& scale, double& sumsq, double x) {
759   double xabs = mozilla::Abs(x);
760   if (scale < xabs) {
761     sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
762     scale = xabs;
763   } else if (scale != 0) {
764     sumsq += (xabs / scale) * (xabs / scale);
765   }
766 }
767 
hypot4(double x,double y,double z,double w)768 double js::hypot4(double x, double y, double z, double w) {
769   AutoUnsafeCallWithABI unsafe;
770 
771   // Check for infinities or NaNs so that we can return immediately.
772   if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) ||
773       mozilla::IsInfinite(z) || mozilla::IsInfinite(w)) {
774     return mozilla::PositiveInfinity<double>();
775   }
776 
777   if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) ||
778       mozilla::IsNaN(w)) {
779     return GenericNaN();
780   }
781 
782   double scale = 0;
783   double sumsq = 1;
784 
785   hypot_step(scale, sumsq, x);
786   hypot_step(scale, sumsq, y);
787   hypot_step(scale, sumsq, z);
788   hypot_step(scale, sumsq, w);
789 
790   return scale * sqrt(sumsq);
791 }
792 
hypot3(double x,double y,double z)793 double js::hypot3(double x, double y, double z) {
794   AutoUnsafeCallWithABI unsafe;
795   return hypot4(x, y, z, 0.0);
796 }
797 
math_hypot(JSContext * cx,unsigned argc,Value * vp)798 bool js::math_hypot(JSContext* cx, unsigned argc, Value* vp) {
799   CallArgs args = CallArgsFromVp(argc, vp);
800   return math_hypot_handle(cx, args, args.rval());
801 }
802 
math_hypot_handle(JSContext * cx,HandleValueArray args,MutableHandleValue res)803 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
804                            MutableHandleValue res) {
805   // IonMonkey calls the ecmaHypot function directly if two arguments are
806   // given. Do that here as well to get the same results.
807   if (args.length() == 2) {
808     double x, y;
809     if (!ToNumber(cx, args[0], &x)) {
810       return false;
811     }
812     if (!ToNumber(cx, args[1], &y)) {
813       return false;
814     }
815 
816     double result = ecmaHypot(x, y);
817     res.setDouble(result);
818     return true;
819   }
820 
821   bool isInfinite = false;
822   bool isNaN = false;
823 
824   double scale = 0;
825   double sumsq = 1;
826 
827   for (unsigned i = 0; i < args.length(); i++) {
828     double x;
829     if (!ToNumber(cx, args[i], &x)) {
830       return false;
831     }
832 
833     isInfinite |= mozilla::IsInfinite(x);
834     isNaN |= mozilla::IsNaN(x);
835     if (isInfinite || isNaN) {
836       continue;
837     }
838 
839     hypot_step(scale, sumsq, x);
840   }
841 
842   double result = isInfinite ? PositiveInfinity<double>()
843                   : isNaN    ? GenericNaN()
844                              : scale * sqrt(sumsq);
845   res.setDouble(result);
846   return true;
847 }
848 
math_trunc_impl(double x)849 double js::math_trunc_impl(double x) {
850   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
851   return fdlibm::trunc(x);
852 }
853 
math_truncf_impl(float x)854 float js::math_truncf_impl(float x) {
855   AutoUnsafeCallWithABI unsafe;
856   return fdlibm::truncf(x);
857 }
858 
math_trunc_handle(JSContext * cx,HandleValue v,MutableHandleValue r)859 bool js::math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
860   double x;
861   if (!ToNumber(cx, v, &x)) {
862     return false;
863   }
864 
865   r.setNumber(math_trunc_impl(x));
866   return true;
867 }
868 
math_trunc(JSContext * cx,unsigned argc,Value * vp)869 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
870   CallArgs args = CallArgsFromVp(argc, vp);
871   if (args.length() == 0) {
872     args.rval().setNaN();
873     return true;
874   }
875 
876   return math_trunc_handle(cx, args[0], args.rval());
877 }
878 
math_sign_impl(double x)879 double js::math_sign_impl(double x) {
880   AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
881 
882   if (mozilla::IsNaN(x)) {
883     return GenericNaN();
884   }
885 
886   return x == 0 ? x : x < 0 ? -1 : 1;
887 }
888 
math_sign_handle(JSContext * cx,HandleValue v,MutableHandleValue r)889 bool js::math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
890   double x;
891   if (!ToNumber(cx, v, &x)) {
892     return false;
893   }
894 
895   r.setNumber(math_sign_impl(x));
896   return true;
897 }
898 
math_sign(JSContext * cx,unsigned argc,Value * vp)899 bool js::math_sign(JSContext* cx, unsigned argc, Value* vp) {
900   CallArgs args = CallArgsFromVp(argc, vp);
901   if (args.length() == 0) {
902     args.rval().setNaN();
903     return true;
904   }
905 
906   return math_sign_handle(cx, args[0], args.rval());
907 }
908 
math_cbrt_impl(double x)909 double js::math_cbrt_impl(double x) {
910   AutoUnsafeCallWithABI unsafe;
911   return fdlibm::cbrt(x);
912 }
913 
math_cbrt(JSContext * cx,unsigned argc,Value * vp)914 bool js::math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
915   return math_function<math_cbrt_impl>(cx, argc, vp);
916 }
917 
math_toSource(JSContext * cx,unsigned argc,Value * vp)918 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
919   CallArgs args = CallArgsFromVp(argc, vp);
920   args.rval().setString(cx->names().Math);
921   return true;
922 }
923 
GetUnaryMathFunctionPtr(UnaryMathFunction fun)924 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
925   switch (fun) {
926     case UnaryMathFunction::Log:
927       return math_log_impl;
928     case UnaryMathFunction::Sin:
929       if (sUseFdlibmForSinCosTan) {
930         return math_sin_fdlibm_impl;
931       }
932       return math_sin_native_impl;
933     case UnaryMathFunction::Cos:
934       if (sUseFdlibmForSinCosTan) {
935         return math_cos_fdlibm_impl;
936       }
937       return math_cos_native_impl;
938     case UnaryMathFunction::Exp:
939       return math_exp_impl;
940     case UnaryMathFunction::Tan:
941       if (sUseFdlibmForSinCosTan) {
942         return math_tan_fdlibm_impl;
943       }
944       return math_tan_native_impl;
945     case UnaryMathFunction::ATan:
946       return math_atan_impl;
947     case UnaryMathFunction::ASin:
948       return math_asin_impl;
949     case UnaryMathFunction::ACos:
950       return math_acos_impl;
951     case UnaryMathFunction::Log10:
952       return math_log10_impl;
953     case UnaryMathFunction::Log2:
954       return math_log2_impl;
955     case UnaryMathFunction::Log1P:
956       return math_log1p_impl;
957     case UnaryMathFunction::ExpM1:
958       return math_expm1_impl;
959     case UnaryMathFunction::CosH:
960       return math_cosh_impl;
961     case UnaryMathFunction::SinH:
962       return math_sinh_impl;
963     case UnaryMathFunction::TanH:
964       return math_tanh_impl;
965     case UnaryMathFunction::ACosH:
966       return math_acosh_impl;
967     case UnaryMathFunction::ASinH:
968       return math_asinh_impl;
969     case UnaryMathFunction::ATanH:
970       return math_atanh_impl;
971     case UnaryMathFunction::Trunc:
972       return math_trunc_impl;
973     case UnaryMathFunction::Cbrt:
974       return math_cbrt_impl;
975     case UnaryMathFunction::Floor:
976       return math_floor_impl;
977     case UnaryMathFunction::Ceil:
978       return math_ceil_impl;
979     case UnaryMathFunction::Round:
980       return math_round_impl;
981   }
982   MOZ_CRASH("Unknown function");
983 }
984 
GetUnaryMathFunctionName(UnaryMathFunction fun)985 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun) {
986   switch (fun) {
987     case UnaryMathFunction::Log:
988       return "Log";
989     case UnaryMathFunction::Sin:
990       return "Sin";
991     case UnaryMathFunction::Cos:
992       return "Cos";
993     case UnaryMathFunction::Exp:
994       return "Exp";
995     case UnaryMathFunction::Tan:
996       return "Tan";
997     case UnaryMathFunction::ACos:
998       return "ACos";
999     case UnaryMathFunction::ASin:
1000       return "ASin";
1001     case UnaryMathFunction::ATan:
1002       return "ATan";
1003     case UnaryMathFunction::Log10:
1004       return "Log10";
1005     case UnaryMathFunction::Log2:
1006       return "Log2";
1007     case UnaryMathFunction::Log1P:
1008       return "Log1P";
1009     case UnaryMathFunction::ExpM1:
1010       return "ExpM1";
1011     case UnaryMathFunction::CosH:
1012       return "CosH";
1013     case UnaryMathFunction::SinH:
1014       return "SinH";
1015     case UnaryMathFunction::TanH:
1016       return "TanH";
1017     case UnaryMathFunction::ACosH:
1018       return "ACosH";
1019     case UnaryMathFunction::ASinH:
1020       return "ASinH";
1021     case UnaryMathFunction::ATanH:
1022       return "ATanH";
1023     case UnaryMathFunction::Trunc:
1024       return "Trunc";
1025     case UnaryMathFunction::Cbrt:
1026       return "Cbrt";
1027     case UnaryMathFunction::Floor:
1028       return "Floor";
1029     case UnaryMathFunction::Ceil:
1030       return "Ceil";
1031     case UnaryMathFunction::Round:
1032       return "Round";
1033   }
1034   MOZ_CRASH("Unknown function");
1035 }
1036 
1037 static const JSFunctionSpec math_static_methods[] = {
1038     JS_FN(js_toSource_str, math_toSource, 0, 0),
1039     JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
1040     JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
1041     JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
1042     JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
1043     JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
1044     JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
1045     JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
1046     JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
1047     JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
1048     JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
1049     JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
1050     JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
1051     JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
1052     JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
1053     JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
1054     JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
1055     JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
1056     JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
1057     JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
1058     JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
1059     JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
1060     JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
1061     JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
1062     JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
1063     JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
1064     JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
1065     JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
1066     JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
1067     JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
1068     JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
1069     JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
1070     JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
1071     JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
1072     JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
1073     JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
1074     JS_FS_END};
1075 
1076 static const JSPropertySpec math_static_properties[] = {
1077     JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT),
1078     JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT),
1079     JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT),
1080     JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT),
1081     JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT),
1082     JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT),
1083     JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT),
1084     JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT),
1085 
1086     JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY),
1087     JS_PS_END};
1088 
CreateMathObject(JSContext * cx,JSProtoKey key)1089 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
1090   Handle<GlobalObject*> global = cx->global();
1091   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
1092   if (!proto) {
1093     return nullptr;
1094   }
1095   return NewTenuredObjectWithGivenProto(cx, &MathClass, proto);
1096 }
1097 
1098 static const ClassSpec MathClassSpec = {CreateMathObject,
1099                                         nullptr,
1100                                         math_static_methods,
1101                                         math_static_properties,
1102                                         nullptr,
1103                                         nullptr,
1104                                         nullptr};
1105 
1106 const JSClass js::MathClass = {js_Math_str,
1107                                JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
1108                                JS_NULL_CLASS_OPS, &MathClassSpec};
1109