home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / js / src / jsnum.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  13.3 KB  |  573 lines

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * JS number type and wrapper class.
  21.  */
  22. #include <errno.h>
  23. #ifdef XP_PC
  24. #include <float.h>
  25. #endif
  26. #include <math.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include "prtypes.h"
  30. #include "prdtoa.h"
  31. #include "prprf.h"
  32. #include "jsapi.h"
  33. #include "jsatom.h"
  34. #include "jscntxt.h"
  35. #include "jsconfig.h"
  36. #include "jsgc.h"
  37. #include "jslock.h"
  38. #include "jsnum.h"
  39. #include "jsobj.h"
  40. #include "jsopcode.h"
  41. #include "jsstr.h"
  42.  
  43. union dpun {
  44.     struct {
  45. #ifdef IS_LITTLE_ENDIAN
  46.     uint32 lo, hi;
  47. #else
  48.     uint32 hi, lo;
  49. #endif
  50.     } s;
  51.     jsdouble d;
  52. };
  53.  
  54. static JSBool
  55. num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  56. {
  57.     jsdouble x;
  58.  
  59.     if (!JS_ValueToNumber(cx, argv[0], &x))
  60.     return JS_FALSE;
  61.     *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x));
  62.     return JS_TRUE;
  63. }
  64.  
  65. static JSBool
  66. num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  67. {
  68.     jsdouble x;
  69.  
  70.     if (!JS_ValueToNumber(cx, argv[0], &x))
  71.     return JS_FALSE;
  72.     *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x));
  73.     return JS_TRUE;
  74. }
  75.  
  76. static JSBool
  77. num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  78.            jsval *rval)
  79. {
  80.     JSString *str;
  81.     jsdouble d, *dp;
  82.     jschar *ep;
  83.  
  84.     str = JS_ValueToString(cx, argv[0]);
  85.     if (!str)
  86.     return JS_FALSE;
  87.     if (!js_strtod(str->chars, &ep, &d) || ep == str->chars) {
  88.     *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
  89.     } else {
  90.     dp = js_NewDouble(cx, d);
  91.     if (!dp)
  92.         return JS_FALSE;
  93.     *rval = DOUBLE_TO_JSVAL(dp);
  94.     }
  95.     return JS_TRUE;
  96. }
  97.  
  98. static JSBool
  99. num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  100. {
  101.     JSString *str;
  102.     jsdouble d;
  103.     jsint radix;
  104.     jschar *ep;
  105.  
  106.     str = JS_ValueToString(cx, argv[0]);
  107.     if (!str)
  108.     return JS_FALSE;
  109.     if (argc > 1) {
  110.     if (!JS_ValueToNumber(cx, argv[1], &d))
  111.         return JS_FALSE;
  112.     radix = (jsint)d;
  113.     } else {
  114.     radix = 0;
  115.     }
  116.     if (!js_strtol(str->chars, &ep, radix, &d)) {
  117.     *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
  118.     } else {
  119.     if (!js_NewNumberValue(cx, d, rval))
  120.         return JS_FALSE;
  121.     }
  122.     return JS_TRUE;
  123. }
  124.  
  125. static JSFunctionSpec number_functions[] = {
  126.     {"isNaN",           num_isNaN,              1},
  127.     {"isFinite",        num_isFinite,           1},
  128.     {"parseFloat",      num_parseFloat,         1},
  129.     {"parseInt",        num_parseInt,           2},
  130.     {0}
  131. };
  132.  
  133. static JSClass number_class = {
  134.     "Number",
  135.     JSCLASS_HAS_PRIVATE,
  136.     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
  137.     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
  138. };
  139.  
  140. static JSBool
  141. Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  142. {
  143.     jsdouble d;
  144.     jsval v;
  145.  
  146.     if (argc != 0) {
  147.     if (!JS_ValueToNumber(cx, argv[0], &d))
  148.         return JS_FALSE;
  149.     } else {
  150.     d = 0.0;
  151.     }
  152.     if (!js_NewNumberValue(cx, d, &v))
  153.     return JS_FALSE;
  154.     if (obj->map->clasp != &number_class) {
  155.     *rval = v;
  156.     return JS_TRUE;
  157.     }
  158.     if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, v))
  159.     return JS_FALSE;
  160.     *rval = OBJECT_TO_JSVAL(obj);
  161.     return JS_TRUE;
  162. }
  163.  
  164. static JSBool
  165. num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  166. {
  167.     jsval v;
  168.     jsdouble d, radix;
  169.     jsint base, ival, dval;
  170.     char *bp, buf[32];
  171.     JSString *str;
  172.  
  173.     if (!JS_InstanceOf(cx, obj, &number_class, argv))
  174.     return JS_FALSE;
  175.     JS_LOCK_VOID(cx, v = js_GetSlot(cx, obj, JSSLOT_PRIVATE));
  176.     d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
  177.     if (argc != 0) {
  178.     if (!JS_ValueToNumber(cx, argv[0], &radix))
  179.         return JS_FALSE;
  180.     base = (jsint)radix;
  181.     if (base < 2 || base > 36) {
  182.         JS_ReportError(cx, "illegal radix %d", base);
  183.         return JS_FALSE;
  184.     }
  185.     ival = (jsint) d;
  186.     bp = buf + sizeof buf;
  187.     for (*--bp = '\0'; ival != 0 && --bp >= buf; ival /= base) {
  188.         dval = ival % base;
  189.         *bp = (char)((dval >= 10) ? 'a' - 10 + dval : '0' + dval);
  190.     }
  191.     if (*bp == '\0')
  192.         *--bp = '0';
  193.     str = JS_NewStringCopyZ(cx, bp);
  194.     } else {
  195.     str = js_NumberToString(cx, d);
  196.     }
  197.     if (!str)
  198.     return JS_FALSE;
  199.     *rval = STRING_TO_JSVAL(str);
  200.     return JS_TRUE;
  201. }
  202.  
  203. static JSBool
  204. num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  205. {
  206.     if (!JS_InstanceOf(cx, obj, &number_class, argv))
  207.     return JS_FALSE;
  208.     JS_LOCK_VOID(cx, *rval = js_GetSlot(cx, obj, JSSLOT_PRIVATE));
  209.     return JS_TRUE;
  210. }
  211.  
  212. static JSFunctionSpec number_methods[] = {
  213.     {js_toString_str,    num_toString,    0},
  214.     {js_valueOf_str,    num_valueOf,    0},
  215.     {0}
  216. };
  217.  
  218. /* NB: Keep this in synch with number_constants[]. */
  219. enum nc_slot {
  220.     NC_NaN,
  221.     NC_POSITIVE_INFINITY,
  222.     NC_NEGATIVE_INFINITY,
  223.     NC_MAX_VALUE,
  224.     NC_MIN_VALUE,
  225.     NC_LIMIT
  226. };
  227.  
  228. /*
  229.  * Some to most C compilers forbid spelling these at compile time, or barf
  230.  * if you try, so all but MAX_VALUE are set at runtime by js_InitNumberClass
  231.  * using union dpun.
  232.  */
  233. static JSConstDoubleSpec number_constants[] = {
  234.     {0,                         "NaN"},
  235.     {0,                         "POSITIVE_INFINITY"},
  236.     {0,                         "NEGATIVE_INFINITY"},
  237.     {1.7976931348623157E+308,   "MAX_VALUE"},
  238.     {0,                         "MIN_VALUE"},
  239.     {0}
  240. };
  241.  
  242. static jsdouble NaN;
  243.  
  244. JSObject *
  245. js_InitNumberClass(JSContext *cx, JSObject *obj)
  246. {
  247.     JSRuntime *rt;
  248.     union dpun u;
  249.     JSObject *proto, *ctor;
  250.  
  251.     rt = cx->runtime;
  252.     if (!rt->jsNaN) {
  253. #ifdef XP_PC
  254. #ifdef XP_OS2
  255.     /*DSR071597 - I have no idea what this really does other than mucking with the floating     */
  256.     /*point unit, but it does fix a "floating point underflow" exception I am getting, and there*/
  257.     /*is similar code in the Hursley java. Making sure we have the same code in Javascript      */
  258.     /*where Netscape was calling control87 on Windows...                                        */
  259.     _control87(MCW_EM+PC_53+RC_NEAR,MCW_EM+MCW_PC+MCW_RC);
  260. #else
  261.     _control87(MCW_EM, MCW_EM);
  262. #endif
  263. #endif
  264.  
  265.     u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
  266.     u.s.lo = 0xffffffff;
  267.     number_constants[NC_NaN].dval = NaN = u.d;
  268.     rt->jsNaN = js_NewDouble(cx, NaN);
  269.     if (!rt->jsNaN || !JS_LockGCThing(cx, rt->jsNaN))
  270.         return NULL;
  271.  
  272.     u.s.hi = JSDOUBLE_HI32_EXPMASK;
  273.     u.s.lo = 0x00000000;
  274.     number_constants[NC_POSITIVE_INFINITY].dval = u.d;
  275.     rt->jsPositiveInfinity = js_NewDouble(cx, u.d);
  276.     if (!rt->jsPositiveInfinity ||
  277.         !JS_LockGCThing(cx, rt->jsPositiveInfinity)) {
  278.         return NULL;
  279.     }
  280.  
  281.     u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
  282.     u.s.lo = 0x00000000;
  283.     number_constants[NC_NEGATIVE_INFINITY].dval = u.d;
  284.     rt->jsNegativeInfinity = js_NewDouble(cx, u.d);
  285.     if (!rt->jsNegativeInfinity ||
  286.         !JS_LockGCThing(cx, rt->jsNegativeInfinity)) {
  287.         return NULL;
  288.     }
  289.  
  290.     u.s.hi = 0;
  291.     u.s.lo = 1;
  292.     number_constants[NC_MIN_VALUE].dval = u.d;
  293.     }
  294.  
  295.     if (!JS_DefineFunctions(cx, obj, number_functions))
  296.     return NULL;
  297.  
  298.     proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1,
  299.              NULL, number_methods, NULL, NULL);
  300.     if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
  301.     return NULL;
  302.     if (!js_SetSlot(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO))
  303.     return NULL;
  304.     if (!JS_DefineConstDoubles(cx, ctor, number_constants))
  305.     return NULL;
  306.  
  307.     /* ECMA 15.1.1.1 */
  308.     if (!JS_DefineProperty(cx, obj, "NaN", DOUBLE_TO_JSVAL(rt->jsNaN),
  309.                NULL, NULL, 0)) {
  310.     return NULL;
  311.     }
  312.  
  313.     /* ECMA 15.1.1.2 */
  314.     if (!JS_DefineProperty(cx, obj, "Infinity",
  315.                DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
  316.                NULL, NULL, 0)) {
  317.     return NULL;
  318.     }
  319.     return proto;
  320. }
  321.  
  322. jsdouble *
  323. js_NewDouble(JSContext *cx, jsdouble d)
  324. {
  325.     jsdouble *dp;
  326.  
  327.     dp = js_AllocGCThing(cx, GCX_DOUBLE);
  328.     if (!dp)
  329.     return NULL;
  330.     *dp = d;
  331.     return dp;
  332. }
  333.  
  334. void
  335. js_FinalizeDouble(JSContext *cx, jsdouble *dp)
  336. {
  337.     *dp = NaN;
  338. }
  339.  
  340. JSBool
  341. js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
  342. {
  343.     jsdouble *dp;
  344.  
  345.     dp = js_NewDouble(cx, d);
  346.     if (!dp)
  347.     return JS_FALSE;
  348.     *rval = DOUBLE_TO_JSVAL(dp);
  349.     return JS_TRUE;
  350. }
  351.  
  352. JSBool
  353. js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
  354. {
  355.     jsint i;
  356.  
  357.     i = (jsint)d;
  358.     if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
  359.     *rval = INT_TO_JSVAL(i);
  360.     } else {
  361.     if (!js_NewDoubleValue(cx, d, rval))
  362.         return JS_FALSE;
  363.     }
  364.     return JS_TRUE;
  365. }
  366.  
  367. JSObject *
  368. js_NumberToObject(JSContext *cx, jsdouble d)
  369. {
  370.     JSObject *obj;
  371.     jsval v;
  372.  
  373.     obj = js_NewObject(cx, &number_class, NULL, NULL);
  374.     if (!obj)
  375.     return NULL;
  376.     if (!js_NewNumberValue(cx, d, &v)) {
  377.     cx->newborn[GCX_OBJECT] = NULL;
  378.     return NULL;
  379.     }
  380.     if (!js_SetSlot(cx, obj, JSSLOT_PRIVATE, v)) {
  381.     cx->newborn[GCX_OBJECT] = NULL;
  382.     return NULL;
  383.     }
  384.     return obj;
  385. }
  386.  
  387. /* XXXbe rewrite me to be ECMA-based! */
  388. JSString *
  389. js_NumberToString(JSContext *cx, jsdouble d)
  390. {
  391.     jsint i;
  392.     char buf[32];
  393.  
  394.     i = (jsint)d;
  395.     if (JSDOUBLE_IS_INT(d, i)) {
  396.     PR_snprintf(buf, sizeof buf, "%ld", (long)i);
  397.     } else {
  398.     /* XXX lock here because prdtoa.c is not threadsafe yet */
  399.     JS_LOCK_VOID(cx, PR_cnvtf(buf, sizeof buf, 20, d));
  400.     }
  401.     return JS_NewStringCopyZ(cx, buf);
  402. }
  403.  
  404. JSBool
  405. js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
  406. {
  407.     JSObject *obj;
  408.     JSString *str;
  409.     jschar *ep;
  410.     jsdouble d;
  411.  
  412.     if (JSVAL_IS_OBJECT(v)) {
  413.     obj = JSVAL_TO_OBJECT(v);
  414.     if (!obj) {
  415.         *dp = 0;
  416.         return JS_TRUE;
  417.     }
  418.     if (!obj->map->clasp->convert(cx, obj, JSTYPE_NUMBER, &v))
  419.         return JS_FALSE;
  420.     }
  421.     if (JSVAL_IS_INT(v)) {
  422.     *dp = (jsdouble)JSVAL_TO_INT(v);
  423.     } else if (JSVAL_IS_DOUBLE(v)) {
  424.     *dp = *JSVAL_TO_DOUBLE(v);
  425.     } else if (JSVAL_IS_STRING(v)) {
  426.     str = JSVAL_TO_STRING(v);
  427.     errno = 0;
  428.     if (!js_strtod(str->chars, &ep, &d) || *ep != 0)
  429.         goto badstr;
  430.     *dp = d;
  431.     } else if (JSVAL_IS_BOOLEAN(v)) {
  432.     *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;
  433.     } else {
  434. #if JS_BUG_FALLIBLE_TONUM
  435.     str = js_ValueToSource(cx, v);
  436. badstr:
  437.     if (str) {
  438.         JS_ReportError(cx, "%s is not a number",
  439.                JS_GetStringBytes(str));
  440.     }
  441.     return JS_FALSE;
  442. #else
  443. badstr:
  444.     *dp = *cx->runtime->jsNaN;
  445. #endif
  446.     }
  447.     return JS_TRUE;
  448. }
  449.  
  450. JSBool
  451. js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
  452. {
  453.     jsdouble d;
  454.     JSString *str;
  455.  
  456.     if (!js_ValueToNumber(cx, v, &d))
  457.     return JS_FALSE;
  458.     if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
  459.     str = js_ValueToSource(cx, v);
  460.     if (str) {
  461.         JS_ReportError(cx, "can't convert %s to an integer",
  462.                JS_GetStringBytes(str));
  463.     }
  464.     return JS_FALSE;
  465.     }
  466.     *ip = (int32)floor(d + 0.5);     /* Round to nearest */
  467.     return JS_TRUE;
  468. }
  469.  
  470. JSBool
  471. js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
  472. {
  473.     jsdouble d;
  474.     jsuint i, m;
  475.     JSBool neg;
  476.  
  477.     if (!js_ValueToNumber(cx, v, &d))
  478.     return JS_FALSE;
  479.     if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
  480.     *ip = 0;
  481.     return JS_TRUE;
  482.     }
  483.     i = (jsuint)d;
  484.     if ((jsdouble)i == d) {
  485.     *ip = (uint16)i;
  486.     return JS_TRUE;
  487.     }
  488.     neg = (d < 0);
  489.     d = floor(neg ? -d : d);
  490.     d = neg ? -d : d;
  491.     m = PR_BIT(16);
  492.     d = fmod(d, m);
  493.     if (d < 0)
  494.     d += m;
  495.     *ip = (uint16) d;
  496.     return JS_TRUE;
  497. }
  498.  
  499. jsdouble
  500. js_DoubleToInteger(jsdouble d)
  501. {
  502.     JSBool neg;
  503.  
  504.     if (d == 0)
  505.     return d;
  506.     if (!JSDOUBLE_IS_FINITE(d)) {
  507.     if (JSDOUBLE_IS_NaN(d))
  508.         return 0;
  509.     return d;
  510.     }
  511.     neg = (d < 0);
  512.     d = floor(neg ? -d : d);
  513.     return neg ? -d : d;
  514. }
  515.  
  516. /* XXXbe rewrite me! */
  517. JSBool
  518. js_strtod(const jschar *s, jschar **ep, jsdouble *dp)
  519. {
  520.     size_t i, n;
  521.     char *cstr, *estr;
  522.     jsdouble d;
  523.  
  524.     n = js_strlen(s);
  525.     cstr = malloc(n + 1);
  526.     if (!cstr)
  527.     return JS_FALSE;
  528.     for (i = 0; i <= n; i++) {
  529.     if (s[i] >> 8) {
  530.         free(cstr);
  531.         return JS_FALSE;
  532.     }
  533.     cstr[i] = (char)s[i];
  534.     }
  535.     errno = 0;
  536.     d = PR_strtod(cstr, &estr);
  537.     free(cstr);
  538.     if (errno == ERANGE)
  539.     return JS_FALSE;
  540.     *ep = (jschar *)s + (estr - cstr);
  541.     *dp = d;
  542.     return JS_TRUE;
  543. }
  544.  
  545. /* XXXbe rewrite me! */
  546. JSBool
  547. js_strtol(const jschar *s, jschar **ep, jsint radix, jsdouble *dp)
  548. {
  549.     size_t i, n;
  550.     char *cstr, *estr;
  551.     long l;
  552.  
  553.     n = js_strlen(s);
  554.     cstr = malloc(n + 1);
  555.     if (!cstr)
  556.     return JS_FALSE;
  557.     for (i = 0; i <= n; i++) {
  558.     if (s[i] >> 8) {
  559.         free(cstr);
  560.         return JS_FALSE;
  561.     }
  562.     cstr[i] = (char)s[i];
  563.     }
  564.     errno = 0;
  565.     l = strtol(cstr, &estr, radix);
  566.     free(cstr);
  567.     if (errno == ERANGE)
  568.     return JS_FALSE;
  569.     *ep = (jschar *)s + (estr - cstr);
  570.     *dp = (jsdouble)l;
  571.     return JS_TRUE;
  572. }
  573.