| 1 | #include "jsi.h" |
| 2 | #include "jsvalue.h" |
| 3 | #include "jsbuiltin.h" |
| 4 | |
| 5 | #if defined(_MSC_VER) && (_MSC_VER < 1700) /* VS2012 has stdint.h */ |
| 6 | typedef unsigned __int64 uint64_t; |
| 7 | #else |
| 8 | #include <stdint.h> |
| 9 | #endif |
| 10 | |
| 11 | static void jsB_new_Number(js_State *J) |
| 12 | { |
| 13 | js_newnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0); |
| 14 | } |
| 15 | |
| 16 | static void jsB_Number(js_State *J) |
| 17 | { |
| 18 | js_pushnumber(J, js_gettop(J) > 1 ? js_tonumber(J, 1) : 0); |
| 19 | } |
| 20 | |
| 21 | static void Np_valueOf(js_State *J) |
| 22 | { |
| 23 | js_Object *self = js_toobject(J, 0); |
| 24 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number" ); |
| 25 | js_pushnumber(J, self->u.number); |
| 26 | } |
| 27 | |
| 28 | static void Np_toString(js_State *J) |
| 29 | { |
| 30 | char buf[100]; |
| 31 | js_Object *self = js_toobject(J, 0); |
| 32 | int radix = js_isundefined(J, 1) ? 10 : js_tointeger(J, 1); |
| 33 | if (self->type != JS_CNUMBER) |
| 34 | js_typeerror(J, "not a number" ); |
| 35 | if (radix == 10) { |
| 36 | js_pushstring(J, jsV_numbertostring(J, buf, self->u.number)); |
| 37 | return; |
| 38 | } |
| 39 | if (radix < 2 || radix > 36) |
| 40 | js_rangeerror(J, "invalid radix" ); |
| 41 | |
| 42 | /* lame number to string conversion for any radix from 2 to 36 */ |
| 43 | { |
| 44 | static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz" ; |
| 45 | double number = self->u.number; |
| 46 | int sign = self->u.number < 0; |
| 47 | js_Buffer *sb = NULL; |
| 48 | uint64_t u, limit = ((uint64_t)1<<52); |
| 49 | |
| 50 | int ndigits, exp, point; |
| 51 | |
| 52 | if (number == 0) { js_pushstring(J, "0" ); return; } |
| 53 | if (isnan(number)) { js_pushstring(J, "NaN" ); return; } |
| 54 | if (isinf(number)) { js_pushstring(J, sign ? "-Infinity" : "Infinity" ); return; } |
| 55 | |
| 56 | if (sign) |
| 57 | number = -number; |
| 58 | |
| 59 | /* fit as many digits as we want in an int */ |
| 60 | exp = 0; |
| 61 | while (number * pow(radix, exp) > limit) |
| 62 | --exp; |
| 63 | while (number * pow(radix, exp+1) < limit) |
| 64 | ++exp; |
| 65 | u = number * pow(radix, exp) + 0.5; |
| 66 | |
| 67 | /* trim trailing zeros */ |
| 68 | while (u > 0 && (u % radix) == 0) { |
| 69 | u /= radix; |
| 70 | --exp; |
| 71 | } |
| 72 | |
| 73 | /* serialize digits */ |
| 74 | ndigits = 0; |
| 75 | while (u > 0) { |
| 76 | buf[ndigits++] = digits[u % radix]; |
| 77 | u /= radix; |
| 78 | } |
| 79 | point = ndigits - exp; |
| 80 | |
| 81 | if (js_try(J)) { |
| 82 | js_free(J, sb); |
| 83 | js_throw(J); |
| 84 | } |
| 85 | |
| 86 | if (sign) |
| 87 | js_putc(J, &sb, '-'); |
| 88 | |
| 89 | if (point <= 0) { |
| 90 | js_putc(J, &sb, '0'); |
| 91 | js_putc(J, &sb, '.'); |
| 92 | while (point++ < 0) |
| 93 | js_putc(J, &sb, '0'); |
| 94 | while (ndigits-- > 0) |
| 95 | js_putc(J, &sb, buf[ndigits]); |
| 96 | } else { |
| 97 | while (ndigits-- > 0) { |
| 98 | js_putc(J, &sb, buf[ndigits]); |
| 99 | if (--point == 0 && ndigits > 0) |
| 100 | js_putc(J, &sb, '.'); |
| 101 | } |
| 102 | while (point-- > 0) |
| 103 | js_putc(J, &sb, '0'); |
| 104 | } |
| 105 | |
| 106 | js_putc(J, &sb, 0); |
| 107 | js_pushstring(J, sb->s); |
| 108 | |
| 109 | js_endtry(J); |
| 110 | js_free(J, sb); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /* Customized ToString() on a number */ |
| 115 | static void numtostr(js_State *J, const char *fmt, int w, double n) |
| 116 | { |
| 117 | /* buf needs to fit printf("%.20f", 1e20) */ |
| 118 | char buf[50], *e; |
| 119 | sprintf(buf, fmt, w, n); |
| 120 | e = strchr(buf, 'e'); |
| 121 | if (e) { |
| 122 | int exp = atoi(e+1); |
| 123 | sprintf(e, "e%+d" , exp); |
| 124 | } |
| 125 | js_pushstring(J, buf); |
| 126 | } |
| 127 | |
| 128 | static void Np_toFixed(js_State *J) |
| 129 | { |
| 130 | js_Object *self = js_toobject(J, 0); |
| 131 | int width = js_tointeger(J, 1); |
| 132 | char buf[32]; |
| 133 | double x; |
| 134 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number" ); |
| 135 | if (width < 0) js_rangeerror(J, "precision %d out of range" , width); |
| 136 | if (width > 20) js_rangeerror(J, "precision %d out of range" , width); |
| 137 | x = self->u.number; |
| 138 | if (isnan(x) || isinf(x) || x <= -1e21 || x >= 1e21) |
| 139 | js_pushstring(J, jsV_numbertostring(J, buf, x)); |
| 140 | else |
| 141 | numtostr(J, "%.*f" , width, x); |
| 142 | } |
| 143 | |
| 144 | static void Np_toExponential(js_State *J) |
| 145 | { |
| 146 | js_Object *self = js_toobject(J, 0); |
| 147 | int width = js_tointeger(J, 1); |
| 148 | char buf[32]; |
| 149 | double x; |
| 150 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number" ); |
| 151 | if (width < 0) js_rangeerror(J, "precision %d out of range" , width); |
| 152 | if (width > 20) js_rangeerror(J, "precision %d out of range" , width); |
| 153 | x = self->u.number; |
| 154 | if (isnan(x) || isinf(x)) |
| 155 | js_pushstring(J, jsV_numbertostring(J, buf, x)); |
| 156 | else |
| 157 | numtostr(J, "%.*e" , width, self->u.number); |
| 158 | } |
| 159 | |
| 160 | static void Np_toPrecision(js_State *J) |
| 161 | { |
| 162 | js_Object *self = js_toobject(J, 0); |
| 163 | int width = js_tointeger(J, 1); |
| 164 | char buf[32]; |
| 165 | double x; |
| 166 | if (self->type != JS_CNUMBER) js_typeerror(J, "not a number" ); |
| 167 | if (width < 1) js_rangeerror(J, "precision %d out of range" , width); |
| 168 | if (width > 21) js_rangeerror(J, "precision %d out of range" , width); |
| 169 | x = self->u.number; |
| 170 | if (isnan(x) || isinf(x)) |
| 171 | js_pushstring(J, jsV_numbertostring(J, buf, x)); |
| 172 | else |
| 173 | numtostr(J, "%.*g" , width, self->u.number); |
| 174 | } |
| 175 | |
| 176 | void jsB_initnumber(js_State *J) |
| 177 | { |
| 178 | J->Number_prototype->u.number = 0; |
| 179 | |
| 180 | js_pushobject(J, J->Number_prototype); |
| 181 | { |
| 182 | jsB_propf(J, "Number.prototype.valueOf" , Np_valueOf, 0); |
| 183 | jsB_propf(J, "Number.prototype.toString" , Np_toString, 1); |
| 184 | jsB_propf(J, "Number.prototype.toLocaleString" , Np_toString, 0); |
| 185 | jsB_propf(J, "Number.prototype.toFixed" , Np_toFixed, 1); |
| 186 | jsB_propf(J, "Number.prototype.toExponential" , Np_toExponential, 1); |
| 187 | jsB_propf(J, "Number.prototype.toPrecision" , Np_toPrecision, 1); |
| 188 | } |
| 189 | js_newcconstructor(J, jsB_Number, jsB_new_Number, "Number" , 0); /* 1 */ |
| 190 | { |
| 191 | jsB_propn(J, "MAX_VALUE" , 1.7976931348623157e+308); |
| 192 | jsB_propn(J, "MIN_VALUE" , 5e-324); |
| 193 | jsB_propn(J, "NaN" , NAN); |
| 194 | jsB_propn(J, "NEGATIVE_INFINITY" , -INFINITY); |
| 195 | jsB_propn(J, "POSITIVE_INFINITY" , INFINITY); |
| 196 | } |
| 197 | js_defglobal(J, "Number" , JS_DONTENUM); |
| 198 | } |
| 199 | |