1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsmath.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1509 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JS math package. 1.12 + */ 1.13 + 1.14 +#include "jsmath.h" 1.15 + 1.16 +#include "mozilla/Constants.h" 1.17 +#include "mozilla/FloatingPoint.h" 1.18 +#include "mozilla/MathAlgorithms.h" 1.19 +#include "mozilla/MemoryReporting.h" 1.20 + 1.21 +#include <algorithm> // for std::max 1.22 +#include <fcntl.h> 1.23 + 1.24 +#ifdef XP_UNIX 1.25 +# include <unistd.h> 1.26 +#endif 1.27 + 1.28 +#include "jsapi.h" 1.29 +#include "jsatom.h" 1.30 +#include "jscntxt.h" 1.31 +#include "jscompartment.h" 1.32 +#include "jslibmath.h" 1.33 +#include "jstypes.h" 1.34 +#include "prmjtime.h" 1.35 + 1.36 +#include "jsobjinlines.h" 1.37 + 1.38 +using namespace js; 1.39 + 1.40 +using mozilla::Abs; 1.41 +using mozilla::NumberEqualsInt32; 1.42 +using mozilla::NumberIsInt32; 1.43 +using mozilla::ExponentComponent; 1.44 +using mozilla::FloatingPoint; 1.45 +using mozilla::IsFinite; 1.46 +using mozilla::IsInfinite; 1.47 +using mozilla::IsNaN; 1.48 +using mozilla::IsNegative; 1.49 +using mozilla::IsNegativeZero; 1.50 +using mozilla::PositiveInfinity; 1.51 +using mozilla::NegativeInfinity; 1.52 +using JS::ToNumber; 1.53 +using JS::GenericNaN; 1.54 + 1.55 +static const JSConstDoubleSpec math_constants[] = { 1.56 + {M_E, "E", 0, {0,0,0}}, 1.57 + {M_LOG2E, "LOG2E", 0, {0,0,0}}, 1.58 + {M_LOG10E, "LOG10E", 0, {0,0,0}}, 1.59 + {M_LN2, "LN2", 0, {0,0,0}}, 1.60 + {M_LN10, "LN10", 0, {0,0,0}}, 1.61 + {M_PI, "PI", 0, {0,0,0}}, 1.62 + {M_SQRT2, "SQRT2", 0, {0,0,0}}, 1.63 + {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}}, 1.64 + {0,0,0,{0,0,0}} 1.65 +}; 1.66 + 1.67 +MathCache::MathCache() { 1.68 + memset(table, 0, sizeof(table)); 1.69 + 1.70 + /* See comments in lookup(). */ 1.71 + JS_ASSERT(IsNegativeZero(-0.0)); 1.72 + JS_ASSERT(!IsNegativeZero(+0.0)); 1.73 + JS_ASSERT(hash(-0.0) != hash(+0.0)); 1.74 +} 1.75 + 1.76 +size_t 1.77 +MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.78 +{ 1.79 + return mallocSizeOf(this); 1.80 +} 1.81 + 1.82 +const Class js::MathClass = { 1.83 + js_Math_str, 1.84 + JSCLASS_HAS_CACHED_PROTO(JSProto_Math), 1.85 + JS_PropertyStub, /* addProperty */ 1.86 + JS_DeletePropertyStub, /* delProperty */ 1.87 + JS_PropertyStub, /* getProperty */ 1.88 + JS_StrictPropertyStub, /* setProperty */ 1.89 + JS_EnumerateStub, 1.90 + JS_ResolveStub, 1.91 + JS_ConvertStub 1.92 +}; 1.93 + 1.94 +bool 1.95 +js_math_abs(JSContext *cx, unsigned argc, Value *vp) 1.96 +{ 1.97 + CallArgs args = CallArgsFromVp(argc, vp); 1.98 + 1.99 + if (args.length() == 0) { 1.100 + args.rval().setNaN(); 1.101 + return true; 1.102 + } 1.103 + 1.104 + double x; 1.105 + if (!ToNumber(cx, args[0], &x)) 1.106 + return false; 1.107 + 1.108 + double z = Abs(x); 1.109 + args.rval().setNumber(z); 1.110 + return true; 1.111 +} 1.112 + 1.113 +#if defined(SOLARIS) && defined(__GNUC__) 1.114 +#define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN(); 1.115 +#else 1.116 +#define ACOS_IF_OUT_OF_RANGE(x) 1.117 +#endif 1.118 + 1.119 +double 1.120 +js::math_acos_impl(MathCache *cache, double x) 1.121 +{ 1.122 + ACOS_IF_OUT_OF_RANGE(x); 1.123 + return cache->lookup(acos, x); 1.124 +} 1.125 + 1.126 +double 1.127 +js::math_acos_uncached(double x) 1.128 +{ 1.129 + ACOS_IF_OUT_OF_RANGE(x); 1.130 + return acos(x); 1.131 +} 1.132 + 1.133 +#undef ACOS_IF_OUT_OF_RANGE 1.134 + 1.135 +bool 1.136 +js::math_acos(JSContext *cx, unsigned argc, Value *vp) 1.137 +{ 1.138 + CallArgs args = CallArgsFromVp(argc, vp); 1.139 + 1.140 + if (args.length() == 0) { 1.141 + args.rval().setNaN(); 1.142 + return true; 1.143 + } 1.144 + 1.145 + double x; 1.146 + if (!ToNumber(cx, args[0], &x)) 1.147 + return false; 1.148 + 1.149 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.150 + if (!mathCache) 1.151 + return false; 1.152 + 1.153 + double z = math_acos_impl(mathCache, x); 1.154 + args.rval().setDouble(z); 1.155 + return true; 1.156 +} 1.157 + 1.158 +#if defined(SOLARIS) && defined(__GNUC__) 1.159 +#define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN(); 1.160 +#else 1.161 +#define ASIN_IF_OUT_OF_RANGE(x) 1.162 +#endif 1.163 + 1.164 +double 1.165 +js::math_asin_impl(MathCache *cache, double x) 1.166 +{ 1.167 + ASIN_IF_OUT_OF_RANGE(x); 1.168 + return cache->lookup(asin, x); 1.169 +} 1.170 + 1.171 +double 1.172 +js::math_asin_uncached(double x) 1.173 +{ 1.174 + ASIN_IF_OUT_OF_RANGE(x); 1.175 + return asin(x); 1.176 +} 1.177 + 1.178 +#undef ASIN_IF_OUT_OF_RANGE 1.179 + 1.180 +bool 1.181 +js::math_asin(JSContext *cx, unsigned argc, Value *vp) 1.182 +{ 1.183 + CallArgs args = CallArgsFromVp(argc, vp); 1.184 + 1.185 + if (args.length() == 0) { 1.186 + args.rval().setNaN(); 1.187 + return true; 1.188 + } 1.189 + 1.190 + double x; 1.191 + if (!ToNumber(cx, args[0], &x)) 1.192 + return false; 1.193 + 1.194 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.195 + if (!mathCache) 1.196 + return false; 1.197 + 1.198 + double z = math_asin_impl(mathCache, x); 1.199 + args.rval().setDouble(z); 1.200 + return true; 1.201 +} 1.202 + 1.203 +double 1.204 +js::math_atan_impl(MathCache *cache, double x) 1.205 +{ 1.206 + return cache->lookup(atan, x); 1.207 +} 1.208 + 1.209 +double 1.210 +js::math_atan_uncached(double x) 1.211 +{ 1.212 + return atan(x); 1.213 +} 1.214 + 1.215 +bool 1.216 +js::math_atan(JSContext *cx, unsigned argc, Value *vp) 1.217 +{ 1.218 + CallArgs args = CallArgsFromVp(argc, vp); 1.219 + 1.220 + if (args.length() == 0) { 1.221 + args.rval().setNaN(); 1.222 + return true; 1.223 + } 1.224 + 1.225 + double x; 1.226 + if (!ToNumber(cx, args[0], &x)) 1.227 + return false; 1.228 + 1.229 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.230 + if (!mathCache) 1.231 + return false; 1.232 + 1.233 + double z = math_atan_impl(mathCache, x); 1.234 + args.rval().setDouble(z); 1.235 + return true; 1.236 +} 1.237 + 1.238 +double 1.239 +js::ecmaAtan2(double y, double x) 1.240 +{ 1.241 +#if defined(_MSC_VER) 1.242 + /* 1.243 + * MSVC's atan2 does not yield the result demanded by ECMA when both x 1.244 + * and y are infinite. 1.245 + * - The result is a multiple of pi/4. 1.246 + * - The sign of y determines the sign of the result. 1.247 + * - The sign of x determines the multiplicator, 1 or 3. 1.248 + */ 1.249 + if (IsInfinite(y) && IsInfinite(x)) { 1.250 + double z = js_copysign(M_PI / 4, y); 1.251 + if (x < 0) 1.252 + z *= 3; 1.253 + return z; 1.254 + } 1.255 +#endif 1.256 + 1.257 +#if defined(SOLARIS) && defined(__GNUC__) 1.258 + if (y == 0) { 1.259 + if (IsNegativeZero(x)) 1.260 + return js_copysign(M_PI, y); 1.261 + if (x == 0) 1.262 + return y; 1.263 + } 1.264 +#endif 1.265 + return atan2(y, x); 1.266 +} 1.267 + 1.268 +bool 1.269 +js::math_atan2(JSContext *cx, unsigned argc, Value *vp) 1.270 +{ 1.271 + CallArgs args = CallArgsFromVp(argc, vp); 1.272 + 1.273 + double y; 1.274 + if (!ToNumber(cx, args.get(0), &y)) 1.275 + return false; 1.276 + 1.277 + double x; 1.278 + if (!ToNumber(cx, args.get(1), &x)) 1.279 + return false; 1.280 + 1.281 + double z = ecmaAtan2(y, x); 1.282 + args.rval().setDouble(z); 1.283 + return true; 1.284 +} 1.285 + 1.286 +double 1.287 +js::math_ceil_impl(double x) 1.288 +{ 1.289 +#ifdef __APPLE__ 1.290 + if (x < 0 && x > -1.0) 1.291 + return js_copysign(0, -1); 1.292 +#endif 1.293 + return ceil(x); 1.294 +} 1.295 + 1.296 +bool 1.297 +js::math_ceil(JSContext *cx, unsigned argc, Value *vp) 1.298 +{ 1.299 + CallArgs args = CallArgsFromVp(argc, vp); 1.300 + 1.301 + if (args.length() == 0) { 1.302 + args.rval().setNaN(); 1.303 + return true; 1.304 + } 1.305 + 1.306 + double x; 1.307 + if (!ToNumber(cx, args[0], &x)) 1.308 + return false; 1.309 + 1.310 + double z = math_ceil_impl(x); 1.311 + args.rval().setNumber(z); 1.312 + return true; 1.313 +} 1.314 + 1.315 +bool 1.316 +js::math_clz32(JSContext *cx, unsigned argc, Value *vp) 1.317 +{ 1.318 + CallArgs args = CallArgsFromVp(argc, vp); 1.319 + 1.320 + if (args.length() == 0) { 1.321 + args.rval().setInt32(32); 1.322 + return true; 1.323 + } 1.324 + 1.325 + uint32_t n; 1.326 + if (!ToUint32(cx, args[0], &n)) 1.327 + return false; 1.328 + 1.329 + if (n == 0) { 1.330 + args.rval().setInt32(32); 1.331 + return true; 1.332 + } 1.333 + 1.334 + args.rval().setInt32(mozilla::CountLeadingZeroes32(n)); 1.335 + return true; 1.336 +} 1.337 + 1.338 +double 1.339 +js::math_cos_impl(MathCache *cache, double x) 1.340 +{ 1.341 + return cache->lookup(cos, x); 1.342 +} 1.343 + 1.344 +double 1.345 +js::math_cos_uncached(double x) 1.346 +{ 1.347 + return cos(x); 1.348 +} 1.349 + 1.350 +bool 1.351 +js::math_cos(JSContext *cx, unsigned argc, Value *vp) 1.352 +{ 1.353 + CallArgs args = CallArgsFromVp(argc, vp); 1.354 + 1.355 + if (args.length() == 0) { 1.356 + args.rval().setNaN(); 1.357 + return true; 1.358 + } 1.359 + 1.360 + double x; 1.361 + if (!ToNumber(cx, args[0], &x)) 1.362 + return false; 1.363 + 1.364 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.365 + if (!mathCache) 1.366 + return false; 1.367 + 1.368 + double z = math_cos_impl(mathCache, x); 1.369 + args.rval().setDouble(z); 1.370 + return true; 1.371 +} 1.372 + 1.373 +#ifdef _WIN32 1.374 +#define EXP_IF_OUT_OF_RANGE(x) \ 1.375 + if (!IsNaN(x)) { \ 1.376 + if (x == PositiveInfinity<double>()) \ 1.377 + return PositiveInfinity<double>(); \ 1.378 + if (x == NegativeInfinity<double>()) \ 1.379 + return 0.0; \ 1.380 + } 1.381 +#else 1.382 +#define EXP_IF_OUT_OF_RANGE(x) 1.383 +#endif 1.384 + 1.385 +double 1.386 +js::math_exp_impl(MathCache *cache, double x) 1.387 +{ 1.388 + EXP_IF_OUT_OF_RANGE(x); 1.389 + return cache->lookup(exp, x); 1.390 +} 1.391 + 1.392 +double 1.393 +js::math_exp_uncached(double x) 1.394 +{ 1.395 + EXP_IF_OUT_OF_RANGE(x); 1.396 + return exp(x); 1.397 +} 1.398 + 1.399 +#undef EXP_IF_OUT_OF_RANGE 1.400 + 1.401 +bool 1.402 +js::math_exp(JSContext *cx, unsigned argc, Value *vp) 1.403 +{ 1.404 + CallArgs args = CallArgsFromVp(argc, vp); 1.405 + 1.406 + if (args.length() == 0) { 1.407 + args.rval().setNaN(); 1.408 + return true; 1.409 + } 1.410 + 1.411 + double x; 1.412 + if (!ToNumber(cx, args[0], &x)) 1.413 + return false; 1.414 + 1.415 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.416 + if (!mathCache) 1.417 + return false; 1.418 + 1.419 + double z = math_exp_impl(mathCache, x); 1.420 + args.rval().setNumber(z); 1.421 + return true; 1.422 +} 1.423 + 1.424 +double 1.425 +js::math_floor_impl(double x) 1.426 +{ 1.427 + return floor(x); 1.428 +} 1.429 + 1.430 +bool 1.431 +js::math_floor(JSContext *cx, unsigned argc, Value *vp) 1.432 +{ 1.433 + CallArgs args = CallArgsFromVp(argc, vp); 1.434 + 1.435 + if (args.length() == 0) { 1.436 + args.rval().setNaN(); 1.437 + return true; 1.438 + } 1.439 + 1.440 + double x; 1.441 + if (!ToNumber(cx, args[0], &x)) 1.442 + return false; 1.443 + 1.444 + double z = math_floor_impl(x); 1.445 + args.rval().setNumber(z); 1.446 + return true; 1.447 +} 1.448 + 1.449 +bool 1.450 +js::math_imul(JSContext *cx, unsigned argc, Value *vp) 1.451 +{ 1.452 + CallArgs args = CallArgsFromVp(argc, vp); 1.453 + 1.454 + uint32_t a = 0, b = 0; 1.455 + if (args.hasDefined(0) && !ToUint32(cx, args[0], &a)) 1.456 + return false; 1.457 + if (args.hasDefined(1) && !ToUint32(cx, args[1], &b)) 1.458 + return false; 1.459 + 1.460 + uint32_t product = a * b; 1.461 + args.rval().setInt32(product > INT32_MAX 1.462 + ? int32_t(INT32_MIN + (product - INT32_MAX - 1)) 1.463 + : int32_t(product)); 1.464 + return true; 1.465 +} 1.466 + 1.467 +// Implements Math.fround (20.2.2.16) up to step 3 1.468 +bool 1.469 +js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out) 1.470 +{ 1.471 + double d; 1.472 + bool success = ToNumber(cx, v, &d); 1.473 + *out = static_cast<float>(d); 1.474 + return success; 1.475 +} 1.476 + 1.477 +bool 1.478 +js::math_fround(JSContext *cx, unsigned argc, Value *vp) 1.479 +{ 1.480 + CallArgs args = CallArgsFromVp(argc, vp); 1.481 + 1.482 + if (args.length() == 0) { 1.483 + args.rval().setNaN(); 1.484 + return true; 1.485 + } 1.486 + 1.487 + float f; 1.488 + if (!RoundFloat32(cx, args[0], &f)) 1.489 + return false; 1.490 + 1.491 + args.rval().setDouble(static_cast<double>(f)); 1.492 + return true; 1.493 +} 1.494 + 1.495 +#if defined(SOLARIS) && defined(__GNUC__) 1.496 +#define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN(); 1.497 +#else 1.498 +#define LOG_IF_OUT_OF_RANGE(x) 1.499 +#endif 1.500 + 1.501 +double 1.502 +js::math_log_impl(MathCache *cache, double x) 1.503 +{ 1.504 + LOG_IF_OUT_OF_RANGE(x); 1.505 + return cache->lookup(log, x); 1.506 +} 1.507 + 1.508 +double 1.509 +js::math_log_uncached(double x) 1.510 +{ 1.511 + LOG_IF_OUT_OF_RANGE(x); 1.512 + return log(x); 1.513 +} 1.514 + 1.515 +#undef LOG_IF_OUT_OF_RANGE 1.516 + 1.517 +bool 1.518 +js::math_log(JSContext *cx, unsigned argc, Value *vp) 1.519 +{ 1.520 + CallArgs args = CallArgsFromVp(argc, vp); 1.521 + 1.522 + if (args.length() == 0) { 1.523 + args.rval().setNaN(); 1.524 + return true; 1.525 + } 1.526 + 1.527 + double x; 1.528 + if (!ToNumber(cx, args[0], &x)) 1.529 + return false; 1.530 + 1.531 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.532 + if (!mathCache) 1.533 + return false; 1.534 + 1.535 + double z = math_log_impl(mathCache, x); 1.536 + args.rval().setNumber(z); 1.537 + return true; 1.538 +} 1.539 + 1.540 +bool 1.541 +js_math_max(JSContext *cx, unsigned argc, Value *vp) 1.542 +{ 1.543 + CallArgs args = CallArgsFromVp(argc, vp); 1.544 + 1.545 + double maxval = NegativeInfinity<double>(); 1.546 + for (unsigned i = 0; i < args.length(); i++) { 1.547 + double x; 1.548 + if (!ToNumber(cx, args[i], &x)) 1.549 + return false; 1.550 + // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0 1.551 + if (x > maxval || IsNaN(x) || (x == maxval && IsNegative(maxval))) 1.552 + maxval = x; 1.553 + } 1.554 + args.rval().setNumber(maxval); 1.555 + return true; 1.556 +} 1.557 + 1.558 +bool 1.559 +js_math_min(JSContext *cx, unsigned argc, Value *vp) 1.560 +{ 1.561 + CallArgs args = CallArgsFromVp(argc, vp); 1.562 + 1.563 + double minval = PositiveInfinity<double>(); 1.564 + for (unsigned i = 0; i < args.length(); i++) { 1.565 + double x; 1.566 + if (!ToNumber(cx, args[i], &x)) 1.567 + return false; 1.568 + // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0 1.569 + if (x < minval || IsNaN(x) || (x == minval && IsNegativeZero(x))) 1.570 + minval = x; 1.571 + } 1.572 + args.rval().setNumber(minval); 1.573 + return true; 1.574 +} 1.575 + 1.576 +// Disable PGO for Math.pow() and related functions (see bug 791214). 1.577 +#if defined(_MSC_VER) 1.578 +# pragma optimize("g", off) 1.579 +#endif 1.580 +double 1.581 +js::powi(double x, int y) 1.582 +{ 1.583 + unsigned n = (y < 0) ? -y : y; 1.584 + double m = x; 1.585 + double p = 1; 1.586 + while (true) { 1.587 + if ((n & 1) != 0) p *= m; 1.588 + n >>= 1; 1.589 + if (n == 0) { 1.590 + if (y < 0) { 1.591 + // Unfortunately, we have to be careful when p has reached 1.592 + // infinity in the computation, because sometimes the higher 1.593 + // internal precision in the pow() implementation would have 1.594 + // given us a finite p. This happens very rarely. 1.595 + 1.596 + double result = 1.0 / p; 1.597 + return (result == 0 && IsInfinite(p)) 1.598 + ? pow(x, static_cast<double>(y)) // Avoid pow(double, int). 1.599 + : result; 1.600 + } 1.601 + 1.602 + return p; 1.603 + } 1.604 + m *= m; 1.605 + } 1.606 +} 1.607 +#if defined(_MSC_VER) 1.608 +# pragma optimize("", on) 1.609 +#endif 1.610 + 1.611 +// Disable PGO for Math.pow() and related functions (see bug 791214). 1.612 +#if defined(_MSC_VER) 1.613 +# pragma optimize("g", off) 1.614 +#endif 1.615 +double 1.616 +js::ecmaPow(double x, double y) 1.617 +{ 1.618 + /* 1.619 + * Use powi if the exponent is an integer-valued double. We don't have to 1.620 + * check for NaN since a comparison with NaN is always false. 1.621 + */ 1.622 + int32_t yi; 1.623 + if (NumberEqualsInt32(y, &yi)) 1.624 + return powi(x, yi); 1.625 + 1.626 + /* 1.627 + * Because C99 and ECMA specify different behavior for pow(), 1.628 + * we need to wrap the libm call to make it ECMA compliant. 1.629 + */ 1.630 + if (!IsFinite(y) && (x == 1.0 || x == -1.0)) 1.631 + return GenericNaN(); 1.632 + 1.633 + /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */ 1.634 + if (y == 0) 1.635 + return 1; 1.636 + 1.637 + /* 1.638 + * Special case for square roots. Note that pow(x, 0.5) != sqrt(x) 1.639 + * when x = -0.0, so we have to guard for this. 1.640 + */ 1.641 + if (IsFinite(x) && x != 0.0) { 1.642 + if (y == 0.5) 1.643 + return sqrt(x); 1.644 + if (y == -0.5) 1.645 + return 1.0 / sqrt(x); 1.646 + } 1.647 + return pow(x, y); 1.648 +} 1.649 +#if defined(_MSC_VER) 1.650 +# pragma optimize("", on) 1.651 +#endif 1.652 + 1.653 +// Disable PGO for Math.pow() and related functions (see bug 791214). 1.654 +#if defined(_MSC_VER) 1.655 +# pragma optimize("g", off) 1.656 +#endif 1.657 +bool 1.658 +js_math_pow(JSContext *cx, unsigned argc, Value *vp) 1.659 +{ 1.660 + CallArgs args = CallArgsFromVp(argc, vp); 1.661 + 1.662 + double x; 1.663 + if (!ToNumber(cx, args.get(0), &x)) 1.664 + return false; 1.665 + 1.666 + double y; 1.667 + if (!ToNumber(cx, args.get(1), &y)) 1.668 + return false; 1.669 + 1.670 + double z = ecmaPow(x, y); 1.671 + args.rval().setNumber(z); 1.672 + return true; 1.673 +} 1.674 +#if defined(_MSC_VER) 1.675 +# pragma optimize("", on) 1.676 +#endif 1.677 + 1.678 +static uint64_t 1.679 +random_generateSeed() 1.680 +{ 1.681 + union { 1.682 + uint8_t u8[8]; 1.683 + uint32_t u32[2]; 1.684 + uint64_t u64; 1.685 + } seed; 1.686 + seed.u64 = 0; 1.687 + 1.688 +#if defined(XP_WIN) 1.689 + /* 1.690 + * Our PRNG only uses 48 bits, so calling rand_s() twice to get 64 bits is 1.691 + * probably overkill. 1.692 + */ 1.693 + rand_s(&seed.u32[0]); 1.694 +#elif defined(XP_UNIX) 1.695 + /* 1.696 + * In the unlikely event we can't read /dev/urandom, there's not much we can 1.697 + * do, so just mix in the fd error code and the current time. 1.698 + */ 1.699 + int fd = open("/dev/urandom", O_RDONLY); 1.700 + MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom"); 1.701 + if (fd >= 0) { 1.702 + read(fd, seed.u8, mozilla::ArrayLength(seed.u8)); 1.703 + close(fd); 1.704 + } 1.705 + seed.u32[0] ^= fd; 1.706 +#else 1.707 +# error "Platform needs to implement random_generateSeed()" 1.708 +#endif 1.709 + 1.710 + seed.u32[1] ^= PRMJ_Now(); 1.711 + return seed.u64; 1.712 +} 1.713 + 1.714 +static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL; 1.715 +static const uint64_t RNG_ADDEND = 0xBLL; 1.716 +static const uint64_t RNG_MASK = (1LL << 48) - 1; 1.717 +static const double RNG_DSCALE = double(1LL << 53); 1.718 + 1.719 +/* 1.720 + * Math.random() support, lifted from java.util.Random.java. 1.721 + */ 1.722 +static void 1.723 +random_initState(uint64_t *rngState) 1.724 +{ 1.725 + /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */ 1.726 + uint64_t seed = random_generateSeed(); 1.727 + seed ^= (seed >> 16); 1.728 + *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK; 1.729 +} 1.730 + 1.731 +uint64_t 1.732 +random_next(uint64_t *rngState, int bits) 1.733 +{ 1.734 + MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState"); 1.735 + MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range"); 1.736 + 1.737 + if (*rngState == 0) { 1.738 + random_initState(rngState); 1.739 + } 1.740 + 1.741 + uint64_t nextstate = *rngState * RNG_MULTIPLIER; 1.742 + nextstate += RNG_ADDEND; 1.743 + nextstate &= RNG_MASK; 1.744 + *rngState = nextstate; 1.745 + return nextstate >> (48 - bits); 1.746 +} 1.747 + 1.748 +static inline double 1.749 +random_nextDouble(JSContext *cx) 1.750 +{ 1.751 + uint64_t *rng = &cx->compartment()->rngState; 1.752 + return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE; 1.753 +} 1.754 + 1.755 +double 1.756 +math_random_no_outparam(JSContext *cx) 1.757 +{ 1.758 + /* Calculate random without memory traffic, for use in the JITs. */ 1.759 + return random_nextDouble(cx); 1.760 +} 1.761 + 1.762 +bool 1.763 +js_math_random(JSContext *cx, unsigned argc, Value *vp) 1.764 +{ 1.765 + CallArgs args = CallArgsFromVp(argc, vp); 1.766 + double z = random_nextDouble(cx); 1.767 + args.rval().setDouble(z); 1.768 + return true; 1.769 +} 1.770 + 1.771 +double 1.772 +js::math_round_impl(double x) 1.773 +{ 1.774 + int32_t ignored; 1.775 + if (NumberIsInt32(x, &ignored)) 1.776 + return x; 1.777 + 1.778 + /* Some numbers are so big that adding 0.5 would give the wrong number. */ 1.779 + if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<double>::ExponentShift)) 1.780 + return x; 1.781 + 1.782 + return js_copysign(floor(x + 0.5), x); 1.783 +} 1.784 + 1.785 +float 1.786 +js::math_roundf_impl(float x) 1.787 +{ 1.788 + int32_t ignored; 1.789 + if (NumberIsInt32(x, &ignored)) 1.790 + return x; 1.791 + 1.792 + /* Some numbers are so big that adding 0.5 would give the wrong number. */ 1.793 + if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<float>::ExponentShift)) 1.794 + return x; 1.795 + 1.796 + return js_copysign(floorf(x + 0.5f), x); 1.797 +} 1.798 + 1.799 +bool /* ES5 15.8.2.15. */ 1.800 +js::math_round(JSContext *cx, unsigned argc, Value *vp) 1.801 +{ 1.802 + CallArgs args = CallArgsFromVp(argc, vp); 1.803 + 1.804 + if (args.length() == 0) { 1.805 + args.rval().setNaN(); 1.806 + return true; 1.807 + } 1.808 + 1.809 + double x; 1.810 + if (!ToNumber(cx, args[0], &x)) 1.811 + return false; 1.812 + 1.813 + double z = math_round_impl(x); 1.814 + args.rval().setNumber(z); 1.815 + return true; 1.816 +} 1.817 + 1.818 +double 1.819 +js::math_sin_impl(MathCache *cache, double x) 1.820 +{ 1.821 + return cache->lookup(sin, x); 1.822 +} 1.823 + 1.824 +double 1.825 +js::math_sin_uncached(double x) 1.826 +{ 1.827 + return sin(x); 1.828 +} 1.829 + 1.830 +bool 1.831 +js::math_sin(JSContext *cx, unsigned argc, Value *vp) 1.832 +{ 1.833 + CallArgs args = CallArgsFromVp(argc, vp); 1.834 + 1.835 + if (args.length() == 0) { 1.836 + args.rval().setNaN(); 1.837 + return true; 1.838 + } 1.839 + 1.840 + double x; 1.841 + if (!ToNumber(cx, args[0], &x)) 1.842 + return false; 1.843 + 1.844 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.845 + if (!mathCache) 1.846 + return false; 1.847 + 1.848 + double z = math_sin_impl(mathCache, x); 1.849 + args.rval().setDouble(z); 1.850 + return true; 1.851 +} 1.852 + 1.853 +bool 1.854 +js_math_sqrt(JSContext *cx, unsigned argc, Value *vp) 1.855 +{ 1.856 + CallArgs args = CallArgsFromVp(argc, vp); 1.857 + 1.858 + if (args.length() == 0) { 1.859 + args.rval().setNaN(); 1.860 + return true; 1.861 + } 1.862 + 1.863 + double x; 1.864 + if (!ToNumber(cx, args[0], &x)) 1.865 + return false; 1.866 + 1.867 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.868 + if (!mathCache) 1.869 + return false; 1.870 + 1.871 + double z = mathCache->lookup(sqrt, x); 1.872 + args.rval().setDouble(z); 1.873 + return true; 1.874 +} 1.875 + 1.876 +double 1.877 +js::math_tan_impl(MathCache *cache, double x) 1.878 +{ 1.879 + return cache->lookup(tan, x); 1.880 +} 1.881 + 1.882 +double 1.883 +js::math_tan_uncached(double x) 1.884 +{ 1.885 + return tan(x); 1.886 +} 1.887 + 1.888 +bool 1.889 +js::math_tan(JSContext *cx, unsigned argc, Value *vp) 1.890 +{ 1.891 + CallArgs args = CallArgsFromVp(argc, vp); 1.892 + 1.893 + if (args.length() == 0) { 1.894 + args.rval().setNaN(); 1.895 + return true; 1.896 + } 1.897 + 1.898 + double x; 1.899 + if (!ToNumber(cx, args[0], &x)) 1.900 + return false; 1.901 + 1.902 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.903 + if (!mathCache) 1.904 + return false; 1.905 + 1.906 + double z = math_tan_impl(mathCache, x); 1.907 + args.rval().setDouble(z); 1.908 + return true; 1.909 +} 1.910 + 1.911 + 1.912 +typedef double (*UnaryMathFunctionType)(MathCache *cache, double); 1.913 + 1.914 +template <UnaryMathFunctionType F> 1.915 +static bool math_function(JSContext *cx, unsigned argc, Value *vp) 1.916 +{ 1.917 + CallArgs args = CallArgsFromVp(argc, vp); 1.918 + if (args.length() == 0) { 1.919 + args.rval().setNumber(GenericNaN()); 1.920 + return true; 1.921 + } 1.922 + 1.923 + double x; 1.924 + if (!ToNumber(cx, args[0], &x)) 1.925 + return false; 1.926 + 1.927 + MathCache *mathCache = cx->runtime()->getMathCache(cx); 1.928 + if (!mathCache) 1.929 + return false; 1.930 + double z = F(mathCache, x); 1.931 + args.rval().setNumber(z); 1.932 + 1.933 + return true; 1.934 +} 1.935 + 1.936 + 1.937 + 1.938 +double 1.939 +js::math_log10_impl(MathCache *cache, double x) 1.940 +{ 1.941 + return cache->lookup(log10, x); 1.942 +} 1.943 + 1.944 +double 1.945 +js::math_log10_uncached(double x) 1.946 +{ 1.947 + return log10(x); 1.948 +} 1.949 + 1.950 +bool 1.951 +js::math_log10(JSContext *cx, unsigned argc, Value *vp) 1.952 +{ 1.953 + return math_function<math_log10_impl>(cx, argc, vp); 1.954 +} 1.955 + 1.956 +#if !HAVE_LOG2 1.957 +double log2(double x) 1.958 +{ 1.959 + return log(x) / M_LN2; 1.960 +} 1.961 +#endif 1.962 + 1.963 +double 1.964 +js::math_log2_impl(MathCache *cache, double x) 1.965 +{ 1.966 + return cache->lookup(log2, x); 1.967 +} 1.968 + 1.969 +double 1.970 +js::math_log2_uncached(double x) 1.971 +{ 1.972 + return log2(x); 1.973 +} 1.974 + 1.975 +bool 1.976 +js::math_log2(JSContext *cx, unsigned argc, Value *vp) 1.977 +{ 1.978 + return math_function<math_log2_impl>(cx, argc, vp); 1.979 +} 1.980 + 1.981 +#if !HAVE_LOG1P 1.982 +double log1p(double x) 1.983 +{ 1.984 + if (fabs(x) < 1e-4) { 1.985 + /* 1.986 + * Use Taylor approx. log(1 + x) = x - x^2 / 2 + x^3 / 3 - x^4 / 4 with error x^5 / 5 1.987 + * Since |x| < 10^-4, |x|^5 < 10^-20, relative error less than 10^-16 1.988 + */ 1.989 + double z = -(x * x * x * x) / 4 + (x * x * x) / 3 - (x * x) / 2 + x; 1.990 + return z; 1.991 + } else { 1.992 + /* For other large enough values of x use direct computation */ 1.993 + return log(1.0 + x); 1.994 + } 1.995 +} 1.996 +#endif 1.997 + 1.998 +#ifdef __APPLE__ 1.999 +// Ensure that log1p(-0) is -0. 1.1000 +#define LOG1P_IF_OUT_OF_RANGE(x) if (x == 0) return x; 1.1001 +#else 1.1002 +#define LOG1P_IF_OUT_OF_RANGE(x) 1.1003 +#endif 1.1004 + 1.1005 +double 1.1006 +js::math_log1p_impl(MathCache *cache, double x) 1.1007 +{ 1.1008 + LOG1P_IF_OUT_OF_RANGE(x); 1.1009 + return cache->lookup(log1p, x); 1.1010 +} 1.1011 + 1.1012 +double 1.1013 +js::math_log1p_uncached(double x) 1.1014 +{ 1.1015 + LOG1P_IF_OUT_OF_RANGE(x); 1.1016 + return log1p(x); 1.1017 +} 1.1018 + 1.1019 +#undef LOG1P_IF_OUT_OF_RANGE 1.1020 + 1.1021 +bool 1.1022 +js::math_log1p(JSContext *cx, unsigned argc, Value *vp) 1.1023 +{ 1.1024 + return math_function<math_log1p_impl>(cx, argc, vp); 1.1025 +} 1.1026 + 1.1027 +#if !HAVE_EXPM1 1.1028 +double expm1(double x) 1.1029 +{ 1.1030 + /* Special handling for -0 */ 1.1031 + if (x == 0.0) 1.1032 + return x; 1.1033 + 1.1034 + if (fabs(x) < 1e-5) { 1.1035 + /* 1.1036 + * Use Taylor approx. exp(x) - 1 = x + x^2 / 2 + x^3 / 6 with error x^4 / 24 1.1037 + * Since |x| < 10^-5, |x|^4 < 10^-20, relative error less than 10^-15 1.1038 + */ 1.1039 + double z = (x * x * x) / 6 + (x * x) / 2 + x; 1.1040 + return z; 1.1041 + } else { 1.1042 + /* For other large enough values of x use direct computation */ 1.1043 + return exp(x) - 1.0; 1.1044 + } 1.1045 +} 1.1046 +#endif 1.1047 + 1.1048 +double 1.1049 +js::math_expm1_impl(MathCache *cache, double x) 1.1050 +{ 1.1051 + return cache->lookup(expm1, x); 1.1052 +} 1.1053 + 1.1054 +double 1.1055 +js::math_expm1_uncached(double x) 1.1056 +{ 1.1057 + return expm1(x); 1.1058 +} 1.1059 + 1.1060 +bool 1.1061 +js::math_expm1(JSContext *cx, unsigned argc, Value *vp) 1.1062 +{ 1.1063 + return math_function<math_expm1_impl>(cx, argc, vp); 1.1064 +} 1.1065 + 1.1066 +#if !HAVE_SQRT1PM1 1.1067 +/* This algorithm computes sqrt(1+x)-1 for small x */ 1.1068 +double sqrt1pm1(double x) 1.1069 +{ 1.1070 + if (fabs(x) > 0.75) 1.1071 + return sqrt(1 + x) - 1; 1.1072 + 1.1073 + return expm1(log1p(x) / 2); 1.1074 +} 1.1075 +#endif 1.1076 + 1.1077 + 1.1078 +double 1.1079 +js::math_cosh_impl(MathCache *cache, double x) 1.1080 +{ 1.1081 + return cache->lookup(cosh, x); 1.1082 +} 1.1083 + 1.1084 +double 1.1085 +js::math_cosh_uncached(double x) 1.1086 +{ 1.1087 + return cosh(x); 1.1088 +} 1.1089 + 1.1090 +bool 1.1091 +js::math_cosh(JSContext *cx, unsigned argc, Value *vp) 1.1092 +{ 1.1093 + return math_function<math_cosh_impl>(cx, argc, vp); 1.1094 +} 1.1095 + 1.1096 +double 1.1097 +js::math_sinh_impl(MathCache *cache, double x) 1.1098 +{ 1.1099 + return cache->lookup(sinh, x); 1.1100 +} 1.1101 + 1.1102 +double 1.1103 +js::math_sinh_uncached(double x) 1.1104 +{ 1.1105 + return sinh(x); 1.1106 +} 1.1107 + 1.1108 +bool 1.1109 +js::math_sinh(JSContext *cx, unsigned argc, Value *vp) 1.1110 +{ 1.1111 + return math_function<math_sinh_impl>(cx, argc, vp); 1.1112 +} 1.1113 + 1.1114 +double 1.1115 +js::math_tanh_impl(MathCache *cache, double x) 1.1116 +{ 1.1117 + return cache->lookup(tanh, x); 1.1118 +} 1.1119 + 1.1120 +double 1.1121 +js::math_tanh_uncached(double x) 1.1122 +{ 1.1123 + return tanh(x); 1.1124 +} 1.1125 + 1.1126 +bool 1.1127 +js::math_tanh(JSContext *cx, unsigned argc, Value *vp) 1.1128 +{ 1.1129 + return math_function<math_tanh_impl>(cx, argc, vp); 1.1130 +} 1.1131 + 1.1132 +#if !HAVE_ACOSH 1.1133 +double acosh(double x) 1.1134 +{ 1.1135 + const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon()); 1.1136 + 1.1137 + if ((x - 1) >= SQUARE_ROOT_EPSILON) { 1.1138 + if (x > 1 / SQUARE_ROOT_EPSILON) { 1.1139 + /* 1.1140 + * http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/06/01/0001/ 1.1141 + * approximation by laurent series in 1/x at 0+ order from -1 to 0 1.1142 + */ 1.1143 + return log(x) + M_LN2; 1.1144 + } else if (x < 1.5) { 1.1145 + // This is just a rearrangement of the standard form below 1.1146 + // devised to minimize loss of precision when x ~ 1: 1.1147 + double y = x - 1; 1.1148 + return log1p(y + sqrt(y * y + 2 * y)); 1.1149 + } else { 1.1150 + // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/ 1.1151 + return log(x + sqrt(x * x - 1)); 1.1152 + } 1.1153 + } else { 1.1154 + // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/ 1.1155 + double y = x - 1; 1.1156 + // approximation by taylor series in y at 0 up to order 2. 1.1157 + // If x is less than 1, sqrt(2 * y) is NaN and the result is NaN. 1.1158 + return sqrt(2 * y) * (1 - y / 12 + 3 * y * y / 160); 1.1159 + } 1.1160 +} 1.1161 +#endif 1.1162 + 1.1163 +double 1.1164 +js::math_acosh_impl(MathCache *cache, double x) 1.1165 +{ 1.1166 + return cache->lookup(acosh, x); 1.1167 +} 1.1168 + 1.1169 +double 1.1170 +js::math_acosh_uncached(double x) 1.1171 +{ 1.1172 + return acosh(x); 1.1173 +} 1.1174 + 1.1175 +bool 1.1176 +js::math_acosh(JSContext *cx, unsigned argc, Value *vp) 1.1177 +{ 1.1178 + return math_function<math_acosh_impl>(cx, argc, vp); 1.1179 +} 1.1180 + 1.1181 +#if !HAVE_ASINH 1.1182 +// Bug 899712 - gcc incorrectly rewrites -asinh(-x) to asinh(x) when overriding 1.1183 +// asinh. 1.1184 +static double my_asinh(double x) 1.1185 +{ 1.1186 + const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon()); 1.1187 + const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON); 1.1188 + 1.1189 + if (x >= FOURTH_ROOT_EPSILON) { 1.1190 + if (x > 1 / SQUARE_ROOT_EPSILON) 1.1191 + // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/06/01/0001/ 1.1192 + // approximation by laurent series in 1/x at 0+ order from -1 to 1 1.1193 + return M_LN2 + log(x) + 1 / (4 * x * x); 1.1194 + else if (x < 0.5) 1.1195 + return log1p(x + sqrt1pm1(x * x)); 1.1196 + else 1.1197 + return log(x + sqrt(x * x + 1)); 1.1198 + } else if (x <= -FOURTH_ROOT_EPSILON) { 1.1199 + return -my_asinh(-x); 1.1200 + } else { 1.1201 + // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/03/01/0001/ 1.1202 + // approximation by taylor series in x at 0 up to order 2 1.1203 + double result = x; 1.1204 + 1.1205 + if (fabs(x) >= SQUARE_ROOT_EPSILON) { 1.1206 + double x3 = x * x * x; 1.1207 + // approximation by taylor series in x at 0 up to order 4 1.1208 + result -= x3 / 6; 1.1209 + } 1.1210 + 1.1211 + return result; 1.1212 + } 1.1213 +} 1.1214 +#endif 1.1215 + 1.1216 +double 1.1217 +js::math_asinh_impl(MathCache *cache, double x) 1.1218 +{ 1.1219 +#ifdef HAVE_ASINH 1.1220 + return cache->lookup(asinh, x); 1.1221 +#else 1.1222 + return cache->lookup(my_asinh, x); 1.1223 +#endif 1.1224 +} 1.1225 + 1.1226 +double 1.1227 +js::math_asinh_uncached(double x) 1.1228 +{ 1.1229 +#ifdef HAVE_ASINH 1.1230 + return asinh(x); 1.1231 +#else 1.1232 + return my_asinh(x); 1.1233 +#endif 1.1234 +} 1.1235 + 1.1236 +bool 1.1237 +js::math_asinh(JSContext *cx, unsigned argc, Value *vp) 1.1238 +{ 1.1239 + return math_function<math_asinh_impl>(cx, argc, vp); 1.1240 +} 1.1241 + 1.1242 +#if !HAVE_ATANH 1.1243 +double atanh(double x) 1.1244 +{ 1.1245 + const double EPSILON = std::numeric_limits<double>::epsilon(); 1.1246 + const double SQUARE_ROOT_EPSILON = sqrt(EPSILON); 1.1247 + const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON); 1.1248 + 1.1249 + if (fabs(x) >= FOURTH_ROOT_EPSILON) { 1.1250 + // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/02/ 1.1251 + if (fabs(x) < 0.5) 1.1252 + return (log1p(x) - log1p(-x)) / 2; 1.1253 + 1.1254 + return log((1 + x) / (1 - x)) / 2; 1.1255 + } else { 1.1256 + // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/ 1.1257 + // approximation by taylor series in x at 0 up to order 2 1.1258 + double result = x; 1.1259 + 1.1260 + if (fabs(x) >= SQUARE_ROOT_EPSILON) { 1.1261 + double x3 = x * x * x; 1.1262 + result += x3 / 3; 1.1263 + } 1.1264 + 1.1265 + return result; 1.1266 + } 1.1267 +} 1.1268 +#endif 1.1269 + 1.1270 +double 1.1271 +js::math_atanh_impl(MathCache *cache, double x) 1.1272 +{ 1.1273 + return cache->lookup(atanh, x); 1.1274 +} 1.1275 + 1.1276 +double 1.1277 +js::math_atanh_uncached(double x) 1.1278 +{ 1.1279 + return atanh(x); 1.1280 +} 1.1281 + 1.1282 +bool 1.1283 +js::math_atanh(JSContext *cx, unsigned argc, Value *vp) 1.1284 +{ 1.1285 + return math_function<math_atanh_impl>(cx, argc, vp); 1.1286 +} 1.1287 + 1.1288 +/* Consistency wrapper for platform deviations in hypot() */ 1.1289 +double 1.1290 +js::ecmaHypot(double x, double y) 1.1291 +{ 1.1292 +#ifdef XP_WIN 1.1293 + /* 1.1294 + * Workaround MS hypot bug, where hypot(Infinity, NaN or Math.MIN_VALUE) 1.1295 + * is NaN, not Infinity. 1.1296 + */ 1.1297 + if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) { 1.1298 + return mozilla::PositiveInfinity<double>(); 1.1299 + } 1.1300 +#endif 1.1301 + return hypot(x, y); 1.1302 +} 1.1303 + 1.1304 +bool 1.1305 +js::math_hypot(JSContext *cx, unsigned argc, Value *vp) 1.1306 +{ 1.1307 + CallArgs args = CallArgsFromVp(argc, vp); 1.1308 + 1.1309 + // IonMonkey calls the system hypot function directly if two arguments are 1.1310 + // given. Do that here as well to get the same results. 1.1311 + if (args.length() == 2) { 1.1312 + double x, y; 1.1313 + if (!ToNumber(cx, args[0], &x)) 1.1314 + return false; 1.1315 + if (!ToNumber(cx, args[1], &y)) 1.1316 + return false; 1.1317 + 1.1318 + double result = ecmaHypot(x, y); 1.1319 + args.rval().setNumber(result); 1.1320 + return true; 1.1321 + } 1.1322 + 1.1323 + bool isInfinite = false; 1.1324 + bool isNaN = false; 1.1325 + 1.1326 + double scale = 0; 1.1327 + double sumsq = 1; 1.1328 + 1.1329 + for (unsigned i = 0; i < args.length(); i++) { 1.1330 + double x; 1.1331 + if (!ToNumber(cx, args[i], &x)) 1.1332 + return false; 1.1333 + 1.1334 + isInfinite |= mozilla::IsInfinite(x); 1.1335 + isNaN |= mozilla::IsNaN(x); 1.1336 + 1.1337 + double xabs = mozilla::Abs(x); 1.1338 + 1.1339 + if (scale < xabs) { 1.1340 + sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs); 1.1341 + scale = xabs; 1.1342 + } else if (scale != 0) { 1.1343 + sumsq += (xabs / scale) * (xabs / scale); 1.1344 + } 1.1345 + } 1.1346 + 1.1347 + double result = isInfinite ? PositiveInfinity<double>() : 1.1348 + isNaN ? GenericNaN() : 1.1349 + scale * sqrt(sumsq); 1.1350 + args.rval().setNumber(result); 1.1351 + return true; 1.1352 +} 1.1353 + 1.1354 +#if !HAVE_TRUNC 1.1355 +double trunc(double x) 1.1356 +{ 1.1357 + return x > 0 ? floor(x) : ceil(x); 1.1358 +} 1.1359 +#endif 1.1360 + 1.1361 +double 1.1362 +js::math_trunc_impl(MathCache *cache, double x) 1.1363 +{ 1.1364 + return cache->lookup(trunc, x); 1.1365 +} 1.1366 + 1.1367 +double 1.1368 +js::math_trunc_uncached(double x) 1.1369 +{ 1.1370 + return trunc(x); 1.1371 +} 1.1372 + 1.1373 +bool 1.1374 +js::math_trunc(JSContext *cx, unsigned argc, Value *vp) 1.1375 +{ 1.1376 + return math_function<math_trunc_impl>(cx, argc, vp); 1.1377 +} 1.1378 + 1.1379 +static double sign(double x) 1.1380 +{ 1.1381 + if (mozilla::IsNaN(x)) 1.1382 + return GenericNaN(); 1.1383 + 1.1384 + return x == 0 ? x : x < 0 ? -1 : 1; 1.1385 +} 1.1386 + 1.1387 +double 1.1388 +js::math_sign_impl(MathCache *cache, double x) 1.1389 +{ 1.1390 + return cache->lookup(sign, x); 1.1391 +} 1.1392 + 1.1393 +double 1.1394 +js::math_sign_uncached(double x) 1.1395 +{ 1.1396 + return sign(x); 1.1397 +} 1.1398 + 1.1399 +bool 1.1400 +js::math_sign(JSContext *cx, unsigned argc, Value *vp) 1.1401 +{ 1.1402 + return math_function<math_sign_impl>(cx, argc, vp); 1.1403 +} 1.1404 + 1.1405 +#if !HAVE_CBRT 1.1406 +double cbrt(double x) 1.1407 +{ 1.1408 + if (x > 0) { 1.1409 + return pow(x, 1.0 / 3.0); 1.1410 + } else if (x == 0) { 1.1411 + return x; 1.1412 + } else { 1.1413 + return -pow(-x, 1.0 / 3.0); 1.1414 + } 1.1415 +} 1.1416 +#endif 1.1417 + 1.1418 +double 1.1419 +js::math_cbrt_impl(MathCache *cache, double x) 1.1420 +{ 1.1421 + return cache->lookup(cbrt, x); 1.1422 +} 1.1423 + 1.1424 +double 1.1425 +js::math_cbrt_uncached(double x) 1.1426 +{ 1.1427 + return cbrt(x); 1.1428 +} 1.1429 + 1.1430 +bool 1.1431 +js::math_cbrt(JSContext *cx, unsigned argc, Value *vp) 1.1432 +{ 1.1433 + return math_function<math_cbrt_impl>(cx, argc, vp); 1.1434 +} 1.1435 + 1.1436 +#if JS_HAS_TOSOURCE 1.1437 +static bool 1.1438 +math_toSource(JSContext *cx, unsigned argc, Value *vp) 1.1439 +{ 1.1440 + CallArgs args = CallArgsFromVp(argc, vp); 1.1441 + args.rval().setString(cx->names().Math); 1.1442 + return true; 1.1443 +} 1.1444 +#endif 1.1445 + 1.1446 +static const JSFunctionSpec math_static_methods[] = { 1.1447 +#if JS_HAS_TOSOURCE 1.1448 + JS_FN(js_toSource_str, math_toSource, 0, 0), 1.1449 +#endif 1.1450 + JS_FN("abs", js_math_abs, 1, 0), 1.1451 + JS_FN("acos", math_acos, 1, 0), 1.1452 + JS_FN("asin", math_asin, 1, 0), 1.1453 + JS_FN("atan", math_atan, 1, 0), 1.1454 + JS_FN("atan2", math_atan2, 2, 0), 1.1455 + JS_FN("ceil", math_ceil, 1, 0), 1.1456 + JS_FN("clz32", math_clz32, 1, 0), 1.1457 + JS_FN("cos", math_cos, 1, 0), 1.1458 + JS_FN("exp", math_exp, 1, 0), 1.1459 + JS_FN("floor", math_floor, 1, 0), 1.1460 + JS_FN("imul", math_imul, 2, 0), 1.1461 + JS_FN("fround", math_fround, 1, 0), 1.1462 + JS_FN("log", math_log, 1, 0), 1.1463 + JS_FN("max", js_math_max, 2, 0), 1.1464 + JS_FN("min", js_math_min, 2, 0), 1.1465 + JS_FN("pow", js_math_pow, 2, 0), 1.1466 + JS_FN("random", js_math_random, 0, 0), 1.1467 + JS_FN("round", math_round, 1, 0), 1.1468 + JS_FN("sin", math_sin, 1, 0), 1.1469 + JS_FN("sqrt", js_math_sqrt, 1, 0), 1.1470 + JS_FN("tan", math_tan, 1, 0), 1.1471 + JS_FN("log10", math_log10, 1, 0), 1.1472 + JS_FN("log2", math_log2, 1, 0), 1.1473 + JS_FN("log1p", math_log1p, 1, 0), 1.1474 + JS_FN("expm1", math_expm1, 1, 0), 1.1475 + JS_FN("cosh", math_cosh, 1, 0), 1.1476 + JS_FN("sinh", math_sinh, 1, 0), 1.1477 + JS_FN("tanh", math_tanh, 1, 0), 1.1478 + JS_FN("acosh", math_acosh, 1, 0), 1.1479 + JS_FN("asinh", math_asinh, 1, 0), 1.1480 + JS_FN("atanh", math_atanh, 1, 0), 1.1481 + JS_FN("hypot", math_hypot, 2, 0), 1.1482 + JS_FN("trunc", math_trunc, 1, 0), 1.1483 + JS_FN("sign", math_sign, 1, 0), 1.1484 + JS_FN("cbrt", math_cbrt, 1, 0), 1.1485 + JS_FS_END 1.1486 +}; 1.1487 + 1.1488 +JSObject * 1.1489 +js_InitMathClass(JSContext *cx, HandleObject obj) 1.1490 +{ 1.1491 + RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx)); 1.1492 + if (!proto) 1.1493 + return nullptr; 1.1494 + RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, obj, SingletonObject)); 1.1495 + if (!Math) 1.1496 + return nullptr; 1.1497 + 1.1498 + if (!JS_DefineProperty(cx, obj, js_Math_str, Math, 0, 1.1499 + JS_PropertyStub, JS_StrictPropertyStub)) 1.1500 + { 1.1501 + return nullptr; 1.1502 + } 1.1503 + 1.1504 + if (!JS_DefineFunctions(cx, Math, math_static_methods)) 1.1505 + return nullptr; 1.1506 + if (!JS_DefineConstDoubles(cx, Math, math_constants)) 1.1507 + return nullptr; 1.1508 + 1.1509 + obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math)); 1.1510 + 1.1511 + return Math; 1.1512 +}