1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 (function(global, utils) { 6 "use strict"; 7 8 %CheckIsBootstrapping(); 9 10 // ------------------------------------------------------------------- 11 // Imports 12 13 define kRandomBatchSize = 64; 14 // The first two slots are reserved to persist PRNG state. 15 define kRandomNumberStart = 2; 16 17 var GlobalFloat64Array = global.Float64Array; 18 var GlobalMath = global.Math; 19 var GlobalObject = global.Object; 20 var InternalArray = utils.InternalArray; 21 var NaN = %GetRootNaN(); 22 var nextRandomIndex = kRandomBatchSize; 23 var randomNumbers = UNDEFINED; 24 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); 25 26 //------------------------------------------------------------------- 27 28 // ECMA 262 - 15.8.2.1 29 function MathAbs(x) { 30 x = +x; 31 return (x > 0) ? x : 0 - x; 32 } 33 34 // ECMA 262 - 15.8.2.2 35 function MathAcosJS(x) { 36 return %_MathAcos(+x); 37 } 38 39 // ECMA 262 - 15.8.2.3 40 function MathAsinJS(x) { 41 return %_MathAsin(+x); 42 } 43 44 // ECMA 262 - 15.8.2.4 45 function MathAtanJS(x) { 46 return %_MathAtan(+x); 47 } 48 49 // ECMA 262 - 15.8.2.5 50 // The naming of y and x matches the spec, as does the order in which 51 // ToNumber (valueOf) is called. 52 function MathAtan2JS(y, x) { 53 y = +y; 54 x = +x; 55 return %_MathAtan2(y, x); 56 } 57 58 // ECMA 262 - 15.8.2.6 59 function MathCeil(x) { 60 return -%_MathFloor(-x); 61 } 62 63 // ECMA 262 - 15.8.2.8 64 function MathExp(x) { 65 return %MathExpRT(TO_NUMBER(x)); 66 } 67 68 // ECMA 262 - 15.8.2.9 69 function MathFloorJS(x) { 70 return %_MathFloor(+x); 71 } 72 73 // ECMA 262 - 15.8.2.10 74 function MathLog(x) { 75 return %_MathLogRT(TO_NUMBER(x)); 76 } 77 78 // ECMA 262 - 15.8.2.11 79 function MathMax(arg1, arg2) { // length == 2 80 var length = %_ArgumentsLength(); 81 if (length == 2) { 82 arg1 = TO_NUMBER(arg1); 83 arg2 = TO_NUMBER(arg2); 84 if (arg2 > arg1) return arg2; 85 if (arg1 > arg2) return arg1; 86 if (arg1 == arg2) { 87 // Make sure -0 is considered less than +0. 88 return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1; 89 } 90 // All comparisons failed, one of the arguments must be NaN. 91 return NaN; 92 } 93 var r = -INFINITY; 94 for (var i = 0; i < length; i++) { 95 var n = %_Arguments(i); 96 n = TO_NUMBER(n); 97 // Make sure +0 is considered greater than -0. 98 if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) { 99 r = n; 100 } 101 } 102 return r; 103 } 104 105 // ECMA 262 - 15.8.2.12 106 function MathMin(arg1, arg2) { // length == 2 107 var length = %_ArgumentsLength(); 108 if (length == 2) { 109 arg1 = TO_NUMBER(arg1); 110 arg2 = TO_NUMBER(arg2); 111 if (arg2 > arg1) return arg1; 112 if (arg1 > arg2) return arg2; 113 if (arg1 == arg2) { 114 // Make sure -0 is considered less than +0. 115 return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2; 116 } 117 // All comparisons failed, one of the arguments must be NaN. 118 return NaN; 119 } 120 var r = INFINITY; 121 for (var i = 0; i < length; i++) { 122 var n = %_Arguments(i); 123 n = TO_NUMBER(n); 124 // Make sure -0 is considered less than +0. 125 if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) { 126 r = n; 127 } 128 } 129 return r; 130 } 131 132 // ECMA 262 - 15.8.2.13 133 function MathPowJS(x, y) { 134 return %_MathPow(TO_NUMBER(x), TO_NUMBER(y)); 135 } 136 137 // ECMA 262 - 15.8.2.14 138 function MathRandom() { 139 if (nextRandomIndex >= kRandomBatchSize) { 140 randomNumbers = %GenerateRandomNumbers(randomNumbers); 141 nextRandomIndex = kRandomNumberStart; 142 } 143 return randomNumbers[nextRandomIndex++]; 144 } 145 146 function MathRandomRaw() { 147 if (nextRandomIndex >= kRandomBatchSize) { 148 randomNumbers = %GenerateRandomNumbers(randomNumbers); 149 nextRandomIndex = kRandomNumberStart; 150 } 151 return %_DoubleLo(randomNumbers[nextRandomIndex++]) & 0x3FFFFFFF; 152 } 153 154 // ECMA 262 - 15.8.2.15 155 function MathRound(x) { 156 return %RoundNumber(TO_NUMBER(x)); 157 } 158 159 // ECMA 262 - 15.8.2.17 160 function MathSqrtJS(x) { 161 return %_MathSqrt(+x); 162 } 163 164 // Non-standard extension. 165 function MathImul(x, y) { 166 return %NumberImul(TO_NUMBER(x), TO_NUMBER(y)); 167 } 168 169 // ES6 draft 09-27-13, section 20.2.2.28. 170 function MathSign(x) { 171 x = +x; 172 if (x > 0) return 1; 173 if (x < 0) return -1; 174 // -0, 0 or NaN. 175 return x; 176 } 177 178 // ES6 draft 09-27-13, section 20.2.2.34. 179 function MathTrunc(x) { 180 x = +x; 181 if (x > 0) return %_MathFloor(x); 182 if (x < 0) return -%_MathFloor(-x); 183 // -0, 0 or NaN. 184 return x; 185 } 186 187 // ES6 draft 09-27-13, section 20.2.2.5. 188 function MathAsinh(x) { 189 x = TO_NUMBER(x); 190 // Idempotent for NaN, +/-0 and +/-Infinity. 191 if (x === 0 || !NUMBER_IS_FINITE(x)) return x; 192 if (x > 0) return MathLog(x + %_MathSqrt(x * x + 1)); 193 // This is to prevent numerical errors caused by large negative x. 194 return -MathLog(-x + %_MathSqrt(x * x + 1)); 195 } 196 197 // ES6 draft 09-27-13, section 20.2.2.3. 198 function MathAcosh(x) { 199 x = TO_NUMBER(x); 200 if (x < 1) return NaN; 201 // Idempotent for NaN and +Infinity. 202 if (!NUMBER_IS_FINITE(x)) return x; 203 return MathLog(x + %_MathSqrt(x + 1) * %_MathSqrt(x - 1)); 204 } 205 206 // ES6 draft 09-27-13, section 20.2.2.7. 207 function MathAtanh(x) { 208 x = TO_NUMBER(x); 209 // Idempotent for +/-0. 210 if (x === 0) return x; 211 // Returns NaN for NaN and +/- Infinity. 212 if (!NUMBER_IS_FINITE(x)) return NaN; 213 return 0.5 * MathLog((1 + x) / (1 - x)); 214 } 215 216 // ES6 draft 09-27-13, section 20.2.2.17. 217 function MathHypot(x, y) { // Function length is 2. 218 // We may want to introduce fast paths for two arguments and when 219 // normalization to avoid overflow is not necessary. For now, we 220 // simply assume the general case. 221 var length = %_ArgumentsLength(); 222 var args = new InternalArray(length); 223 var max = 0; 224 for (var i = 0; i < length; i++) { 225 var n = %_Arguments(i); 226 n = TO_NUMBER(n); 227 if (n === INFINITY || n === -INFINITY) return INFINITY; 228 n = MathAbs(n); 229 if (n > max) max = n; 230 args[i] = n; 231 } 232 233 // Kahan summation to avoid rounding errors. 234 // Normalize the numbers to the largest one to avoid overflow. 235 if (max === 0) max = 1; 236 var sum = 0; 237 var compensation = 0; 238 for (var i = 0; i < length; i++) { 239 var n = args[i] / max; 240 var summand = n * n - compensation; 241 var preliminary = sum + summand; 242 compensation = (preliminary - sum) - summand; 243 sum = preliminary; 244 } 245 return %_MathSqrt(sum) * max; 246 } 247 248 // ES6 draft 09-27-13, section 20.2.2.16. 249 function MathFroundJS(x) { 250 return %MathFround(TO_NUMBER(x)); 251 } 252 253 // ES6 draft 07-18-14, section 20.2.2.11 254 function MathClz32JS(x) { 255 return %_MathClz32(x >>> 0); 256 } 257 258 // ES6 draft 09-27-13, section 20.2.2.9. 259 // Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm 260 // Using initial approximation adapted from Kahan's cbrt and 4 iterations 261 // of Newton's method. 262 function MathCbrt(x) { 263 x = TO_NUMBER(x); 264 if (x == 0 || !NUMBER_IS_FINITE(x)) return x; 265 return x >= 0 ? CubeRoot(x) : -CubeRoot(-x); 266 } 267 268 macro NEWTON_ITERATION_CBRT(x, approx) 269 (1.0 / 3.0) * (x / (approx * approx) + 2 * approx); 270 endmacro 271 272 function CubeRoot(x) { 273 var approx_hi = MathFloorJS(%_DoubleHi(x) / 3) + 0x2A9F7893; 274 var approx = %_ConstructDouble(approx_hi | 0, 0); 275 approx = NEWTON_ITERATION_CBRT(x, approx); 276 approx = NEWTON_ITERATION_CBRT(x, approx); 277 approx = NEWTON_ITERATION_CBRT(x, approx); 278 return NEWTON_ITERATION_CBRT(x, approx); 279 } 280 281 // ------------------------------------------------------------------- 282 283 %AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM); 284 285 // Set up math constants. 286 utils.InstallConstants(GlobalMath, [ 287 // ECMA-262, section 15.8.1.1. 288 "E", 2.7182818284590452354, 289 // ECMA-262, section 15.8.1.2. 290 "LN10", 2.302585092994046, 291 // ECMA-262, section 15.8.1.3. 292 "LN2", 0.6931471805599453, 293 // ECMA-262, section 15.8.1.4. 294 "LOG2E", 1.4426950408889634, 295 "LOG10E", 0.4342944819032518, 296 "PI", 3.1415926535897932, 297 "SQRT1_2", 0.7071067811865476, 298 "SQRT2", 1.4142135623730951 299 ]); 300 301 // Set up non-enumerable functions of the Math object and 302 // set their names. 303 utils.InstallFunctions(GlobalMath, DONT_ENUM, [ 304 "random", MathRandom, 305 "abs", MathAbs, 306 "acos", MathAcosJS, 307 "asin", MathAsinJS, 308 "atan", MathAtanJS, 309 "ceil", MathCeil, 310 "exp", MathExp, 311 "floor", MathFloorJS, 312 "log", MathLog, 313 "round", MathRound, 314 "sqrt", MathSqrtJS, 315 "atan2", MathAtan2JS, 316 "pow", MathPowJS, 317 "max", MathMax, 318 "min", MathMin, 319 "imul", MathImul, 320 "sign", MathSign, 321 "trunc", MathTrunc, 322 "asinh", MathAsinh, 323 "acosh", MathAcosh, 324 "atanh", MathAtanh, 325 "hypot", MathHypot, 326 "fround", MathFroundJS, 327 "clz32", MathClz32JS, 328 "cbrt", MathCbrt 329 ]); 330 331 %SetForceInlineFlag(MathAbs); 332 %SetForceInlineFlag(MathAcosJS); 333 %SetForceInlineFlag(MathAsinJS); 334 %SetForceInlineFlag(MathAtanJS); 335 %SetForceInlineFlag(MathAtan2JS); 336 %SetForceInlineFlag(MathCeil); 337 %SetForceInlineFlag(MathClz32JS); 338 %SetForceInlineFlag(MathFloorJS); 339 %SetForceInlineFlag(MathRandom); 340 %SetForceInlineFlag(MathSign); 341 %SetForceInlineFlag(MathSqrtJS); 342 %SetForceInlineFlag(MathTrunc); 343 344 // ------------------------------------------------------------------- 345 // Exports 346 347 utils.Export(function(to) { 348 to.MathAbs = MathAbs; 349 to.MathExp = MathExp; 350 to.MathFloor = MathFloorJS; 351 to.IntRandom = MathRandomRaw; 352 to.MathMax = MathMax; 353 to.MathMin = MathMin; 354 }); 355 356 }) 357