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