Back to index

lightning-sunbird  0.9+nobinonly
jsmath.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /*
00041  * JS math package.
00042  */
00043 #include "jsstddef.h"
00044 #include "jslibmath.h"
00045 #include <stdlib.h>
00046 #include "jstypes.h"
00047 #include "jslong.h"
00048 #include "prmjtime.h"
00049 #include "jsapi.h"
00050 #include "jsatom.h"
00051 #include "jscntxt.h"
00052 #include "jsconfig.h"
00053 #include "jslock.h"
00054 #include "jsmath.h"
00055 #include "jsnum.h"
00056 #include "jsobj.h"
00057 
00058 #ifndef M_E
00059 #define M_E             2.7182818284590452354
00060 #endif
00061 #ifndef M_LOG2E
00062 #define M_LOG2E         1.4426950408889634074
00063 #endif
00064 #ifndef M_LOG10E
00065 #define M_LOG10E        0.43429448190325182765
00066 #endif
00067 #ifndef M_LN2
00068 #define M_LN2           0.69314718055994530942
00069 #endif
00070 #ifndef M_LN10
00071 #define M_LN10          2.30258509299404568402
00072 #endif
00073 #ifndef M_PI
00074 #define M_PI            3.14159265358979323846
00075 #endif
00076 #ifndef M_SQRT2
00077 #define M_SQRT2         1.41421356237309504880
00078 #endif
00079 #ifndef M_SQRT1_2
00080 #define M_SQRT1_2       0.70710678118654752440
00081 #endif
00082 
00083 static JSConstDoubleSpec math_constants[] = {
00084     {M_E,       "E",            0, {0,0,0}},
00085     {M_LOG2E,   "LOG2E",        0, {0,0,0}},
00086     {M_LOG10E,  "LOG10E",       0, {0,0,0}},
00087     {M_LN2,     "LN2",          0, {0,0,0}},
00088     {M_LN10,    "LN10",         0, {0,0,0}},
00089     {M_PI,      "PI",           0, {0,0,0}},
00090     {M_SQRT2,   "SQRT2",        0, {0,0,0}},
00091     {M_SQRT1_2, "SQRT1_2",      0, {0,0,0}},
00092     {0,0,0,{0,0,0}}
00093 };
00094 
00095 JSClass js_MathClass = {
00096     js_Math_str,
00097     JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
00098     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00099     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
00100     JSCLASS_NO_OPTIONAL_MEMBERS
00101 };
00102 
00103 static JSBool
00104 math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00105 {
00106     jsdouble x, z;
00107 
00108     if (!js_ValueToNumber(cx, argv[0], &x))
00109         return JS_FALSE;
00110     z = fd_fabs(x);
00111     return js_NewNumberValue(cx, z, rval);
00112 }
00113 
00114 static JSBool
00115 math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00116 {
00117     jsdouble x, z;
00118 
00119     if (!js_ValueToNumber(cx, argv[0], &x))
00120         return JS_FALSE;
00121     z = fd_acos(x);
00122     return js_NewNumberValue(cx, z, rval);
00123 }
00124 
00125 static JSBool
00126 math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00127 {
00128     jsdouble x, z;
00129 
00130     if (!js_ValueToNumber(cx, argv[0], &x))
00131         return JS_FALSE;
00132     z = fd_asin(x);
00133     return js_NewNumberValue(cx, z, rval);
00134 }
00135 
00136 static JSBool
00137 math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00138 {
00139     jsdouble x, z;
00140 
00141     if (!js_ValueToNumber(cx, argv[0], &x))
00142         return JS_FALSE;
00143     z = fd_atan(x);
00144     return js_NewNumberValue(cx, z, rval);
00145 }
00146 
00147 static JSBool
00148 math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00149 {
00150     jsdouble x, y, z;
00151 
00152     if (!js_ValueToNumber(cx, argv[0], &x))
00153         return JS_FALSE;
00154     if (!js_ValueToNumber(cx, argv[1], &y))
00155         return JS_FALSE;
00156 #if !JS_USE_FDLIBM_MATH && defined(_MSC_VER)
00157     /*
00158      * MSVC's atan2 does not yield the result demanded by ECMA when both x
00159      * and y are infinite.
00160      * - The result is a multiple of pi/4.
00161      * - The sign of x determines the sign of the result.
00162      * - The sign of y determines the multiplicator, 1 or 3.
00163      */
00164     if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) {
00165         z = fd_copysign(M_PI / 4, x);
00166         if (y < 0)
00167             z *= 3;
00168         return js_NewDoubleValue(cx, z, rval);
00169     }
00170 #endif
00171     z = fd_atan2(x, y);
00172     return js_NewNumberValue(cx, z, rval);
00173 }
00174 
00175 static JSBool
00176 math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00177 {
00178     jsdouble x, z;
00179 
00180     if (!js_ValueToNumber(cx, argv[0], &x))
00181         return JS_FALSE;
00182     z = fd_ceil(x);
00183     return js_NewNumberValue(cx, z, rval);
00184 }
00185 
00186 static JSBool
00187 math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00188 {
00189     jsdouble x, z;
00190 
00191     if (!js_ValueToNumber(cx, argv[0], &x))
00192         return JS_FALSE;
00193     z = fd_cos(x);
00194     return js_NewNumberValue(cx, z, rval);
00195 }
00196 
00197 static JSBool
00198 math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00199 {
00200     jsdouble x, z;
00201 
00202     if (!js_ValueToNumber(cx, argv[0], &x))
00203         return JS_FALSE;
00204 #ifdef _WIN32
00205     if (!JSDOUBLE_IS_NaN(x)) {
00206         if (x == *cx->runtime->jsPositiveInfinity) {
00207             *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
00208             return JS_TRUE;
00209         }
00210         if (x == *cx->runtime->jsNegativeInfinity) {
00211             *rval = JSVAL_ZERO;
00212             return JS_TRUE;
00213         }
00214     }
00215 #endif
00216     z = fd_exp(x);
00217     return js_NewNumberValue(cx, z, rval);
00218 }
00219 
00220 static JSBool
00221 math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00222 {
00223     jsdouble x, z;
00224 
00225     if (!js_ValueToNumber(cx, argv[0], &x))
00226         return JS_FALSE;
00227     z = fd_floor(x);
00228     return js_NewNumberValue(cx, z, rval);
00229 }
00230 
00231 static JSBool
00232 math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00233 {
00234     jsdouble x, z;
00235 
00236     if (!js_ValueToNumber(cx, argv[0], &x))
00237         return JS_FALSE;
00238     z = fd_log(x);
00239     return js_NewNumberValue(cx, z, rval);
00240 }
00241 
00242 static JSBool
00243 math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00244 {
00245     jsdouble x, z = *cx->runtime->jsNegativeInfinity;
00246     uintN i;
00247 
00248     if (argc == 0) {
00249         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
00250         return JS_TRUE;
00251     }
00252     for (i = 0; i < argc; i++) {
00253         if (!js_ValueToNumber(cx, argv[i], &x))
00254             return JS_FALSE;
00255         if (JSDOUBLE_IS_NaN(x)) {
00256             *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00257             return JS_TRUE;
00258         }
00259         if (x == 0 && x == z && fd_copysign(1.0, z) == -1)
00260             z = x;
00261         else
00262             z = (x > z) ? x : z;
00263     }
00264     return js_NewNumberValue(cx, z, rval);
00265 }
00266 
00267 static JSBool
00268 math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00269 {
00270     jsdouble x, z = *cx->runtime->jsPositiveInfinity;
00271     uintN i;
00272 
00273     if (argc == 0) {
00274         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
00275         return JS_TRUE;
00276     }
00277     for (i = 0; i < argc; i++) {
00278         if (!js_ValueToNumber(cx, argv[i], &x))
00279             return JS_FALSE;
00280         if (JSDOUBLE_IS_NaN(x)) {
00281             *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00282             return JS_TRUE;
00283         }
00284         if (x == 0 && x == z && fd_copysign(1.0,x) == -1)
00285             z = x;
00286         else
00287             z = (x < z) ? x : z;
00288     }
00289     return js_NewNumberValue(cx, z, rval);
00290 }
00291 
00292 static JSBool
00293 math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00294 {
00295     jsdouble x, y, z;
00296 
00297     if (!js_ValueToNumber(cx, argv[0], &x))
00298         return JS_FALSE;
00299     if (!js_ValueToNumber(cx, argv[1], &y))
00300         return JS_FALSE;
00301 #if !JS_USE_FDLIBM_MATH
00302     /*
00303      * Because C99 and ECMA specify different behavior for pow(),
00304      * we need to wrap the libm call to make it ECMA compliant.
00305      */
00306     if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) {
00307         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00308         return JS_TRUE;
00309     }
00310     /* pow(x, +-0) is always 1, even for x = NaN. */
00311     if (y == 0) {
00312         *rval = JSVAL_ONE;
00313         return JS_TRUE;
00314     }
00315 #endif
00316     z = fd_pow(x, y);
00317     return js_NewNumberValue(cx, z, rval);
00318 }
00319 
00320 /*
00321  * Math.random() support, lifted from java.util.Random.java.
00322  */
00323 static void
00324 random_setSeed(JSRuntime *rt, int64 seed)
00325 {
00326     int64 tmp;
00327 
00328     JSLL_I2L(tmp, 1000);
00329     JSLL_DIV(seed, seed, tmp);
00330     JSLL_XOR(tmp, seed, rt->rngMultiplier);
00331     JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
00332 }
00333 
00334 static void
00335 random_init(JSRuntime *rt)
00336 {
00337     int64 tmp, tmp2;
00338 
00339     /* Do at most once. */
00340     if (rt->rngInitialized)
00341         return;
00342     rt->rngInitialized = JS_TRUE;
00343 
00344     /* rt->rngMultiplier = 0x5DEECE66DL */
00345     JSLL_ISHL(tmp, 0x5, 32);
00346     JSLL_UI2L(tmp2, 0xDEECE66DL);
00347     JSLL_OR(rt->rngMultiplier, tmp, tmp2);
00348 
00349     /* rt->rngAddend = 0xBL */
00350     JSLL_I2L(rt->rngAddend, 0xBL);
00351 
00352     /* rt->rngMask = (1L << 48) - 1 */
00353     JSLL_I2L(tmp, 1);
00354     JSLL_SHL(tmp2, tmp, 48);
00355     JSLL_SUB(rt->rngMask, tmp2, tmp);
00356 
00357     /* rt->rngDscale = (jsdouble)(1L << 53) */
00358     JSLL_SHL(tmp2, tmp, 53);
00359     JSLL_L2D(rt->rngDscale, tmp2);
00360 
00361     /* Finally, set the seed from current time. */
00362     random_setSeed(rt, PRMJ_Now());
00363 }
00364 
00365 static uint32
00366 random_next(JSRuntime *rt, int bits)
00367 {
00368     int64 nextseed, tmp;
00369     uint32 retval;
00370 
00371     JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
00372     JSLL_ADD(nextseed, nextseed, rt->rngAddend);
00373     JSLL_AND(nextseed, nextseed, rt->rngMask);
00374     rt->rngSeed = nextseed;
00375     JSLL_USHR(tmp, nextseed, 48 - bits);
00376     JSLL_L2I(retval, tmp);
00377     return retval;
00378 }
00379 
00380 static jsdouble
00381 random_nextDouble(JSRuntime *rt)
00382 {
00383     int64 tmp, tmp2;
00384     jsdouble d;
00385 
00386     JSLL_ISHL(tmp, random_next(rt, 26), 27);
00387     JSLL_UI2L(tmp2, random_next(rt, 27));
00388     JSLL_ADD(tmp, tmp, tmp2);
00389     JSLL_L2D(d, tmp);
00390     return d / rt->rngDscale;
00391 }
00392 
00393 static JSBool
00394 math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00395 {
00396     JSRuntime *rt;
00397     jsdouble z;
00398 
00399     rt = cx->runtime;
00400     JS_LOCK_RUNTIME(rt);
00401     random_init(rt);
00402     z = random_nextDouble(rt);
00403     JS_UNLOCK_RUNTIME(rt);
00404     return js_NewNumberValue(cx, z, rval);
00405 }
00406 
00407 #if defined _WIN32 && !defined WINCE && _MSC_VER < 1400
00408 /* Try to work around apparent _copysign bustage in VC6 and VC7. */
00409 double
00410 js_copysign(double x, double y)
00411 {
00412     jsdpun xu, yu;
00413 
00414     xu.d = x;
00415     yu.d = y;
00416     xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT;
00417     xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT;
00418     return xu.d;
00419 }
00420 #endif
00421 
00422 static JSBool
00423 math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00424 {
00425     jsdouble x, z;
00426 
00427     if (!js_ValueToNumber(cx, argv[0], &x))
00428         return JS_FALSE;
00429     z = fd_copysign(fd_floor(x + 0.5), x);
00430     return js_NewNumberValue(cx, z, rval);
00431 }
00432 
00433 static JSBool
00434 math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00435 {
00436     jsdouble x, z;
00437 
00438     if (!js_ValueToNumber(cx, argv[0], &x))
00439         return JS_FALSE;
00440     z = fd_sin(x);
00441     return js_NewNumberValue(cx, z, rval);
00442 }
00443 
00444 static JSBool
00445 math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00446 {
00447     jsdouble x, z;
00448 
00449     if (!js_ValueToNumber(cx, argv[0], &x))
00450         return JS_FALSE;
00451     z = fd_sqrt(x);
00452     return js_NewNumberValue(cx, z, rval);
00453 }
00454 
00455 static JSBool
00456 math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00457 {
00458     jsdouble x, z;
00459 
00460     if (!js_ValueToNumber(cx, argv[0], &x))
00461         return JS_FALSE;
00462     z = fd_tan(x);
00463     return js_NewNumberValue(cx, z, rval);
00464 }
00465 
00466 #if JS_HAS_TOSOURCE
00467 static JSBool
00468 math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00469               jsval *rval)
00470 {
00471     *rval = ATOM_KEY(CLASS_ATOM(cx, Math));
00472     return JS_TRUE;
00473 }
00474 #endif
00475 
00476 static JSFunctionSpec math_static_methods[] = {
00477 #if JS_HAS_TOSOURCE
00478     {js_toSource_str,   math_toSource,          0, 0, 0},
00479 #endif
00480     {"abs",             math_abs,               1, 0, 0},
00481     {"acos",            math_acos,              1, 0, 0},
00482     {"asin",            math_asin,              1, 0, 0},
00483     {"atan",            math_atan,              1, 0, 0},
00484     {"atan2",           math_atan2,             2, 0, 0},
00485     {"ceil",            math_ceil,              1, 0, 0},
00486     {"cos",             math_cos,               1, 0, 0},
00487     {"exp",             math_exp,               1, 0, 0},
00488     {"floor",           math_floor,             1, 0, 0},
00489     {"log",             math_log,               1, 0, 0},
00490     {"max",             math_max,               2, 0, 0},
00491     {"min",             math_min,               2, 0, 0},
00492     {"pow",             math_pow,               2, 0, 0},
00493     {"random",          math_random,            0, 0, 0},
00494     {"round",           math_round,             1, 0, 0},
00495     {"sin",             math_sin,               1, 0, 0},
00496     {"sqrt",            math_sqrt,              1, 0, 0},
00497     {"tan",             math_tan,               1, 0, 0},
00498     {0,0,0,0,0}
00499 };
00500 
00501 JSObject *
00502 js_InitMathClass(JSContext *cx, JSObject *obj)
00503 {
00504     JSObject *Math;
00505 
00506     Math = JS_DefineObject(cx, obj, js_Math_str, &js_MathClass, NULL, 0);
00507     if (!Math)
00508         return NULL;
00509     if (!JS_DefineFunctions(cx, Math, math_static_methods))
00510         return NULL;
00511     if (!JS_DefineConstDoubles(cx, Math, math_constants))
00512         return NULL;
00513     return Math;
00514 }