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