1 |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* |
3 |
* ***** BEGIN LICENSE BLOCK ***** |
4 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
5 |
* |
6 |
* The contents of this file are subject to the Mozilla Public License Version |
7 |
* 1.1 (the "License"); you may not use this file except in compliance with |
8 |
* the License. You may obtain a copy of the License at |
9 |
* http://www.mozilla.org/MPL/ |
10 |
* |
11 |
* Software distributed under the License is distributed on an "AS IS" basis, |
12 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 |
* for the specific language governing rights and limitations under the |
14 |
* License. |
15 |
* |
16 |
* The Original Code is Mozilla Communicator client code, released |
17 |
* March 31, 1998. |
18 |
* |
19 |
* The Initial Developer of the Original Code is |
20 |
* Netscape Communications Corporation. |
21 |
* Portions created by the Initial Developer are Copyright (C) 1998 |
22 |
* the Initial Developer. All Rights Reserved. |
23 |
* |
24 |
* Contributor(s): |
25 |
* IBM Corp. |
26 |
* |
27 |
* Alternatively, the contents of this file may be used under the terms of |
28 |
* either of the GNU General Public License Version 2 or later (the "GPL"), |
29 |
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
30 |
* in which case the provisions of the GPL or the LGPL are applicable instead |
31 |
* of those above. If you wish to allow use of your version of this file only |
32 |
* under the terms of either the GPL or the LGPL, and not to allow others to |
33 |
* use your version of this file under the terms of the MPL, indicate your |
34 |
* decision by deleting the provisions above and replace them with the notice |
35 |
* and other provisions required by the GPL or the LGPL. If you do not delete |
36 |
* the provisions above, a recipient may use your version of this file under |
37 |
* the terms of any one of the MPL, the GPL or the LGPL. |
38 |
* |
39 |
* ***** END LICENSE BLOCK ***** */ |
40 |
|
41 |
#define __STDC_LIMIT_MACROS |
42 |
|
43 |
/* |
44 |
* JS number type and wrapper class. |
45 |
*/ |
46 |
#ifdef XP_OS2 |
47 |
#define _PC_53 PC_53 |
48 |
#define _MCW_EM MCW_EM |
49 |
#define _MCW_PC MCW_PC |
50 |
#endif |
51 |
#include <locale.h> |
52 |
#include <limits.h> |
53 |
#include <math.h> |
54 |
#include <stdlib.h> |
55 |
#include <string.h> |
56 |
#include "jstypes.h" |
57 |
#include "jsstdint.h" |
58 |
#include "jsutil.h" /* Added by JSIFY */ |
59 |
#include "jsapi.h" |
60 |
#include "jsatom.h" |
61 |
#include "jsbuiltins.h" |
62 |
#include "jscntxt.h" |
63 |
#include "jsversion.h" |
64 |
#include "jsdtoa.h" |
65 |
#include "jsgc.h" |
66 |
#include "jsinterp.h" |
67 |
#include "jsnum.h" |
68 |
#include "jsobj.h" |
69 |
#include "jsopcode.h" |
70 |
#include "jsprf.h" |
71 |
#include "jsscope.h" |
72 |
#include "jsstr.h" |
73 |
#include "jsstrinlines.h" |
74 |
#include "jsvector.h" |
75 |
|
76 |
|
77 |
#ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */ |
78 |
|
79 |
JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX); |
80 |
JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX); |
81 |
JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX); |
82 |
JS_STATIC_ASSERT(uint64_t(-1) == UINT64_MAX); |
83 |
|
84 |
JS_STATIC_ASSERT(INT8_MAX > INT8_MIN); |
85 |
JS_STATIC_ASSERT(uint8_t(INT8_MAX) + uint8_t(1) == uint8_t(INT8_MIN)); |
86 |
JS_STATIC_ASSERT(INT16_MAX > INT16_MIN); |
87 |
JS_STATIC_ASSERT(uint16_t(INT16_MAX) + uint16_t(1) == uint16_t(INT16_MIN)); |
88 |
JS_STATIC_ASSERT(INT32_MAX > INT32_MIN); |
89 |
JS_STATIC_ASSERT(uint32_t(INT32_MAX) + uint32_t(1) == uint32_t(INT32_MIN)); |
90 |
JS_STATIC_ASSERT(INT64_MAX > INT64_MIN); |
91 |
JS_STATIC_ASSERT(uint64_t(INT64_MAX) + uint64_t(1) == uint64_t(INT64_MIN)); |
92 |
|
93 |
JS_STATIC_ASSERT(INTPTR_MAX > INTPTR_MIN); |
94 |
JS_STATIC_ASSERT(uintptr_t(INTPTR_MAX) + uintptr_t(1) == uintptr_t(INTPTR_MIN)); |
95 |
JS_STATIC_ASSERT(uintptr_t(-1) == UINTPTR_MAX); |
96 |
JS_STATIC_ASSERT(size_t(-1) == SIZE_MAX); |
97 |
JS_STATIC_ASSERT(PTRDIFF_MAX > PTRDIFF_MIN); |
98 |
JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MAX) == PTRDIFF_MAX); |
99 |
JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MIN) == PTRDIFF_MIN); |
100 |
JS_STATIC_ASSERT(uintptr_t(PTRDIFF_MAX) + uintptr_t(1) == uintptr_t(PTRDIFF_MIN)); |
101 |
|
102 |
#endif /* JS_HAVE_STDINT_H */ |
103 |
|
104 |
static JSBool |
105 |
num_isNaN(JSContext *cx, uintN argc, jsval *vp) |
106 |
{ |
107 |
jsdouble x; |
108 |
|
109 |
if (argc == 0) { |
110 |
*vp = JSVAL_TRUE; |
111 |
return JS_TRUE; |
112 |
} |
113 |
x = js_ValueToNumber(cx, &vp[2]); |
114 |
if (JSVAL_IS_NULL(vp[2])) |
115 |
return JS_FALSE; |
116 |
*vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); |
117 |
return JS_TRUE; |
118 |
} |
119 |
|
120 |
static JSBool |
121 |
num_isFinite(JSContext *cx, uintN argc, jsval *vp) |
122 |
{ |
123 |
jsdouble x; |
124 |
|
125 |
if (argc == 0) { |
126 |
*vp = JSVAL_FALSE; |
127 |
return JS_TRUE; |
128 |
} |
129 |
x = js_ValueToNumber(cx, &vp[2]); |
130 |
if (JSVAL_IS_NULL(vp[2])) |
131 |
return JS_FALSE; |
132 |
*vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); |
133 |
return JS_TRUE; |
134 |
} |
135 |
|
136 |
static JSBool |
137 |
num_parseFloat(JSContext *cx, uintN argc, jsval *vp) |
138 |
{ |
139 |
JSString *str; |
140 |
jsdouble d; |
141 |
const jschar *bp, *end, *ep; |
142 |
|
143 |
if (argc == 0) { |
144 |
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
145 |
return JS_TRUE; |
146 |
} |
147 |
str = js_ValueToString(cx, vp[2]); |
148 |
if (!str) |
149 |
return JS_FALSE; |
150 |
str->getCharsAndEnd(bp, end); |
151 |
if (!js_strtod(cx, bp, end, &ep, &d)) |
152 |
return JS_FALSE; |
153 |
if (ep == bp) { |
154 |
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
155 |
return JS_TRUE; |
156 |
} |
157 |
return js_NewNumberInRootedValue(cx, d, vp); |
158 |
} |
159 |
|
160 |
#ifdef JS_TRACER |
161 |
static jsdouble FASTCALL |
162 |
ParseFloat(JSContext* cx, JSString* str) |
163 |
{ |
164 |
const jschar* bp; |
165 |
const jschar* end; |
166 |
const jschar* ep; |
167 |
jsdouble d; |
168 |
|
169 |
str->getCharsAndEnd(bp, end); |
170 |
if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) |
171 |
return js_NaN; |
172 |
return d; |
173 |
} |
174 |
#endif |
175 |
|
176 |
/* See ECMA 15.1.2.2. */ |
177 |
static JSBool |
178 |
num_parseInt(JSContext *cx, uintN argc, jsval *vp) |
179 |
{ |
180 |
jsint radix; |
181 |
JSString *str; |
182 |
jsdouble d; |
183 |
const jschar *bp, *end, *ep; |
184 |
|
185 |
if (argc == 0) { |
186 |
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
187 |
return JS_TRUE; |
188 |
} |
189 |
if (argc > 1) { |
190 |
radix = js_ValueToECMAInt32(cx, &vp[3]); |
191 |
if (JSVAL_IS_NULL(vp[3])) |
192 |
return JS_FALSE; |
193 |
} else { |
194 |
radix = 0; |
195 |
} |
196 |
if (radix != 0 && (radix < 2 || radix > 36)) { |
197 |
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
198 |
return JS_TRUE; |
199 |
} |
200 |
|
201 |
if (JSVAL_IS_INT(vp[2]) && (radix == 0 || radix == 10)) { |
202 |
*vp = vp[2]; |
203 |
return JS_TRUE; |
204 |
} |
205 |
|
206 |
str = js_ValueToString(cx, vp[2]); |
207 |
if (!str) |
208 |
return JS_FALSE; |
209 |
str->getCharsAndEnd(bp, end); |
210 |
if (!js_strtointeger(cx, bp, end, &ep, radix, &d)) |
211 |
return JS_FALSE; |
212 |
if (ep == bp) { |
213 |
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); |
214 |
return JS_TRUE; |
215 |
} |
216 |
return js_NewNumberInRootedValue(cx, d, vp); |
217 |
} |
218 |
|
219 |
#ifdef JS_TRACER |
220 |
static jsdouble FASTCALL |
221 |
ParseInt(JSContext* cx, JSString* str) |
222 |
{ |
223 |
const jschar* bp; |
224 |
const jschar* end; |
225 |
const jschar* ep; |
226 |
jsdouble d; |
227 |
|
228 |
str->getCharsAndEnd(bp, end); |
229 |
if (!js_strtointeger(cx, bp, end, &ep, 0, &d) || ep == bp) |
230 |
return js_NaN; |
231 |
return d; |
232 |
} |
233 |
|
234 |
static jsdouble FASTCALL |
235 |
ParseIntDouble(jsdouble d) |
236 |
{ |
237 |
if (!JSDOUBLE_IS_FINITE(d)) |
238 |
return js_NaN; |
239 |
if (d > 0) |
240 |
return floor(d); |
241 |
if (d < 0) |
242 |
return -floor(-d); |
243 |
return 0; |
244 |
} |
245 |
#endif |
246 |
|
247 |
const char js_Infinity_str[] = "Infinity"; |
248 |
const char js_NaN_str[] = "NaN"; |
249 |
const char js_isNaN_str[] = "isNaN"; |
250 |
const char js_isFinite_str[] = "isFinite"; |
251 |
const char js_parseFloat_str[] = "parseFloat"; |
252 |
const char js_parseInt_str[] = "parseInt"; |
253 |
|
254 |
#ifdef JS_TRACER |
255 |
|
256 |
JS_DEFINE_TRCINFO_2(num_parseInt, |
257 |
(2, (static, DOUBLE, ParseInt, CONTEXT, STRING, 1, 1)), |
258 |
(1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, 1))) |
259 |
|
260 |
JS_DEFINE_TRCINFO_1(num_parseFloat, |
261 |
(2, (static, DOUBLE, ParseFloat, CONTEXT, STRING, 1, 1))) |
262 |
|
263 |
#endif /* JS_TRACER */ |
264 |
|
265 |
static JSFunctionSpec number_functions[] = { |
266 |
JS_FN(js_isNaN_str, num_isNaN, 1,0), |
267 |
JS_FN(js_isFinite_str, num_isFinite, 1,0), |
268 |
JS_TN(js_parseFloat_str, num_parseFloat, 1,0, &num_parseFloat_trcinfo), |
269 |
JS_TN(js_parseInt_str, num_parseInt, 2,0, &num_parseInt_trcinfo), |
270 |
JS_FS_END |
271 |
}; |
272 |
|
273 |
JSClass js_NumberClass = { |
274 |
js_Number_str, |
275 |
JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), |
276 |
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
277 |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, |
278 |
JSCLASS_NO_OPTIONAL_MEMBERS |
279 |
}; |
280 |
|
281 |
static JSBool |
282 |
Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
283 |
{ |
284 |
jsval v; |
285 |
jsdouble d; |
286 |
|
287 |
if (argc != 0) { |
288 |
d = js_ValueToNumber(cx, &argv[0]); |
289 |
v = argv[0]; |
290 |
if (JSVAL_IS_NULL(v)) |
291 |
return JS_FALSE; |
292 |
if (v != JSVAL_TRUE) { |
293 |
JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)); |
294 |
} else { |
295 |
if (!js_NewNumberInRootedValue(cx, d, &argv[0])) |
296 |
return JS_FALSE; |
297 |
v = argv[0]; |
298 |
} |
299 |
} else { |
300 |
v = JSVAL_ZERO; |
301 |
} |
302 |
if (!JS_IsConstructing(cx)) |
303 |
*rval = v; |
304 |
else |
305 |
obj->fslots[JSSLOT_PRIMITIVE_THIS] = v; |
306 |
return true; |
307 |
} |
308 |
|
309 |
#if JS_HAS_TOSOURCE |
310 |
static JSBool |
311 |
num_toSource(JSContext *cx, uintN argc, jsval *vp) |
312 |
{ |
313 |
jsval v; |
314 |
jsdouble d; |
315 |
char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; |
316 |
char buf[64]; |
317 |
JSString *str; |
318 |
|
319 |
if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) |
320 |
return JS_FALSE; |
321 |
JS_ASSERT(JSVAL_IS_NUMBER(v)); |
322 |
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); |
323 |
numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); |
324 |
if (!numStr) { |
325 |
JS_ReportOutOfMemory(cx); |
326 |
return JS_FALSE; |
327 |
} |
328 |
JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); |
329 |
str = JS_NewStringCopyZ(cx, buf); |
330 |
if (!str) |
331 |
return JS_FALSE; |
332 |
*vp = STRING_TO_JSVAL(str); |
333 |
return JS_TRUE; |
334 |
} |
335 |
#endif |
336 |
|
337 |
/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ |
338 |
static char * |
339 |
IntToCString(jsint i, jsint base, char *buf, size_t bufSize) |
340 |
{ |
341 |
char *cp; |
342 |
jsuint u; |
343 |
|
344 |
u = (i < 0) ? -i : i; |
345 |
|
346 |
cp = buf + bufSize; /* one past last buffer cell */ |
347 |
*--cp = '\0'; /* null terminate the string to be */ |
348 |
|
349 |
/* |
350 |
* Build the string from behind. We use multiply and subtraction |
351 |
* instead of modulus because that's much faster. |
352 |
*/ |
353 |
switch (base) { |
354 |
case 10: |
355 |
do { |
356 |
jsuint newu = u / 10; |
357 |
*--cp = (char)(u - newu * 10) + '0'; |
358 |
u = newu; |
359 |
} while (u != 0); |
360 |
break; |
361 |
case 16: |
362 |
do { |
363 |
jsuint newu = u / 16; |
364 |
*--cp = "0123456789abcdef"[u - newu * 16]; |
365 |
u = newu; |
366 |
} while (u != 0); |
367 |
break; |
368 |
default: |
369 |
JS_ASSERT(base >= 2 && base <= 36); |
370 |
do { |
371 |
jsuint newu = u / base; |
372 |
*--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base]; |
373 |
u = newu; |
374 |
} while (u != 0); |
375 |
break; |
376 |
} |
377 |
if (i < 0) |
378 |
*--cp = '-'; |
379 |
|
380 |
JS_ASSERT(cp >= buf); |
381 |
return cp; |
382 |
} |
383 |
|
384 |
static JSBool |
385 |
num_toString(JSContext *cx, uintN argc, jsval *vp) |
386 |
{ |
387 |
jsval v; |
388 |
jsdouble d; |
389 |
jsint base; |
390 |
JSString *str; |
391 |
|
392 |
if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) |
393 |
return JS_FALSE; |
394 |
JS_ASSERT(JSVAL_IS_NUMBER(v)); |
395 |
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); |
396 |
base = 10; |
397 |
if (argc != 0 && !JSVAL_IS_VOID(vp[2])) { |
398 |
base = js_ValueToECMAInt32(cx, &vp[2]); |
399 |
if (JSVAL_IS_NULL(vp[2])) |
400 |
return JS_FALSE; |
401 |
if (base < 2 || base > 36) { |
402 |
char numBuf[12]; |
403 |
char *numStr = IntToCString(base, 10, numBuf, sizeof numBuf); |
404 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, |
405 |
numStr); |
406 |
return JS_FALSE; |
407 |
} |
408 |
} |
409 |
if (base == 10) { |
410 |
str = js_NumberToString(cx, d); |
411 |
} else { |
412 |
char *dStr = JS_dtobasestr(base, d); |
413 |
if (!dStr) { |
414 |
JS_ReportOutOfMemory(cx); |
415 |
return JS_FALSE; |
416 |
} |
417 |
str = JS_NewStringCopyZ(cx, dStr); |
418 |
js_free(dStr); |
419 |
} |
420 |
if (!str) |
421 |
return JS_FALSE; |
422 |
*vp = STRING_TO_JSVAL(str); |
423 |
return JS_TRUE; |
424 |
} |
425 |
|
426 |
static JSBool |
427 |
num_toLocaleString(JSContext *cx, uintN argc, jsval *vp) |
428 |
{ |
429 |
char thousandsLength, decimalLength; |
430 |
const char *numGrouping, *tmpGroup; |
431 |
JSRuntime *rt; |
432 |
JSString *numStr, *str; |
433 |
const char *num, *end, *tmpSrc; |
434 |
char *buf, *tmpDest; |
435 |
const char *nint; |
436 |
int digits, size, remainder, nrepeat; |
437 |
|
438 |
/* |
439 |
* Create the string, move back to bytes to make string twiddling |
440 |
* a bit easier and so we can insert platform charset seperators. |
441 |
*/ |
442 |
if (!num_toString(cx, 0, vp)) |
443 |
return JS_FALSE; |
444 |
JS_ASSERT(JSVAL_IS_STRING(*vp)); |
445 |
numStr = JSVAL_TO_STRING(*vp); |
446 |
num = js_GetStringBytes(cx, numStr); |
447 |
if (!num) |
448 |
return JS_FALSE; |
449 |
|
450 |
/* |
451 |
* Find the first non-integer value, whether it be a letter as in |
452 |
* 'Infinity', a decimal point, or an 'e' from exponential notation. |
453 |
*/ |
454 |
nint = num; |
455 |
if (*nint == '-') |
456 |
nint++; |
457 |
while (*nint >= '0' && *nint <= '9') |
458 |
nint++; |
459 |
digits = nint - num; |
460 |
end = num + digits; |
461 |
if (!digits) |
462 |
return JS_TRUE; |
463 |
|
464 |
rt = cx->runtime; |
465 |
thousandsLength = strlen(rt->thousandsSeparator); |
466 |
decimalLength = strlen(rt->decimalSeparator); |
467 |
|
468 |
/* Figure out how long resulting string will be. */ |
469 |
size = digits + (*nint ? strlen(nint + 1) + 1 : 0); |
470 |
if (*nint == '.') |
471 |
size += decimalLength; |
472 |
|
473 |
numGrouping = tmpGroup = rt->numGrouping; |
474 |
remainder = digits; |
475 |
if (*num == '-') |
476 |
remainder--; |
477 |
|
478 |
while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { |
479 |
if (*tmpGroup >= remainder) |
480 |
break; |
481 |
size += thousandsLength; |
482 |
remainder -= *tmpGroup; |
483 |
tmpGroup++; |
484 |
} |
485 |
if (*tmpGroup == '\0' && *numGrouping != '\0') { |
486 |
nrepeat = (remainder - 1) / tmpGroup[-1]; |
487 |
size += thousandsLength * nrepeat; |
488 |
remainder -= nrepeat * tmpGroup[-1]; |
489 |
} else { |
490 |
nrepeat = 0; |
491 |
} |
492 |
tmpGroup--; |
493 |
|
494 |
buf = (char *)cx->malloc(size + 1); |
495 |
if (!buf) |
496 |
return JS_FALSE; |
497 |
|
498 |
tmpDest = buf; |
499 |
tmpSrc = num; |
500 |
|
501 |
while (*tmpSrc == '-' || remainder--) |
502 |
*tmpDest++ = *tmpSrc++; |
503 |
while (tmpSrc < end) { |
504 |
strcpy(tmpDest, rt->thousandsSeparator); |
505 |
tmpDest += thousandsLength; |
506 |
memcpy(tmpDest, tmpSrc, *tmpGroup); |
507 |
tmpDest += *tmpGroup; |
508 |
tmpSrc += *tmpGroup; |
509 |
if (--nrepeat < 0) |
510 |
tmpGroup--; |
511 |
} |
512 |
|
513 |
if (*nint == '.') { |
514 |
strcpy(tmpDest, rt->decimalSeparator); |
515 |
tmpDest += decimalLength; |
516 |
strcpy(tmpDest, nint + 1); |
517 |
} else { |
518 |
strcpy(tmpDest, nint); |
519 |
} |
520 |
|
521 |
if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) |
522 |
return cx->localeCallbacks->localeToUnicode(cx, buf, vp); |
523 |
|
524 |
str = JS_NewString(cx, buf, size); |
525 |
if (!str) { |
526 |
cx->free(buf); |
527 |
return JS_FALSE; |
528 |
} |
529 |
|
530 |
*vp = STRING_TO_JSVAL(str); |
531 |
return JS_TRUE; |
532 |
} |
533 |
|
534 |
static JSBool |
535 |
num_valueOf(JSContext *cx, uintN argc, jsval *vp) |
536 |
{ |
537 |
jsval v; |
538 |
JSObject *obj; |
539 |
|
540 |
v = vp[1]; |
541 |
if (JSVAL_IS_NUMBER(v)) { |
542 |
*vp = v; |
543 |
return JS_TRUE; |
544 |
} |
545 |
obj = JS_THIS_OBJECT(cx, vp); |
546 |
if (!JS_InstanceOf(cx, obj, &js_NumberClass, vp + 2)) |
547 |
return JS_FALSE; |
548 |
*vp = obj->fslots[JSSLOT_PRIMITIVE_THIS]; |
549 |
return JS_TRUE; |
550 |
} |
551 |
|
552 |
|
553 |
#define MAX_PRECISION 100 |
554 |
|
555 |
static JSBool |
556 |
num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, |
557 |
jsint precisionMin, jsint precisionMax, jsint precisionOffset, |
558 |
uintN argc, jsval *vp) |
559 |
{ |
560 |
jsval v; |
561 |
jsdouble d, precision; |
562 |
JSString *str; |
563 |
|
564 |
/* Use MAX_PRECISION+1 because precisionOffset can be 1. */ |
565 |
char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)]; |
566 |
char *numStr; |
567 |
|
568 |
if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) |
569 |
return JS_FALSE; |
570 |
JS_ASSERT(JSVAL_IS_NUMBER(v)); |
571 |
d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); |
572 |
|
573 |
if (argc == 0) { |
574 |
precision = 0.0; |
575 |
oneArgMode = zeroArgMode; |
576 |
} else { |
577 |
precision = js_ValueToNumber(cx, &vp[2]); |
578 |
if (JSVAL_IS_NULL(vp[2])) |
579 |
return JS_FALSE; |
580 |
precision = js_DoubleToInteger(precision); |
581 |
if (precision < precisionMin || precision > precisionMax) { |
582 |
numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); |
583 |
if (!numStr) |
584 |
JS_ReportOutOfMemory(cx); |
585 |
else |
586 |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); |
587 |
return JS_FALSE; |
588 |
} |
589 |
} |
590 |
|
591 |
numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); |
592 |
if (!numStr) { |
593 |
JS_ReportOutOfMemory(cx); |
594 |
return JS_FALSE; |
595 |
} |
596 |
str = JS_NewStringCopyZ(cx, numStr); |
597 |
if (!str) |
598 |
return JS_FALSE; |
599 |
*vp = STRING_TO_JSVAL(str); |
600 |
return JS_TRUE; |
601 |
} |
602 |
|
603 |
/* |
604 |
* In the following three implementations, we allow a larger range of precision |
605 |
* than ECMA requires; this is permitted by ECMA-262. |
606 |
*/ |
607 |
static JSBool |
608 |
num_toFixed(JSContext *cx, uintN argc, jsval *vp) |
609 |
{ |
610 |
return num_to(cx, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0, |
611 |
argc, vp); |
612 |
} |
613 |
|
614 |
static JSBool |
615 |
num_toExponential(JSContext *cx, uintN argc, jsval *vp) |
616 |
{ |
617 |
return num_to(cx, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, |
618 |
MAX_PRECISION, 1, argc, vp); |
619 |
} |
620 |
|
621 |
static JSBool |
622 |
num_toPrecision(JSContext *cx, uintN argc, jsval *vp) |
623 |
{ |
624 |
if (argc == 0 || JSVAL_IS_VOID(vp[2])) |
625 |
return num_toString(cx, 0, vp); |
626 |
return num_to(cx, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0, |
627 |
argc, vp); |
628 |
} |
629 |
|
630 |
#ifdef JS_TRACER |
631 |
|
632 |
JS_DEFINE_TRCINFO_1(num_toString, |
633 |
(2, (extern, STRING_RETRY, js_NumberToString, CONTEXT, THIS_DOUBLE, 1, 1))) |
634 |
|
635 |
#endif /* JS_TRACER */ |
636 |
|
637 |
static JSFunctionSpec number_methods[] = { |
638 |
#if JS_HAS_TOSOURCE |
639 |
JS_FN(js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER), |
640 |
#endif |
641 |
JS_TN(js_toString_str, num_toString, 1,JSFUN_THISP_NUMBER, &num_toString_trcinfo), |
642 |
JS_FN(js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER), |
643 |
JS_FN(js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER), |
644 |
JS_FN(js_toJSON_str, num_valueOf, 0,JSFUN_THISP_NUMBER), |
645 |
JS_FN("toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER), |
646 |
JS_FN("toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER), |
647 |
JS_FN("toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER), |
648 |
JS_FS_END |
649 |
}; |
650 |
|
651 |
/* NB: Keep this in synch with number_constants[]. */ |
652 |
enum nc_slot { |
653 |
NC_NaN, |
654 |
NC_POSITIVE_INFINITY, |
655 |
NC_NEGATIVE_INFINITY, |
656 |
NC_MAX_VALUE, |
657 |
NC_MIN_VALUE, |
658 |
NC_LIMIT |
659 |
}; |
660 |
|
661 |
/* |
662 |
* Some to most C compilers forbid spelling these at compile time, or barf |
663 |
* if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState |
664 |
* using union jsdpun. |
665 |
*/ |
666 |
static JSConstDoubleSpec number_constants[] = { |
667 |
{0, js_NaN_str, 0,{0,0,0}}, |
668 |
{0, "POSITIVE_INFINITY", 0,{0,0,0}}, |
669 |
{0, "NEGATIVE_INFINITY", 0,{0,0,0}}, |
670 |
{1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, |
671 |
{0, "MIN_VALUE", 0,{0,0,0}}, |
672 |
{0,0,0,{0,0,0}} |
673 |
}; |
674 |
|
675 |
jsdouble js_NaN; |
676 |
|
677 |
|
678 |
#if (defined __GNUC__ && defined __i386__) |
679 |
|
680 |
/* |
681 |
* Set the exception mask to mask all exceptions and set the FPU precision |
682 |
* to 53 bit mantissa (64 bit doubles). |
683 |
*/ |
684 |
inline void FIX_FPU() { |
685 |
short control; |
686 |
asm("fstcw %0" : "=m" (control) : ); |
687 |
control &= ~0x300; // Lower bits 8 and 9 (precision control). |
688 |
control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision). |
689 |
asm("fldcw %0" : : "m" (control) ); |
690 |
} |
691 |
|
692 |
#else |
693 |
|
694 |
#define FIX_FPU() ((void)0) |
695 |
|
696 |
#endif |
697 |
|
698 |
JSBool |
699 |
js_InitRuntimeNumberState(JSContext *cx) |
700 |
{ |
701 |
JSRuntime *rt; |
702 |
jsdpun u; |
703 |
struct lconv *locale; |
704 |
|
705 |
rt = cx->runtime; |
706 |
JS_ASSERT(!rt->jsNaN); |
707 |
|
708 |
FIX_FPU(); |
709 |
|
710 |
u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; |
711 |
u.s.lo = 0xffffffff; |
712 |
number_constants[NC_NaN].dval = js_NaN = u.d; |
713 |
rt->jsNaN = js_NewWeaklyRootedDouble(cx, js_NaN); |
714 |
if (!rt->jsNaN) |
715 |
return JS_FALSE; |
716 |
|
717 |
u.s.hi = JSDOUBLE_HI32_EXPMASK; |
718 |
u.s.lo = 0x00000000; |
719 |
number_constants[NC_POSITIVE_INFINITY].dval = u.d; |
720 |
rt->jsPositiveInfinity = js_NewWeaklyRootedDouble(cx, u.d); |
721 |
if (!rt->jsPositiveInfinity) |
722 |
return JS_FALSE; |
723 |
|
724 |
u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; |
725 |
u.s.lo = 0x00000000; |
726 |
number_constants[NC_NEGATIVE_INFINITY].dval = u.d; |
727 |
rt->jsNegativeInfinity = js_NewWeaklyRootedDouble(cx, u.d); |
728 |
if (!rt->jsNegativeInfinity) |
729 |
return JS_FALSE; |
730 |
|
731 |
u.s.hi = 0; |
732 |
u.s.lo = 1; |
733 |
number_constants[NC_MIN_VALUE].dval = u.d; |
734 |
|
735 |
locale = localeconv(); |
736 |
rt->thousandsSeparator = |
737 |
JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); |
738 |
rt->decimalSeparator = |
739 |
JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); |
740 |
rt->numGrouping = |
741 |
JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); |
742 |
|
743 |
return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; |
744 |
} |
745 |
|
746 |
void |
747 |
js_TraceRuntimeNumberState(JSTracer *trc) |
748 |
{ |
749 |
JSRuntime *rt; |
750 |
|
751 |
rt = trc->context->runtime; |
752 |
if (rt->jsNaN) |
753 |
JS_CALL_DOUBLE_TRACER(trc, rt->jsNaN, "NaN"); |
754 |
if (rt->jsPositiveInfinity) |
755 |
JS_CALL_DOUBLE_TRACER(trc, rt->jsPositiveInfinity, "+Infinity"); |
756 |
if (rt->jsNegativeInfinity) |
757 |
JS_CALL_DOUBLE_TRACER(trc, rt->jsNegativeInfinity, "-Infinity"); |
758 |
} |
759 |
|
760 |
void |
761 |
js_FinishRuntimeNumberState(JSContext *cx) |
762 |
{ |
763 |
JSRuntime *rt = cx->runtime; |
764 |
|
765 |
rt->jsNaN = NULL; |
766 |
rt->jsNegativeInfinity = NULL; |
767 |
rt->jsPositiveInfinity = NULL; |
768 |
|
769 |
cx->free((void *)rt->thousandsSeparator); |
770 |
cx->free((void *)rt->decimalSeparator); |
771 |
cx->free((void *)rt->numGrouping); |
772 |
rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; |
773 |
} |
774 |
|
775 |
JSObject * |
776 |
js_InitNumberClass(JSContext *cx, JSObject *obj) |
777 |
{ |
778 |
JSObject *proto, *ctor; |
779 |
JSRuntime *rt; |
780 |
|
781 |
/* XXX must do at least once per new thread, so do it per JSContext... */ |
782 |
FIX_FPU(); |
783 |
|
784 |
if (!JS_DefineFunctions(cx, obj, number_functions)) |
785 |
return NULL; |
786 |
|
787 |
proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, |
788 |
NULL, number_methods, NULL, NULL); |
789 |
if (!proto || !(ctor = JS_GetConstructor(cx, proto))) |
790 |
return NULL; |
791 |
proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_ZERO; |
792 |
if (!JS_DefineConstDoubles(cx, ctor, number_constants)) |
793 |
return NULL; |
794 |
|
795 |
/* ECMA 15.1.1.1 */ |
796 |
rt = cx->runtime; |
797 |
if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN), |
798 |
NULL, NULL, JSPROP_PERMANENT)) { |
799 |
return NULL; |
800 |
} |
801 |
|
802 |
/* ECMA 15.1.1.2 */ |
803 |
if (!JS_DefineProperty(cx, obj, js_Infinity_str, |
804 |
DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), |
805 |
NULL, NULL, JSPROP_PERMANENT)) { |
806 |
return NULL; |
807 |
} |
808 |
return proto; |
809 |
} |
810 |
|
811 |
JSBool |
812 |
js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp) |
813 |
{ |
814 |
jsint i; |
815 |
|
816 |
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { |
817 |
*vp = INT_TO_JSVAL(i); |
818 |
return JS_TRUE; |
819 |
} |
820 |
return js_NewDoubleInRootedValue(cx, d, vp); |
821 |
} |
822 |
|
823 |
JSBool |
824 |
js_NewWeaklyRootedNumber(JSContext *cx, jsdouble d, jsval *rval) |
825 |
{ |
826 |
jsint i; |
827 |
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { |
828 |
*rval = INT_TO_JSVAL(i); |
829 |
return JS_TRUE; |
830 |
} |
831 |
return JS_NewDoubleValue(cx, d, rval); |
832 |
} |
833 |
|
834 |
/* |
835 |
* Convert a number to C string. The buf must be large enough to accommodate |
836 |
* the result, including '-' and '\0', if base == 10 or d is an integer that |
837 |
* fits in 32 bits. The caller must free the resulting pointer if it does not |
838 |
* point into buf. |
839 |
*/ |
840 |
static char * |
841 |
NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize) |
842 |
{ |
843 |
jsint i; |
844 |
char *numStr; |
845 |
|
846 |
JS_ASSERT(bufSize >= DTOSTR_STANDARD_BUFFER_SIZE); |
847 |
if (JSDOUBLE_IS_INT(d, i)) { |
848 |
numStr = IntToCString(i, base, buf, bufSize); |
849 |
} else { |
850 |
if (base == 10) |
851 |
numStr = JS_dtostr(buf, bufSize, DTOSTR_STANDARD, 0, d); |
852 |
else |
853 |
numStr = JS_dtobasestr(base, d); |
854 |
if (!numStr) { |
855 |
JS_ReportOutOfMemory(cx); |
856 |
return NULL; |
857 |
} |
858 |
} |
859 |
return numStr; |
860 |
} |
861 |
|
862 |
static JSString * |
863 |
NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) |
864 |
{ |
865 |
/* |
866 |
* The longest possible result here that would need to fit in buf is |
867 |
* (-0x80000000).toString(2), which has length 33. (This can produce |
868 |
* longer results, but in those cases buf is not used; see comment at |
869 |
* NumberToCString.) |
870 |
*/ |
871 |
char buf[34]; |
872 |
char *numStr; |
873 |
JSString *s; |
874 |
|
875 |
if (base < 2 || base > 36) |
876 |
return NULL; |
877 |
numStr = NumberToCString(cx, d, base, buf, sizeof buf); |
878 |
if (!numStr) |
879 |
return NULL; |
880 |
s = JS_NewStringCopyZ(cx, numStr); |
881 |
if (!(numStr >= buf && numStr < buf + sizeof buf)) |
882 |
js_free(numStr); |
883 |
return s; |
884 |
} |
885 |
|
886 |
JSString * JS_FASTCALL |
887 |
js_NumberToString(JSContext *cx, jsdouble d) |
888 |
{ |
889 |
jsint i; |
890 |
|
891 |
if (JSDOUBLE_IS_INT(d, i) && jsuint(i) < INT_STRING_LIMIT) |
892 |
return JSString::intString(i); |
893 |
return NumberToStringWithBase(cx, d, 10); |
894 |
} |
895 |
|
896 |
JSBool JS_FASTCALL |
897 |
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharBuffer &cb) |
898 |
{ |
899 |
/* Convert to C-string. */ |
900 |
static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE; |
901 |
char arr[arrSize]; |
902 |
const char *cstr; |
903 |
if (JSVAL_IS_INT(v)) { |
904 |
cstr = IntToCString(JSVAL_TO_INT(v), 10, arr, arrSize); |
905 |
} else { |
906 |
JS_ASSERT(JSVAL_IS_DOUBLE(v)); |
907 |
cstr = JS_dtostr(arr, arrSize, DTOSTR_STANDARD, 0, *JSVAL_TO_DOUBLE(v)); |
908 |
} |
909 |
if (!cstr) |
910 |
return JS_FALSE; |
911 |
|
912 |
/* |
913 |
* Inflate to jschar string. The input C-string characters are < 127, so |
914 |
* even if jschars are UTF-8, all chars should map to one jschar. |
915 |
*/ |
916 |
size_t cstrlen = strlen(cstr); |
917 |
JS_ASSERT(cstrlen < arrSize); |
918 |
size_t sizeBefore = cb.length(); |
919 |
if (!cb.growBy(cstrlen)) |
920 |
return JS_FALSE; |
921 |
jschar *appendBegin = cb.begin() + sizeBefore; |
922 |
#ifdef DEBUG |
923 |
size_t oldcstrlen = cstrlen; |
924 |
JSBool ok = |
925 |
#endif |
926 |
js_InflateStringToBuffer(cx, cstr, cstrlen, appendBegin, &cstrlen); |
927 |
JS_ASSERT(ok && cstrlen == oldcstrlen); |
928 |
return JS_TRUE; |
929 |
} |
930 |
|
931 |
jsdouble |
932 |
js_ValueToNumber(JSContext *cx, jsval *vp) |
933 |
{ |
934 |
jsval v; |
935 |
JSString *str; |
936 |
const jschar *bp, *end, *ep; |
937 |
jsdouble d, *dp; |
938 |
JSObject *obj; |
939 |
|
940 |
v = *vp; |
941 |
for (;;) { |
942 |
if (JSVAL_IS_INT(v)) |
943 |
return (jsdouble) JSVAL_TO_INT(v); |
944 |
if (JSVAL_IS_DOUBLE(v)) |
945 |
return *JSVAL_TO_DOUBLE(v); |
946 |
if (JSVAL_IS_STRING(v)) { |
947 |
str = JSVAL_TO_STRING(v); |
948 |
|
949 |
/* |
950 |
* Note that ECMA doesn't treat a string beginning with a '0' as |
951 |
* an octal number here. This works because all such numbers will |
952 |
* be interpreted as decimal by js_strtod and will never get |
953 |
* passed to js_strtointeger (which would interpret them as |
954 |
* octal). |
955 |
*/ |
956 |
str->getCharsAndEnd(bp, end); |
957 |
|
958 |
/* ECMA doesn't allow signed hex numbers (bug 273467). */ |
959 |
bp = js_SkipWhiteSpace(bp, end); |
960 |
if (bp + 2 < end && (*bp == '-' || *bp == '+') && |
961 |
bp[1] == '0' && (bp[2] == 'X' || bp[2] == 'x')) { |
962 |
break; |
963 |
} |
964 |
|
965 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
966 |
js_SkipWhiteSpace(ep, end) != end) && |
967 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
968 |
js_SkipWhiteSpace(ep, end) != end)) { |
969 |
break; |
970 |
} |
971 |
|
972 |
/* |
973 |
* JSVAL_TRUE indicates that double jsval was never constructed |
974 |
* for the result. |
975 |
*/ |
976 |
*vp = JSVAL_TRUE; |
977 |
return d; |
978 |
} |
979 |
if (JSVAL_IS_BOOLEAN(v)) { |
980 |
if (JSVAL_TO_BOOLEAN(v)) { |
981 |
*vp = JSVAL_ONE; |
982 |
return 1.0; |
983 |
} else { |
984 |
*vp = JSVAL_ZERO; |
985 |
return 0.0; |
986 |
} |
987 |
} |
988 |
if (JSVAL_IS_NULL(v)) { |
989 |
*vp = JSVAL_ZERO; |
990 |
return 0.0; |
991 |
} |
992 |
if (JSVAL_IS_VOID(v)) |
993 |
break; |
994 |
|
995 |
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); |
996 |
obj = JSVAL_TO_OBJECT(v); |
997 |
|
998 |
/* |
999 |
* vp roots obj so we cannot use it as an extra root for |
1000 |
* obj->defaultValue result when calling the hook. |
1001 |
*/ |
1002 |
JSAutoTempValueRooter tvr(cx, v); |
1003 |
if (!obj->defaultValue(cx, JSTYPE_NUMBER, tvr.addr())) |
1004 |
obj = NULL; |
1005 |
else |
1006 |
v = *vp = tvr.value(); |
1007 |
if (!obj) { |
1008 |
*vp = JSVAL_NULL; |
1009 |
return 0.0; |
1010 |
} |
1011 |
if (!JSVAL_IS_PRIMITIVE(v)) |
1012 |
break; |
1013 |
} |
1014 |
|
1015 |
dp = cx->runtime->jsNaN; |
1016 |
*vp = DOUBLE_TO_JSVAL(dp); |
1017 |
return *dp; |
1018 |
} |
1019 |
|
1020 |
int32 |
1021 |
js_ValueToECMAInt32(JSContext *cx, jsval *vp) |
1022 |
{ |
1023 |
jsval v; |
1024 |
jsdouble d; |
1025 |
|
1026 |
v = *vp; |
1027 |
if (JSVAL_IS_INT(v)) |
1028 |
return JSVAL_TO_INT(v); |
1029 |
if (JSVAL_IS_DOUBLE(v)) { |
1030 |
d = *JSVAL_TO_DOUBLE(v); |
1031 |
*vp = JSVAL_TRUE; |
1032 |
} else { |
1033 |
d = js_ValueToNumber(cx, vp); |
1034 |
if (JSVAL_IS_NULL(*vp)) |
1035 |
return 0; |
1036 |
*vp = JSVAL_TRUE; |
1037 |
} |
1038 |
return js_DoubleToECMAInt32(d); |
1039 |
} |
1040 |
|
1041 |
uint32 |
1042 |
js_ValueToECMAUint32(JSContext *cx, jsval *vp) |
1043 |
{ |
1044 |
jsval v; |
1045 |
jsint i; |
1046 |
jsdouble d; |
1047 |
|
1048 |
v = *vp; |
1049 |
if (JSVAL_IS_INT(v)) { |
1050 |
i = JSVAL_TO_INT(v); |
1051 |
if (i < 0) |
1052 |
*vp = JSVAL_TRUE; |
1053 |
return (uint32) i; |
1054 |
} |
1055 |
if (JSVAL_IS_DOUBLE(v)) { |
1056 |
d = *JSVAL_TO_DOUBLE(v); |
1057 |
*vp = JSVAL_TRUE; |
1058 |
} else { |
1059 |
d = js_ValueToNumber(cx, vp); |
1060 |
if (JSVAL_IS_NULL(*vp)) |
1061 |
return 0; |
1062 |
*vp = JSVAL_TRUE; |
1063 |
} |
1064 |
return js_DoubleToECMAUint32(d); |
1065 |
} |
1066 |
|
1067 |
uint32 |
1068 |
js_DoubleToECMAUint32(jsdouble d) |
1069 |
{ |
1070 |
int32 i; |
1071 |
JSBool neg; |
1072 |
jsdouble two32; |
1073 |
|
1074 |
if (!JSDOUBLE_IS_FINITE(d)) |
1075 |
return 0; |
1076 |
|
1077 |
/* |
1078 |
* We check whether d fits int32, not uint32, as all but the ">>>" bit |
1079 |
* manipulation bytecode stores the result as int, not uint. When the |
1080 |
* result does not fit int jsval, it will be stored as a negative double. |
1081 |
*/ |
1082 |
i = (int32) d; |
1083 |
if ((jsdouble) i == d) |
1084 |
return (int32)i; |
1085 |
|
1086 |
neg = (d < 0); |
1087 |
d = floor(neg ? -d : d); |
1088 |
d = neg ? -d : d; |
1089 |
|
1090 |
two32 = 4294967296.0; |
1091 |
d = fmod(d, two32); |
1092 |
|
1093 |
return (uint32) (d >= 0 ? d : d + two32); |
1094 |
} |
1095 |
|
1096 |
int32 |
1097 |
js_ValueToInt32(JSContext *cx, jsval *vp) |
1098 |
{ |
1099 |
jsval v; |
1100 |
jsdouble d; |
1101 |
|
1102 |
v = *vp; |
1103 |
if (JSVAL_IS_INT(v)) |
1104 |
return JSVAL_TO_INT(v); |
1105 |
d = js_ValueToNumber(cx, vp); |
1106 |
if (JSVAL_IS_NULL(*vp)) |
1107 |
return 0; |
1108 |
if (JSVAL_IS_INT(*vp)) |
1109 |
return JSVAL_TO_INT(*vp); |
1110 |
|
1111 |
*vp = JSVAL_TRUE; |
1112 |
if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { |
1113 |
js_ReportValueError(cx, JSMSG_CANT_CONVERT, |
1114 |
JSDVG_SEARCH_STACK, v, NULL); |
1115 |
*vp = JSVAL_NULL; |
1116 |
return 0; |
1117 |
} |
1118 |
return (int32) floor(d + 0.5); /* Round to nearest */ |
1119 |
} |
1120 |
|
1121 |
uint16 |
1122 |
js_ValueToUint16(JSContext *cx, jsval *vp) |
1123 |
{ |
1124 |
jsdouble d; |
1125 |
uint16 u; |
1126 |
jsuint m; |
1127 |
JSBool neg; |
1128 |
|
1129 |
d = js_ValueToNumber(cx, vp); |
1130 |
if (JSVAL_IS_NULL(*vp)) |
1131 |
return 0; |
1132 |
|
1133 |
if (JSVAL_IS_INT(*vp)) { |
1134 |
u = (uint16) JSVAL_TO_INT(*vp); |
1135 |
} else if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { |
1136 |
u = (uint16) 0; |
1137 |
} else { |
1138 |
u = (uint16) d; |
1139 |
if ((jsdouble) u != d) { |
1140 |
neg = (d < 0); |
1141 |
d = floor(neg ? -d : d); |
1142 |
d = neg ? -d : d; |
1143 |
m = JS_BIT(16); |
1144 |
d = fmod(d, (double) m); |
1145 |
if (d < 0) |
1146 |
d += m; |
1147 |
u = (uint16) d; |
1148 |
} |
1149 |
} |
1150 |
*vp = INT_TO_JSVAL(u); |
1151 |
return u; |
1152 |
} |
1153 |
|
1154 |
JSBool |
1155 |
js_strtod(JSContext *cx, const jschar *s, const jschar *send, |
1156 |
const jschar **ep, jsdouble *dp) |
1157 |
{ |
1158 |
const jschar *s1; |
1159 |
size_t length, i; |
1160 |
char cbuf[32]; |
1161 |
char *cstr, *istr, *estr; |
1162 |
JSBool negative; |
1163 |
jsdouble d; |
1164 |
|
1165 |
s1 = js_SkipWhiteSpace(s, send); |
1166 |
length = send - s1; |
1167 |
|
1168 |
/* Use cbuf to avoid malloc */ |
1169 |
if (length >= sizeof cbuf) { |
1170 |
cstr = (char *) cx->malloc(length + 1); |
1171 |
if (!cstr) |
1172 |
return JS_FALSE; |
1173 |
} else { |
1174 |
cstr = cbuf; |
1175 |
} |
1176 |
|
1177 |
for (i = 0; i != length; i++) { |
1178 |
if (s1[i] >> 8) |
1179 |
break; |
1180 |
cstr[i] = (char)s1[i]; |
1181 |
} |
1182 |
cstr[i] = 0; |
1183 |
|
1184 |
istr = cstr; |
1185 |
if ((negative = (*istr == '-')) != 0 || *istr == '+') |
1186 |
istr++; |
1187 |
if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { |
1188 |
d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); |
1189 |
estr = istr + 8; |
1190 |
} else { |
1191 |
int err; |
1192 |
d = JS_strtod(cstr, &estr, &err); |
1193 |
if (d == HUGE_VAL) |
1194 |
d = *cx->runtime->jsPositiveInfinity; |
1195 |
else if (d == -HUGE_VAL) |
1196 |
d = *cx->runtime->jsNegativeInfinity; |
1197 |
} |
1198 |
|
1199 |
i = estr - cstr; |
1200 |
if (cstr != cbuf) |
1201 |
cx->free(cstr); |
1202 |
*ep = i ? s1 + i : s; |
1203 |
*dp = d; |
1204 |
return JS_TRUE; |
1205 |
} |
1206 |
|
1207 |
struct BinaryDigitReader |
1208 |
{ |
1209 |
uintN base; /* Base of number; must be a power of 2 */ |
1210 |
uintN digit; /* Current digit value in radix given by base */ |
1211 |
uintN digitMask; /* Mask to extract the next bit from digit */ |
1212 |
const jschar *digits; /* Pointer to the remaining digits */ |
1213 |
const jschar *end; /* Pointer to first non-digit */ |
1214 |
}; |
1215 |
|
1216 |
/* Return the next binary digit from the number or -1 if done */ |
1217 |
static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) |
1218 |
{ |
1219 |
intN bit; |
1220 |
|
1221 |
if (bdr->digitMask == 0) { |
1222 |
uintN c; |
1223 |
|
1224 |
if (bdr->digits == bdr->end) |
1225 |
return -1; |
1226 |
|
1227 |
c = *bdr->digits++; |
1228 |
if ('0' <= c && c <= '9') |
1229 |
bdr->digit = c - '0'; |
1230 |
else if ('a' <= c && c <= 'z') |
1231 |
bdr->digit = c - 'a' + 10; |
1232 |
else |
1233 |
bdr->digit = c - 'A' + 10; |
1234 |
bdr->digitMask = bdr->base >> 1; |
1235 |
} |
1236 |
bit = (bdr->digit & bdr->digitMask) != 0; |
1237 |
bdr->digitMask >>= 1; |
1238 |
return bit; |
1239 |
} |
1240 |
|
1241 |
JSBool |
1242 |
js_strtointeger(JSContext *cx, const jschar *s, const jschar *send, |
1243 |
const jschar **ep, jsint base, jsdouble *dp) |
1244 |
{ |
1245 |
const jschar *s1, *start; |
1246 |
JSBool negative; |
1247 |
jsdouble value; |
1248 |
|
1249 |
s1 = js_SkipWhiteSpace(s, send); |
1250 |
if (s1 == send) |
1251 |
goto no_digits; |
1252 |
if ((negative = (*s1 == '-')) != 0 || *s1 == '+') { |
1253 |
s1++; |
1254 |
if (s1 == send) |
1255 |
goto no_digits; |
1256 |
} |
1257 |
|
1258 |
if (base == 0) { |
1259 |
/* No base supplied, or some base that evaluated to 0. */ |
1260 |
if (*s1 == '0') { |
1261 |
/* It's either hex or octal; only increment char if str isn't '0' */ |
1262 |
if (s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { |
1263 |
base = 16; |
1264 |
s1 += 2; |
1265 |
if (s1 == send) |
1266 |
goto no_digits; |
1267 |
} else { |
1268 |
base = 8; |
1269 |
} |
1270 |
} else { |
1271 |
base = 10; /* Default to decimal. */ |
1272 |
} |
1273 |
} else if (base == 16) { |
1274 |
/* If base is 16, ignore hex prefix. */ |
1275 |
if (*s1 == '0' && s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { |
1276 |
s1 += 2; |
1277 |
if (s1 == send) |
1278 |
goto no_digits; |
1279 |
} |
1280 |
} |
1281 |
|
1282 |
/* |
1283 |
* Done with the preliminaries; find some prefix of the string that's |
1284 |
* a number in the given base. |
1285 |
*/ |
1286 |
JS_ASSERT(s1 < send); |
1287 |
start = s1; |
1288 |
value = 0.0; |
1289 |
do { |
1290 |
uintN digit; |
1291 |
jschar c = *s1; |
1292 |
if ('0' <= c && c <= '9') |
1293 |
digit = c - '0'; |
1294 |
else if ('a' <= c && c <= 'z') |
1295 |
digit = c - 'a' + 10; |
1296 |
else if ('A' <= c && c <= 'Z') |
1297 |
digit = c - 'A' + 10; |
1298 |
else |
1299 |
break; |
1300 |
if (digit >= (uintN)base) |
1301 |
break; |
1302 |
value = value * base + digit; |
1303 |
} while (++s1 != send); |
1304 |
|
1305 |
if (value >= 9007199254740992.0) { |
1306 |
if (base == 10) { |
1307 |
/* |
1308 |
* If we're accumulating a decimal number and the number is >= |
1309 |
* 2^53, then the result from the repeated multiply-add above may |
1310 |
* be inaccurate. Call JS_strtod to get the correct answer. |
1311 |
*/ |
1312 |
size_t i; |
1313 |
size_t length = s1 - start; |
1314 |
char *cstr = (char *) cx->malloc(length + 1); |
1315 |
char *estr; |
1316 |
int err=0; |
1317 |
|
1318 |
if (!cstr) |
1319 |
return JS_FALSE; |
1320 |
for (i = 0; i != length; i++) |
1321 |
cstr[i] = (char)start[i]; |
1322 |
cstr[length] = 0; |
1323 |
|
1324 |
value = JS_strtod(cstr, &estr, &err); |
1325 |
if (err == JS_DTOA_ENOMEM) { |
1326 |
JS_ReportOutOfMemory(cx); |
1327 |
cx->free(cstr); |
1328 |
return JS_FALSE; |
1329 |
} |
1330 |
if (err == JS_DTOA_ERANGE && value == HUGE_VAL) |
1331 |
value = *cx->runtime->jsPositiveInfinity; |
1332 |
cx->free(cstr); |
1333 |
} else if ((base & (base - 1)) == 0) { |
1334 |
/* |
1335 |
* The number may also be inaccurate for power-of-two bases. This |
1336 |
* happens if the addition in value * base + digit causes a round- |
1337 |
* down to an even least significant mantissa bit when the first |
1338 |
* dropped bit is a one. If any of the following digits in the |
1339 |
* number (which haven't been added in yet) are nonzero, then the |
1340 |
* correct action would have been to round up instead of down. An |
1341 |
* example occurs when reading the number 0x1000000000000081, which |
1342 |
* rounds to 0x1000000000000000 instead of 0x1000000000000100. |
1343 |
*/ |
1344 |
struct BinaryDigitReader bdr; |
1345 |
intN bit, bit2; |
1346 |
intN j; |
1347 |
|
1348 |
bdr.base = base; |
1349 |
bdr.digitMask = 0; |
1350 |
bdr.digits = start; |
1351 |
bdr.end = s1; |
1352 |
value = 0.0; |
1353 |
|
1354 |
/* Skip leading zeros. */ |
1355 |
do { |
1356 |
bit = GetNextBinaryDigit(&bdr); |
1357 |
} while (bit == 0); |
1358 |
|
1359 |
if (bit == 1) { |
1360 |
/* Gather the 53 significant bits (including the leading 1) */ |
1361 |
value = 1.0; |
1362 |
for (j = 52; j; j--) { |
1363 |
bit = GetNextBinaryDigit(&bdr); |
1364 |
if (bit < 0) |
1365 |
goto done; |
1366 |
value = value*2 + bit; |
1367 |
} |
1368 |
/* bit2 is the 54th bit (the first dropped from the mantissa) */ |
1369 |
bit2 = GetNextBinaryDigit(&bdr); |
1370 |
if (bit2 >= 0) { |
1371 |
jsdouble factor = 2.0; |
1372 |
intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ |
1373 |
intN bit3; |
1374 |
|
1375 |
while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { |
1376 |
sticky |= bit3; |
1377 |
factor *= 2; |
1378 |
} |
1379 |
value += bit2 & (bit | sticky); |
1380 |
value *= factor; |
1381 |
} |
1382 |
done:; |
1383 |
} |
1384 |
} |
1385 |
} |
1386 |
/* We don't worry about inaccurate numbers for any other base. */ |
1387 |
|
1388 |
if (s1 == start) { |
1389 |
no_digits: |
1390 |
*dp = 0.0; |
1391 |
*ep = s; |
1392 |
} else { |
1393 |
*dp = negative ? -value : value; |
1394 |
*ep = s1; |
1395 |
} |
1396 |
return JS_TRUE; |
1397 |
} |