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 | |