jsmath.cpp (34733B)
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/CheckedInt.h" 14 #include "mozilla/FloatingPoint.h" 15 #include "mozilla/MathAlgorithms.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/ForOfIterator.h" 28 #include "js/Prefs.h" 29 #include "js/PropertySpec.h" 30 #include "util/DifferentialTesting.h" 31 #include "vm/Float16.h" 32 #include "vm/Interpreter.h" 33 #include "vm/JSContext.h" 34 #include "vm/Realm.h" 35 #include "vm/Time.h" 36 #include "xsum/xsum.h" 37 38 #include "vm/JSObject-inl.h" 39 40 using namespace js; 41 42 using JS::GenericNaN; 43 using JS::ToNumber; 44 using mozilla::ExponentComponent; 45 using mozilla::FloatingPoint; 46 using mozilla::IsNegative; 47 using mozilla::IsNegativeZero; 48 using mozilla::Maybe; 49 using mozilla::NegativeInfinity; 50 using mozilla::NumberEqualsInt32; 51 using mozilla::NumberEqualsInt64; 52 using mozilla::PositiveInfinity; 53 using mozilla::WrappingMultiply; 54 55 bool js::math_use_fdlibm_for_sin_cos_tan() { 56 return JS::Prefs::use_fdlibm_for_sin_cos_tan(); 57 } 58 59 static inline bool UseFdlibmForSinCosTan(const CallArgs& args) { 60 return math_use_fdlibm_for_sin_cos_tan() || 61 args.callee().nonCCWRealm()->creationOptions().alwaysUseFdlibm(); 62 } 63 64 // Stack alignment on x86 Windows is 4 byte. Align to 16 bytes when calling 65 // rounding functions with double parameters. 66 // 67 // See |ABIStackAlignment| in "js/src/jit/x86/Assembler-x86.h". 68 #if defined(JS_CODEGEN_X86) && (!defined(__GNUC__) || defined(__MINGW32__)) 69 # define ALIGN_STACK_FOR_ROUNDING_FUNCTION \ 70 __attribute__((force_align_arg_pointer)) 71 #else 72 # define ALIGN_STACK_FOR_ROUNDING_FUNCTION 73 #endif 74 75 template <UnaryMathFunctionType F> 76 static bool math_function(JSContext* cx, CallArgs& args) { 77 if (args.length() == 0) { 78 args.rval().setNaN(); 79 return true; 80 } 81 82 double x; 83 if (!ToNumber(cx, args[0], &x)) { 84 return false; 85 } 86 87 // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always 88 // type the value as a double. 89 90 // NB: Always stored as a double so the math function can be inlined 91 // through MMathFunction. 92 double z = F(x); 93 args.rval().setDouble(z); 94 return true; 95 } 96 97 double js::math_abs_impl(double x) { return mozilla::Abs(x); } 98 99 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) { 100 CallArgs args = CallArgsFromVp(argc, vp); 101 102 if (args.length() == 0) { 103 args.rval().setNaN(); 104 return true; 105 } 106 107 double x; 108 if (!ToNumber(cx, args[0], &x)) { 109 return false; 110 } 111 112 args.rval().setNumber(math_abs_impl(x)); 113 return true; 114 } 115 116 double js::math_acos_impl(double x) { 117 AutoUnsafeCallWithABI unsafe; 118 return fdlibm_acos(x); 119 } 120 121 static bool math_acos(JSContext* cx, unsigned argc, Value* vp) { 122 CallArgs args = CallArgsFromVp(argc, vp); 123 return math_function<math_acos_impl>(cx, args); 124 } 125 126 double js::math_asin_impl(double x) { 127 AutoUnsafeCallWithABI unsafe; 128 return fdlibm_asin(x); 129 } 130 131 static bool math_asin(JSContext* cx, unsigned argc, Value* vp) { 132 CallArgs args = CallArgsFromVp(argc, vp); 133 return math_function<math_asin_impl>(cx, args); 134 } 135 136 double js::math_atan_impl(double x) { 137 AutoUnsafeCallWithABI unsafe; 138 return fdlibm_atan(x); 139 } 140 141 static bool math_atan(JSContext* cx, unsigned argc, Value* vp) { 142 CallArgs args = CallArgsFromVp(argc, vp); 143 return math_function<math_atan_impl>(cx, args); 144 } 145 146 double js::ecmaAtan2(double y, double x) { 147 AutoUnsafeCallWithABI unsafe; 148 return fdlibm_atan2(y, x); 149 } 150 151 static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) { 152 CallArgs args = CallArgsFromVp(argc, vp); 153 154 double y; 155 if (!ToNumber(cx, args.get(0), &y)) { 156 return false; 157 } 158 159 double x; 160 if (!ToNumber(cx, args.get(1), &x)) { 161 return false; 162 } 163 164 double z = ecmaAtan2(y, x); 165 args.rval().setDouble(z); 166 return true; 167 } 168 169 ALIGN_STACK_FOR_ROUNDING_FUNCTION 170 double js::math_ceil_impl(double x) { 171 AutoUnsafeCallWithABI unsafe; 172 return std::ceil(x); 173 } 174 175 static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) { 176 CallArgs args = CallArgsFromVp(argc, vp); 177 178 if (args.length() == 0) { 179 args.rval().setNaN(); 180 return true; 181 } 182 183 double x; 184 if (!ToNumber(cx, args[0], &x)) { 185 return false; 186 } 187 188 args.rval().setNumber(math_ceil_impl(x)); 189 return true; 190 } 191 192 static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) { 193 CallArgs args = CallArgsFromVp(argc, vp); 194 195 if (args.length() == 0) { 196 args.rval().setInt32(32); 197 return true; 198 } 199 200 uint32_t n; 201 if (!ToUint32(cx, args[0], &n)) { 202 return false; 203 } 204 205 if (n == 0) { 206 args.rval().setInt32(32); 207 return true; 208 } 209 210 args.rval().setInt32(mozilla::CountLeadingZeroes32(n)); 211 return true; 212 } 213 214 double js::math_cos_fdlibm_impl(double x) { 215 AutoUnsafeCallWithABI unsafe; 216 return fdlibm_cos(x); 217 } 218 219 double js::math_cos_native_impl(double x) { 220 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan()); 221 AutoUnsafeCallWithABI unsafe; 222 return std::cos(x); 223 } 224 225 static bool math_cos(JSContext* cx, unsigned argc, Value* vp) { 226 CallArgs args = CallArgsFromVp(argc, vp); 227 if (UseFdlibmForSinCosTan(args)) { 228 return math_function<math_cos_fdlibm_impl>(cx, args); 229 } 230 return math_function<math_cos_native_impl>(cx, args); 231 } 232 233 double js::math_exp_impl(double x) { 234 AutoUnsafeCallWithABI unsafe; 235 return fdlibm_exp(x); 236 } 237 238 static bool math_exp(JSContext* cx, unsigned argc, Value* vp) { 239 CallArgs args = CallArgsFromVp(argc, vp); 240 return math_function<math_exp_impl>(cx, args); 241 } 242 243 ALIGN_STACK_FOR_ROUNDING_FUNCTION 244 double js::math_floor_impl(double x) { 245 AutoUnsafeCallWithABI unsafe; 246 return std::floor(x); 247 } 248 249 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) { 250 CallArgs args = CallArgsFromVp(argc, vp); 251 252 if (args.length() == 0) { 253 args.rval().setNaN(); 254 return true; 255 } 256 257 double x; 258 if (!ToNumber(cx, args[0], &x)) { 259 return false; 260 } 261 262 args.rval().setNumber(math_floor_impl(x)); 263 return true; 264 } 265 266 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs, 267 MutableHandleValue res) { 268 int32_t a = 0, b = 0; 269 if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) { 270 return false; 271 } 272 if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) { 273 return false; 274 } 275 276 res.setInt32(WrappingMultiply(a, b)); 277 return true; 278 } 279 280 static bool math_imul(JSContext* cx, unsigned argc, Value* vp) { 281 CallArgs args = CallArgsFromVp(argc, vp); 282 283 return math_imul_handle(cx, args.get(0), args.get(1), args.rval()); 284 } 285 286 // Implements Math.fround (20.2.2.16) up to step 3 287 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) { 288 double d; 289 bool success = ToNumber(cx, v, &d); 290 *out = static_cast<float>(d); 291 return success; 292 } 293 294 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) { 295 float f; 296 if (!RoundFloat32(cx, arg, &f)) { 297 return false; 298 } 299 300 res.setDouble(static_cast<double>(f)); 301 return true; 302 } 303 304 double js::RoundFloat32(double d) { 305 return static_cast<double>(static_cast<float>(d)); 306 } 307 308 static bool math_fround(JSContext* cx, unsigned argc, Value* vp) { 309 CallArgs args = CallArgsFromVp(argc, vp); 310 311 if (args.length() == 0) { 312 args.rval().setNaN(); 313 return true; 314 } 315 316 return RoundFloat32(cx, args[0], args.rval()); 317 } 318 319 double js::RoundFloat16(double d) { 320 AutoUnsafeCallWithABI unsafe; 321 322 // http://tc39.es/proposal-float16array/#sec-function-properties-of-the-math-object 323 324 // 1. Let n be ? ToNumber(x). 325 // [Not applicable here] 326 327 // 2. If n is NaN, return NaN. 328 // 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n. 329 // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format 330 // using roundTiesToEven mode. 331 js::float16 f16 = js::float16(d); 332 333 // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 334 // format. 335 // 6. Return the ECMAScript Number value corresponding to n64. 336 return static_cast<double>(f16); 337 } 338 339 static bool math_f16round(JSContext* cx, unsigned argc, Value* vp) { 340 // http://tc39.es/proposal-float16array/#sec-function-properties-of-the-math-object 341 CallArgs args = CallArgsFromVp(argc, vp); 342 343 if (args.length() == 0) { 344 args.rval().setNaN(); 345 return true; 346 } 347 348 // 1. Let n be ? ToNumber(x). 349 double d; 350 if (!ToNumber(cx, args[0], &d)) { 351 return false; 352 } 353 354 // Steps 2-6. 355 args.rval().setDouble(RoundFloat16(d)); 356 return true; 357 } 358 359 double js::math_log_impl(double x) { 360 AutoUnsafeCallWithABI unsafe; 361 return fdlibm_log(x); 362 } 363 364 static bool math_log(JSContext* cx, unsigned argc, Value* vp) { 365 CallArgs args = CallArgsFromVp(argc, vp); 366 return math_function<math_log_impl>(cx, args); 367 } 368 369 double js::math_max_impl(double x, double y) { 370 AutoUnsafeCallWithABI unsafe; 371 372 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0 373 if (x > y || std::isnan(x) || (x == y && IsNegative(y))) { 374 return x; 375 } 376 return y; 377 } 378 379 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) { 380 CallArgs args = CallArgsFromVp(argc, vp); 381 382 double maxval = NegativeInfinity<double>(); 383 for (unsigned i = 0; i < args.length(); i++) { 384 double x; 385 if (!ToNumber(cx, args[i], &x)) { 386 return false; 387 } 388 maxval = math_max_impl(x, maxval); 389 } 390 args.rval().setNumber(maxval); 391 return true; 392 } 393 394 double js::math_min_impl(double x, double y) { 395 AutoUnsafeCallWithABI unsafe; 396 397 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0 398 if (x < y || std::isnan(x) || (x == y && IsNegativeZero(x))) { 399 return x; 400 } 401 return y; 402 } 403 404 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) { 405 CallArgs args = CallArgsFromVp(argc, vp); 406 407 double minval = PositiveInfinity<double>(); 408 for (unsigned i = 0; i < args.length(); i++) { 409 double x; 410 if (!ToNumber(cx, args[i], &x)) { 411 return false; 412 } 413 minval = math_min_impl(x, minval); 414 } 415 args.rval().setNumber(minval); 416 return true; 417 } 418 419 double js::powi(double x, int32_t y) { 420 AutoUnsafeCallWithABI unsafe; 421 422 // It's only safe to optimize this when we can compute with integer values or 423 // the exponent is a small, positive constant. 424 if (y >= 0) { 425 uint32_t n = uint32_t(y); 426 427 // NB: Have to take fast-path for n <= 4 to match |MPow::foldsTo|. Otherwise 428 // we risk causing differential testing issues. 429 if (n == 0) { 430 return 1; 431 } 432 if (n == 1) { 433 return x; 434 } 435 if (n == 2) { 436 return x * x; 437 } 438 if (n == 3) { 439 return x * x * x; 440 } 441 if (n == 4) { 442 double z = x * x; 443 return z * z; 444 } 445 446 int64_t i; 447 if (NumberEqualsInt64(x, &i)) { 448 // Special-case: |-0 ** odd| is -0. 449 if (i == 0) { 450 return (n & 1) ? x : 0; 451 } 452 453 // Use int64 to cover cases like |Math.pow(2, 53)|. 454 mozilla::CheckedInt64 runningSquare = i; 455 mozilla::CheckedInt64 result = 1; 456 while (true) { 457 if ((n & 1) != 0) { 458 result *= runningSquare; 459 if (!result.isValid()) { 460 break; 461 } 462 } 463 464 n >>= 1; 465 if (n == 0) { 466 return static_cast<double>(result.value()); 467 } 468 469 runningSquare *= runningSquare; 470 if (!runningSquare.isValid()) { 471 break; 472 } 473 } 474 } 475 476 // Fall-back to use std::pow to reduce floating point precision errors. 477 } 478 479 return std::pow(x, static_cast<double>(y)); // Avoid pow(double, int). 480 } 481 482 double js::ecmaPow(double x, double y) { 483 AutoUnsafeCallWithABI unsafe; 484 485 /* 486 * Use powi if the exponent is an integer-valued double. We don't have to 487 * check for NaN since a comparison with NaN is always false. 488 */ 489 int32_t yi; 490 if (NumberEqualsInt32(y, &yi)) { 491 return powi(x, yi); 492 } 493 494 /* 495 * Because C99 and ECMA specify different behavior for pow(), 496 * we need to wrap the libm call to make it ECMA compliant. 497 */ 498 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) { 499 return GenericNaN(); 500 } 501 502 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */ 503 if (y == 0) { 504 return 1; 505 } 506 507 /* 508 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x) 509 * when x = -0.0, so we have to guard for this. 510 */ 511 if (std::isfinite(x) && x != 0.0) { 512 if (y == 0.5) { 513 return std::sqrt(x); 514 } 515 if (y == -0.5) { 516 return 1.0 / std::sqrt(x); 517 } 518 } 519 return std::pow(x, y); 520 } 521 522 static bool math_pow(JSContext* cx, unsigned argc, Value* vp) { 523 CallArgs args = CallArgsFromVp(argc, vp); 524 525 double x; 526 if (!ToNumber(cx, args.get(0), &x)) { 527 return false; 528 } 529 530 double y; 531 if (!ToNumber(cx, args.get(1), &y)) { 532 return false; 533 } 534 535 double z = ecmaPow(x, y); 536 args.rval().setNumber(z); 537 return true; 538 } 539 540 uint64_t js::GenerateRandomSeed() { 541 Maybe<uint64_t> maybeSeed = mozilla::RandomUint64(); 542 543 return maybeSeed.valueOrFrom([] { 544 // Use PRMJ_Now() in case we couldn't read random bits from the OS. 545 uint64_t timestamp = PRMJ_Now(); 546 return timestamp ^ (timestamp << 32); 547 }); 548 } 549 550 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) { 551 // XorShift128PlusRNG must be initialized with a non-zero seed. 552 do { 553 seed[0] = GenerateRandomSeed(); 554 seed[1] = GenerateRandomSeed(); 555 } while (seed[0] == 0 && seed[1] == 0); 556 } 557 558 mozilla::non_crypto::XorShift128PlusRNG& 559 Realm::getOrCreateRandomNumberGenerator() { 560 if (randomNumberGenerator_.isNothing()) { 561 mozilla::Array<uint64_t, 2> seed; 562 GenerateXorShift128PlusSeed(seed); 563 randomNumberGenerator_.emplace(seed[0], seed[1]); 564 } 565 566 return randomNumberGenerator_.ref(); 567 } 568 569 double js::math_random_impl(JSContext* cx) { 570 return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble(); 571 } 572 573 static bool math_random(JSContext* cx, unsigned argc, Value* vp) { 574 CallArgs args = CallArgsFromVp(argc, vp); 575 if (js::SupportDifferentialTesting()) { 576 args.rval().setDouble(0); 577 } else { 578 args.rval().setDouble(math_random_impl(cx)); 579 } 580 return true; 581 } 582 583 template <typename T> 584 T js::GetBiggestNumberLessThan(T x) { 585 MOZ_ASSERT(!IsNegative(x)); 586 MOZ_ASSERT(std::isfinite(x)); 587 using Bits = typename mozilla::FloatingPoint<T>::Bits; 588 Bits bits = mozilla::BitwiseCast<Bits>(x); 589 MOZ_ASSERT(bits > 0, "will underflow"); 590 return mozilla::BitwiseCast<T>(bits - 1); 591 } 592 593 template double js::GetBiggestNumberLessThan<>(double x); 594 template float js::GetBiggestNumberLessThan<>(float x); 595 596 ALIGN_STACK_FOR_ROUNDING_FUNCTION 597 double js::math_round_impl(double x) { 598 AutoUnsafeCallWithABI unsafe; 599 600 double result = std::ceil(x); 601 if (x < result - 0.5) { 602 result -= 1.0; 603 } 604 return result; 605 } 606 607 float js::math_roundf_impl(float x) { 608 AutoUnsafeCallWithABI unsafe; 609 610 float result = std::ceil(x); 611 if (x < result - 0.5f) { 612 result -= 1.0f; 613 } 614 return result; 615 } 616 617 /* ES5 15.8.2.15. */ 618 static bool math_round(JSContext* cx, unsigned argc, Value* vp) { 619 CallArgs args = CallArgsFromVp(argc, vp); 620 621 if (args.length() == 0) { 622 args.rval().setNaN(); 623 return true; 624 } 625 626 double x; 627 if (!ToNumber(cx, args[0], &x)) { 628 return false; 629 } 630 631 args.rval().setNumber(math_round_impl(x)); 632 return true; 633 } 634 635 double js::math_sin_fdlibm_impl(double x) { 636 AutoUnsafeCallWithABI unsafe; 637 return fdlibm_sin(x); 638 } 639 640 double js::math_sin_native_impl(double x) { 641 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan()); 642 AutoUnsafeCallWithABI unsafe; 643 return std::sin(x); 644 } 645 646 static bool math_sin(JSContext* cx, unsigned argc, Value* vp) { 647 CallArgs args = CallArgsFromVp(argc, vp); 648 if (UseFdlibmForSinCosTan(args)) { 649 return math_function<math_sin_fdlibm_impl>(cx, args); 650 } 651 return math_function<math_sin_native_impl>(cx, args); 652 } 653 654 double js::math_sqrt_impl(double x) { 655 AutoUnsafeCallWithABI unsafe; 656 return std::sqrt(x); 657 } 658 659 static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) { 660 CallArgs args = CallArgsFromVp(argc, vp); 661 return math_function<math_sqrt_impl>(cx, args); 662 } 663 664 double js::math_tan_fdlibm_impl(double x) { 665 AutoUnsafeCallWithABI unsafe; 666 return fdlibm_tan(x); 667 } 668 669 double js::math_tan_native_impl(double x) { 670 MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan()); 671 AutoUnsafeCallWithABI unsafe; 672 return std::tan(x); 673 } 674 675 static bool math_tan(JSContext* cx, unsigned argc, Value* vp) { 676 CallArgs args = CallArgsFromVp(argc, vp); 677 if (UseFdlibmForSinCosTan(args)) { 678 return math_function<math_tan_fdlibm_impl>(cx, args); 679 } 680 return math_function<math_tan_native_impl>(cx, args); 681 } 682 683 double js::math_log10_impl(double x) { 684 AutoUnsafeCallWithABI unsafe; 685 return fdlibm_log10(x); 686 } 687 688 static bool math_log10(JSContext* cx, unsigned argc, Value* vp) { 689 CallArgs args = CallArgsFromVp(argc, vp); 690 return math_function<math_log10_impl>(cx, args); 691 } 692 693 double js::math_log2_impl(double x) { 694 AutoUnsafeCallWithABI unsafe; 695 return fdlibm_log2(x); 696 } 697 698 static bool math_log2(JSContext* cx, unsigned argc, Value* vp) { 699 CallArgs args = CallArgsFromVp(argc, vp); 700 return math_function<math_log2_impl>(cx, args); 701 } 702 703 double js::math_log1p_impl(double x) { 704 AutoUnsafeCallWithABI unsafe; 705 return fdlibm_log1p(x); 706 } 707 708 static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) { 709 CallArgs args = CallArgsFromVp(argc, vp); 710 return math_function<math_log1p_impl>(cx, args); 711 } 712 713 double js::math_expm1_impl(double x) { 714 AutoUnsafeCallWithABI unsafe; 715 return fdlibm_expm1(x); 716 } 717 718 static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) { 719 CallArgs args = CallArgsFromVp(argc, vp); 720 return math_function<math_expm1_impl>(cx, args); 721 } 722 723 double js::math_cosh_impl(double x) { 724 AutoUnsafeCallWithABI unsafe; 725 return fdlibm_cosh(x); 726 } 727 728 static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) { 729 CallArgs args = CallArgsFromVp(argc, vp); 730 return math_function<math_cosh_impl>(cx, args); 731 } 732 733 double js::math_sinh_impl(double x) { 734 AutoUnsafeCallWithABI unsafe; 735 return fdlibm_sinh(x); 736 } 737 738 static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) { 739 CallArgs args = CallArgsFromVp(argc, vp); 740 return math_function<math_sinh_impl>(cx, args); 741 } 742 743 double js::math_tanh_impl(double x) { 744 AutoUnsafeCallWithABI unsafe; 745 return fdlibm_tanh(x); 746 } 747 748 static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) { 749 CallArgs args = CallArgsFromVp(argc, vp); 750 return math_function<math_tanh_impl>(cx, args); 751 } 752 753 double js::math_acosh_impl(double x) { 754 AutoUnsafeCallWithABI unsafe; 755 return fdlibm_acosh(x); 756 } 757 758 static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) { 759 CallArgs args = CallArgsFromVp(argc, vp); 760 return math_function<math_acosh_impl>(cx, args); 761 } 762 763 double js::math_asinh_impl(double x) { 764 AutoUnsafeCallWithABI unsafe; 765 return fdlibm_asinh(x); 766 } 767 768 static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) { 769 CallArgs args = CallArgsFromVp(argc, vp); 770 return math_function<math_asinh_impl>(cx, args); 771 } 772 773 double js::math_atanh_impl(double x) { 774 AutoUnsafeCallWithABI unsafe; 775 return fdlibm_atanh(x); 776 } 777 778 static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) { 779 CallArgs args = CallArgsFromVp(argc, vp); 780 return math_function<math_atanh_impl>(cx, args); 781 } 782 783 double js::ecmaHypot(double x, double y) { 784 AutoUnsafeCallWithABI unsafe; 785 return fdlibm_hypot(x, y); 786 } 787 788 static inline void hypot_step(double& scale, double& sumsq, double x) { 789 double xabs = mozilla::Abs(x); 790 if (scale < xabs) { 791 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs); 792 scale = xabs; 793 } else if (scale != 0) { 794 sumsq += (xabs / scale) * (xabs / scale); 795 } 796 } 797 798 double js::hypot4(double x, double y, double z, double w) { 799 AutoUnsafeCallWithABI unsafe; 800 801 // Check for infinities or NaNs so that we can return immediately. 802 if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) { 803 return mozilla::PositiveInfinity<double>(); 804 } 805 806 if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) { 807 return GenericNaN(); 808 } 809 810 double scale = 0; 811 double sumsq = 1; 812 813 hypot_step(scale, sumsq, x); 814 hypot_step(scale, sumsq, y); 815 hypot_step(scale, sumsq, z); 816 hypot_step(scale, sumsq, w); 817 818 return scale * std::sqrt(sumsq); 819 } 820 821 double js::hypot3(double x, double y, double z) { 822 AutoUnsafeCallWithABI unsafe; 823 return hypot4(x, y, z, 0.0); 824 } 825 826 static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) { 827 CallArgs args = CallArgsFromVp(argc, vp); 828 return math_hypot_handle(cx, args, args.rval()); 829 } 830 831 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args, 832 MutableHandleValue res) { 833 // IonMonkey calls the ecmaHypot function directly if two arguments are 834 // given. Do that here as well to get the same results. 835 if (args.length() == 2) { 836 double x, y; 837 if (!ToNumber(cx, args[0], &x)) { 838 return false; 839 } 840 if (!ToNumber(cx, args[1], &y)) { 841 return false; 842 } 843 844 double result = ecmaHypot(x, y); 845 res.setDouble(result); 846 return true; 847 } 848 849 bool isInfinite = false; 850 bool isNaN = false; 851 852 double scale = 0; 853 double sumsq = 1; 854 855 for (unsigned i = 0; i < args.length(); i++) { 856 double x; 857 if (!ToNumber(cx, args[i], &x)) { 858 return false; 859 } 860 861 isInfinite |= std::isinf(x); 862 isNaN |= std::isnan(x); 863 if (isInfinite || isNaN) { 864 continue; 865 } 866 867 hypot_step(scale, sumsq, x); 868 } 869 870 double result = isInfinite ? PositiveInfinity<double>() 871 : isNaN ? GenericNaN() 872 : scale * std::sqrt(sumsq); 873 res.setDouble(result); 874 return true; 875 } 876 877 ALIGN_STACK_FOR_ROUNDING_FUNCTION 878 double js::math_trunc_impl(double x) { 879 AutoUnsafeCallWithABI unsafe; 880 return std::trunc(x); 881 } 882 883 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) { 884 CallArgs args = CallArgsFromVp(argc, vp); 885 if (args.length() == 0) { 886 args.rval().setNaN(); 887 return true; 888 } 889 890 double x; 891 if (!ToNumber(cx, args[0], &x)) { 892 return false; 893 } 894 895 args.rval().setNumber(math_trunc_impl(x)); 896 return true; 897 } 898 899 double js::math_sign_impl(double x) { 900 AutoUnsafeCallWithABI unsafe; 901 902 if (std::isnan(x)) { 903 return GenericNaN(); 904 } 905 906 return x == 0 ? x : x < 0 ? -1 : 1; 907 } 908 909 bool js::math_sign(JSContext* cx, unsigned argc, Value* vp) { 910 CallArgs args = CallArgsFromVp(argc, vp); 911 if (args.length() == 0) { 912 args.rval().setNaN(); 913 return true; 914 } 915 916 double x; 917 if (!ToNumber(cx, args[0], &x)) { 918 return false; 919 } 920 921 args.rval().setNumber(math_sign_impl(x)); 922 return true; 923 } 924 925 double js::math_cbrt_impl(double x) { 926 AutoUnsafeCallWithABI unsafe; 927 return fdlibm_cbrt(x); 928 } 929 930 static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) { 931 CallArgs args = CallArgsFromVp(argc, vp); 932 return math_function<math_cbrt_impl>(cx, args); 933 } 934 935 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) { 936 CallArgs args = CallArgsFromVp(argc, vp); 937 args.rval().setString(cx->names().Math); 938 return true; 939 } 940 941 enum class SumPreciseState : uint8_t { 942 MinusZero, 943 Finite, 944 PlusInfinity, 945 MinusInfinity, 946 NotANumber, 947 }; 948 949 /** 950 * Math.sumPrecise ( items ) 951 * 952 * https://tc39.es/proposal-math-sum/#sec-math.sumprecise 953 */ 954 static bool math_sumPrecise(JSContext* cx, unsigned argc, Value* vp) { 955 constexpr int64_t MaxCount = int64_t(1) << 53; 956 957 // Step 1. Perform ? RequireObjectCoercible(items). 958 CallArgs args = CallArgsFromVp(argc, vp); 959 if (!args.requireAtLeast(cx, "Math.sumPrecise", 1)) { 960 return false; 961 } 962 963 // Step 2. Let iteratorRecord be ? GetIterator(items, sync). 964 JS::ForOfIterator iterator(cx); 965 if (!iterator.init(args[0], JS::ForOfIterator::ThrowOnNonIterable)) { 966 return false; 967 } 968 969 // Step 3. Let state be minus-zero. 970 SumPreciseState state = SumPreciseState::MinusZero; 971 972 // Step 4. Let sum be 0. 973 xsum_small_accumulator sum; 974 xsum_small_init(&sum); 975 976 // Step 5. Let count be 0. 977 int64_t count = 0; 978 979 // Step 6. Let next be not-started. 980 // (implicit) 981 982 JS::Rooted<JS::Value> value(cx); 983 984 // Step 7. Repeat, while next is not done, 985 while (true) { 986 // Step 7.a. Set next to ? IteratorStepValue(iteratorRecord). 987 bool done; 988 if (!iterator.next(&value, &done)) { 989 return false; 990 } 991 992 // Step 7.b. If next is not done, then 993 if (done) { 994 break; 995 } 996 997 // Step 7.b.i. Set count to count + 1. 998 count += 1; 999 1000 // Step 7.b.ii. If count ≥ 2**53, then 1001 if (count >= MaxCount) { 1002 // Step 7.b.ii.1. Let error be ThrowCompletion(a newly created RangeError 1003 // object). 1004 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1005 JSMSG_SUMPRECISE_TOO_MANY_VALUES); 1006 1007 // Step 7.b.ii.2. Return ? IteratorClose(iteratorRecord, error). 1008 iterator.closeThrow(); 1009 return false; 1010 } 1011 1012 // Step 7.b.iv. If next is not a Number, then 1013 if (!value.isNumber()) { 1014 // Step 7.b.iv.1. Let error be ThrowCompletion(a newly created TypeError 1015 // object). 1016 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1017 JSMSG_SUMPRECISE_EXPECTED_NUMBER); 1018 1019 // Step 7.b.iv.2. Return ? IteratorClose(iteratorRecord, error). 1020 iterator.closeThrow(); 1021 return false; 1022 } 1023 1024 // Step 7.b.v. Let n be next. 1025 double n = value.toNumber(); 1026 1027 // Step 7.b.vi. If state is not not-a-number, then 1028 if (state == SumPreciseState::NotANumber) { 1029 continue; 1030 } 1031 1032 // Step 7.b.vi.1. If n is NaN, then 1033 if (std::isnan(n)) { 1034 // Step 7.b.vi.1.a. Set state to not-a-number. 1035 state = SumPreciseState::NotANumber; 1036 } else if (n == PositiveInfinity<double>()) { 1037 // Step 7.b.vi.2. Else if n is +∞𝔽, then 1038 if (state == SumPreciseState::MinusInfinity) { 1039 // Step 7.b.vi.2.a. If state is minus-infinity, set state to 1040 // not-a-number. 1041 state = SumPreciseState::NotANumber; 1042 } else { 1043 // Step 7.b.vi.2.b. Else, set state to plus-infinity. 1044 state = SumPreciseState::PlusInfinity; 1045 } 1046 } else if (n == NegativeInfinity<double>()) { 1047 // Step 7.b.vi.3. Else if n is -∞𝔽, then 1048 if (state == SumPreciseState::PlusInfinity) { 1049 // Step 7.b.vi.3.a. If state is plus-infinity, set state to 1050 // not-a-number. 1051 state = SumPreciseState::NotANumber; 1052 } else { 1053 // Step 7.b.vi.3.b. Else, set state to minus-infinity. 1054 state = SumPreciseState::MinusInfinity; 1055 } 1056 } else if (!IsNegativeZero(n) && (state == SumPreciseState::MinusZero || 1057 state == SumPreciseState::Finite)) { 1058 // Step 7.b.vi.4. Else if n is not -0𝔽 and state is either minus-zero or 1059 // finite, then 1060 // Step 7.b.vi.4.a. Set state to finite. 1061 state = SumPreciseState::Finite; 1062 1063 // Step 7.b.vi.4.b. Set sum to sum + ℝ(n). 1064 xsum_small_add1(&sum, n); 1065 } 1066 } 1067 1068 double rval; 1069 switch (state) { 1070 case SumPreciseState::NotANumber: 1071 // Step 8. If state is not-a-number, return NaN. 1072 rval = GenericNaN(); 1073 break; 1074 case SumPreciseState::PlusInfinity: 1075 // Step 9. If state is plus-infinity, return +∞𝔽. 1076 rval = PositiveInfinity<double>(); 1077 break; 1078 case SumPreciseState::MinusInfinity: 1079 // Step 10. If state is minus-infinity, return -∞𝔽. 1080 rval = NegativeInfinity<double>(); 1081 break; 1082 case SumPreciseState::MinusZero: 1083 // Step 11. If state is minus-zero, return -0𝔽. 1084 rval = -0.0; 1085 break; 1086 case SumPreciseState::Finite: 1087 // Step 12. Return 𝔽(sum). 1088 rval = xsum_small_round(&sum); 1089 break; 1090 } 1091 1092 args.rval().setNumber(rval); 1093 return true; 1094 } 1095 1096 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) { 1097 switch (fun) { 1098 case UnaryMathFunction::SinNative: 1099 return math_sin_native_impl; 1100 case UnaryMathFunction::SinFdlibm: 1101 return math_sin_fdlibm_impl; 1102 case UnaryMathFunction::CosNative: 1103 return math_cos_native_impl; 1104 case UnaryMathFunction::CosFdlibm: 1105 return math_cos_fdlibm_impl; 1106 case UnaryMathFunction::TanNative: 1107 return math_tan_native_impl; 1108 case UnaryMathFunction::TanFdlibm: 1109 return math_tan_fdlibm_impl; 1110 case UnaryMathFunction::Log: 1111 return math_log_impl; 1112 case UnaryMathFunction::Exp: 1113 return math_exp_impl; 1114 case UnaryMathFunction::ATan: 1115 return math_atan_impl; 1116 case UnaryMathFunction::ASin: 1117 return math_asin_impl; 1118 case UnaryMathFunction::ACos: 1119 return math_acos_impl; 1120 case UnaryMathFunction::Log10: 1121 return math_log10_impl; 1122 case UnaryMathFunction::Log2: 1123 return math_log2_impl; 1124 case UnaryMathFunction::Log1P: 1125 return math_log1p_impl; 1126 case UnaryMathFunction::ExpM1: 1127 return math_expm1_impl; 1128 case UnaryMathFunction::CosH: 1129 return math_cosh_impl; 1130 case UnaryMathFunction::SinH: 1131 return math_sinh_impl; 1132 case UnaryMathFunction::TanH: 1133 return math_tanh_impl; 1134 case UnaryMathFunction::ACosH: 1135 return math_acosh_impl; 1136 case UnaryMathFunction::ASinH: 1137 return math_asinh_impl; 1138 case UnaryMathFunction::ATanH: 1139 return math_atanh_impl; 1140 case UnaryMathFunction::Trunc: 1141 return math_trunc_impl; 1142 case UnaryMathFunction::Cbrt: 1143 return math_cbrt_impl; 1144 case UnaryMathFunction::Floor: 1145 return math_floor_impl; 1146 case UnaryMathFunction::Ceil: 1147 return math_ceil_impl; 1148 case UnaryMathFunction::Round: 1149 return math_round_impl; 1150 } 1151 MOZ_CRASH("Unknown function"); 1152 } 1153 1154 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun, bool enumName) { 1155 switch (fun) { 1156 case UnaryMathFunction::SinNative: 1157 return enumName ? "SinNative" : "Sin (native)"; 1158 case UnaryMathFunction::SinFdlibm: 1159 return enumName ? "SinFdlibm" : "Sin (fdlibm)"; 1160 case UnaryMathFunction::CosNative: 1161 return enumName ? "CosNative" : "Cos (native)"; 1162 case UnaryMathFunction::CosFdlibm: 1163 return enumName ? "CosFdlibm" : "Cos (fdlibm)"; 1164 case UnaryMathFunction::TanNative: 1165 return enumName ? "TanNative" : "Tan (native)"; 1166 case UnaryMathFunction::TanFdlibm: 1167 return enumName ? "TanFdlibm" : "Tan (fdlibm)"; 1168 case UnaryMathFunction::Log: 1169 return "Log"; 1170 case UnaryMathFunction::Exp: 1171 return "Exp"; 1172 case UnaryMathFunction::ACos: 1173 return "ACos"; 1174 case UnaryMathFunction::ASin: 1175 return "ASin"; 1176 case UnaryMathFunction::ATan: 1177 return "ATan"; 1178 case UnaryMathFunction::Log10: 1179 return "Log10"; 1180 case UnaryMathFunction::Log2: 1181 return "Log2"; 1182 case UnaryMathFunction::Log1P: 1183 return "Log1P"; 1184 case UnaryMathFunction::ExpM1: 1185 return "ExpM1"; 1186 case UnaryMathFunction::CosH: 1187 return "CosH"; 1188 case UnaryMathFunction::SinH: 1189 return "SinH"; 1190 case UnaryMathFunction::TanH: 1191 return "TanH"; 1192 case UnaryMathFunction::ACosH: 1193 return "ACosH"; 1194 case UnaryMathFunction::ASinH: 1195 return "ASinH"; 1196 case UnaryMathFunction::ATanH: 1197 return "ATanH"; 1198 case UnaryMathFunction::Trunc: 1199 return "Trunc"; 1200 case UnaryMathFunction::Cbrt: 1201 return "Cbrt"; 1202 case UnaryMathFunction::Floor: 1203 return "Floor"; 1204 case UnaryMathFunction::Ceil: 1205 return "Ceil"; 1206 case UnaryMathFunction::Round: 1207 return "Round"; 1208 } 1209 MOZ_CRASH("Unknown function"); 1210 } 1211 1212 static const JSFunctionSpec math_static_methods[] = { 1213 JS_FN("toSource", math_toSource, 0, 0), 1214 JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs), 1215 JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos), 1216 JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin), 1217 JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan), 1218 JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2), 1219 JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil), 1220 JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32), 1221 JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos), 1222 JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp), 1223 JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor), 1224 JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul), 1225 JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound), 1226 JS_INLINABLE_FN("f16round", math_f16round, 1, 0, MathF16Round), 1227 JS_INLINABLE_FN("log", math_log, 1, 0, MathLog), 1228 JS_INLINABLE_FN("max", math_max, 2, 0, MathMax), 1229 JS_INLINABLE_FN("min", math_min, 2, 0, MathMin), 1230 JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow), 1231 JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom), 1232 JS_INLINABLE_FN("round", math_round, 1, 0, MathRound), 1233 JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin), 1234 JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt), 1235 JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan), 1236 JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10), 1237 JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2), 1238 JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P), 1239 JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1), 1240 JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH), 1241 JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH), 1242 JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH), 1243 JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH), 1244 JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH), 1245 JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH), 1246 JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot), 1247 JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc), 1248 JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign), 1249 JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt), 1250 JS_FN("sumPrecise", math_sumPrecise, 1, 0), 1251 JS_FS_END, 1252 }; 1253 1254 static const JSPropertySpec math_static_properties[] = { 1255 JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT), 1256 JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT), 1257 JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT), 1258 JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT), 1259 JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT), 1260 JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT), 1261 JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT), 1262 JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT), 1263 1264 JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY), 1265 JS_PS_END, 1266 }; 1267 1268 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) { 1269 RootedObject proto(cx, &cx->global()->getObjectPrototype()); 1270 return NewTenuredObjectWithGivenProto(cx, &MathClass, proto); 1271 } 1272 1273 static const ClassSpec MathClassSpec = { 1274 CreateMathObject, 1275 nullptr, 1276 math_static_methods, 1277 math_static_properties, 1278 nullptr, 1279 nullptr, 1280 nullptr, 1281 }; 1282 1283 const JSClass js::MathClass = { 1284 "Math", 1285 JSCLASS_HAS_CACHED_PROTO(JSProto_Math), 1286 JS_NULL_CLASS_OPS, 1287 &MathClassSpec, 1288 }; 1289 1290 #undef ALIGN_STACK_FOR_ROUNDING_FUNCTION