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