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