1 | // -*- c-basic-offset: 2 -*- |
2 | // krazy:excludeall=doublequote_chars (UStrings aren't QStrings) |
3 | /* |
4 | * This file is part of the KDE libraries |
5 | * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) |
6 | * Copyright (C) 2007 Apple Inc. All rights reserved. |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 |
21 | * USA |
22 | * |
23 | */ |
24 | |
25 | #include "number_object.h" |
26 | #include <config-kjs.h> |
27 | #include "number_object.lut.h" |
28 | |
29 | #include "dtoa.h" |
30 | #include "error_object.h" |
31 | #include "operations.h" |
32 | #include <wtf/Assertions.h> |
33 | #include <wtf/MathExtras.h> |
34 | #include <wtf/Vector.h> |
35 | |
36 | namespace KJS { |
37 | |
38 | // GCC cstring uses these automatically, but not all implementations do. |
39 | using std::strlen; |
40 | using std::strcpy; |
41 | using std::strncpy; |
42 | using std::memset; |
43 | using std::memcpy; |
44 | |
45 | static const double MAX_SAFE_INTEGER = 9007199254740991.0; //(2^53)-1 |
46 | static const double MIN_SAFE_INTEGER = -9007199254740991.0; //-((2^53)-1) |
47 | |
48 | // ------------------------------ NumberInstance ---------------------------- |
49 | |
50 | const ClassInfo NumberInstance::info = {"Number" , 0, 0, 0}; |
51 | |
52 | NumberInstance::NumberInstance(JSObject* proto) |
53 | : JSWrapperObject(proto) |
54 | { |
55 | } |
56 | |
57 | JSObject* NumberInstance::valueClone(Interpreter* targetCtx) const |
58 | { |
59 | NumberInstance* copy = new NumberInstance(targetCtx->builtinNumberPrototype()); |
60 | copy->setInternalValue(internalValue()); |
61 | return copy; |
62 | } |
63 | |
64 | // ------------------------------ NumberPrototype --------------------------- |
65 | |
66 | // ECMA 15.7.4 |
67 | |
68 | NumberPrototype::NumberPrototype(ExecState* exec, ObjectPrototype* objProto, FunctionPrototype* funcProto) |
69 | : NumberInstance(objProto) |
70 | { |
71 | setInternalValue(jsNumber(0)); |
72 | |
73 | // The constructor will be added later, after NumberObjectImp has been constructed |
74 | |
75 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToString, 1, exec->propertyNames().toString), DontEnum); |
76 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToLocaleString, 0, exec->propertyNames().toLocaleString), DontEnum); |
77 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ValueOf, 0, exec->propertyNames().valueOf), DontEnum); |
78 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToFixed, 1, exec->propertyNames().toFixed), DontEnum); |
79 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToExponential, 1, exec->propertyNames().toExponential), DontEnum); |
80 | putDirectFunction(new NumberProtoFunc(exec, funcProto, NumberProtoFunc::ToPrecision, 1, exec->propertyNames().toPrecision), DontEnum); |
81 | } |
82 | |
83 | |
84 | // ------------------------------ NumberProtoFunc --------------------------- |
85 | |
86 | NumberProtoFunc::NumberProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name) |
87 | : InternalFunctionImp(funcProto, name) |
88 | , id(i) |
89 | { |
90 | putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum); |
91 | } |
92 | |
93 | static UString integer_part_noexp(double d) |
94 | { |
95 | int decimalPoint; |
96 | int sign; |
97 | char* result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL); |
98 | bool resultIsInfOrNan = (decimalPoint == 9999); |
99 | size_t length = strlen(result); |
100 | |
101 | UString str = sign ? "-" : "" ; |
102 | if (resultIsInfOrNan) |
103 | str += result; |
104 | else if (decimalPoint <= 0) |
105 | str += "0" ; |
106 | else { |
107 | Vector<char, 1024> buf(decimalPoint + 1); |
108 | |
109 | if (static_cast<int>(length) <= decimalPoint) { |
110 | strcpy(buf.data(), result); |
111 | memset(buf.data() + length, '0', decimalPoint - length); |
112 | } else |
113 | strncpy(buf.data(), result, decimalPoint); |
114 | |
115 | buf[decimalPoint] = '\0'; |
116 | str += UString(buf.data()); |
117 | } |
118 | |
119 | kjs_freedtoa(result); |
120 | |
121 | return str; |
122 | } |
123 | |
124 | static UString char_sequence(char c, int count) |
125 | { |
126 | Vector<char, 2048> buf(count + 1, c); |
127 | buf[count] = '\0'; |
128 | |
129 | return UString(buf.data()); |
130 | } |
131 | |
132 | static double intPow10(int e) |
133 | { |
134 | // This function uses the "exponentiation by squaring" algorithm and |
135 | // long double to quickly and precisely calculate integer powers of 10.0. |
136 | |
137 | // This is a handy workaround for <rdar://problem/4494756> |
138 | |
139 | if (e == 0) |
140 | return 1.0; |
141 | |
142 | bool negative = e < 0; |
143 | unsigned exp = negative ? -e : e; |
144 | |
145 | long double result = 10.0; |
146 | bool foundOne = false; |
147 | for (int bit = 31; bit >= 0; bit--) { |
148 | if (!foundOne) { |
149 | if ((exp >> bit) & 1) |
150 | foundOne = true; |
151 | } else { |
152 | result = result * result; |
153 | if ((exp >> bit) & 1) |
154 | result = result * 10.0; |
155 | } |
156 | } |
157 | |
158 | if (negative) |
159 | return static_cast<double>(1.0 / result); |
160 | return static_cast<double>(result); |
161 | } |
162 | |
163 | static JSValue* numberToString(ExecState* exec, JSValue* v, const List& args) |
164 | { |
165 | double radixAsDouble = args[0]->toInteger(exec); // nan -> 0 |
166 | if (radixAsDouble == 10 || args[0]->isUndefined()) |
167 | return jsString(v->toString(exec)); |
168 | |
169 | if (radixAsDouble < 2 || radixAsDouble > 36) |
170 | return throwError(exec, RangeError, "toString() radix argument must be between 2 and 36" ); |
171 | |
172 | int radix = static_cast<int>(radixAsDouble); |
173 | const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz" ; |
174 | // INT_MAX results in 1024 characters left of the dot with radix 2 |
175 | // give the same space on the right side. safety checks are in place |
176 | // unless someone finds a precise rule. |
177 | char s[2048 + 3]; |
178 | const char* lastCharInString = s + sizeof(s) - 1; |
179 | double x = v->toNumber(exec); |
180 | if (isNaN(x) || isInf(x)) |
181 | return jsString(UString::from(x)); |
182 | |
183 | bool isNegative = x < 0.0; |
184 | if (isNegative) |
185 | x = -x; |
186 | |
187 | double integerPart = floor(x); |
188 | char* decimalPoint = s + sizeof(s) / 2; |
189 | |
190 | // convert integer portion |
191 | char* p = decimalPoint; |
192 | double d = integerPart; |
193 | do { |
194 | int remainderDigit = static_cast<int>(fmod(d, radix)); |
195 | *--p = digits[remainderDigit]; |
196 | d /= radix; |
197 | } while ((d <= -1.0 || d >= 1.0) && s < p); |
198 | |
199 | if (isNegative) |
200 | *--p = '-'; |
201 | char* startOfResultString = p; |
202 | ASSERT(s <= startOfResultString); |
203 | |
204 | d = x - integerPart; |
205 | p = decimalPoint; |
206 | const double epsilon = 0.001; // TODO: guessed. base on radix ? |
207 | bool hasFractionalPart = (d < -epsilon || d > epsilon); |
208 | if (hasFractionalPart) { |
209 | *p++ = '.'; |
210 | do { |
211 | d *= radix; |
212 | const int digit = static_cast<int>(d); |
213 | *p++ = digits[digit]; |
214 | d -= digit; |
215 | } while ((d < -epsilon || d > epsilon) && p < lastCharInString); |
216 | } |
217 | *p = '\0'; |
218 | ASSERT(p < s + sizeof(s)); |
219 | |
220 | return jsString(startOfResultString); |
221 | } |
222 | |
223 | static JSValue* numberToFixed(ExecState* exec, JSValue* v, const List& args) |
224 | { |
225 | JSValue* fractionDigits = args[0]; |
226 | double df = fractionDigits->toInteger(exec); |
227 | if (!(df >= 0 && df <= 20)) |
228 | return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20" ); |
229 | int f = (int)df; |
230 | |
231 | double x = v->toNumber(exec); |
232 | if (isNaN(x)) |
233 | return jsString("NaN" ); |
234 | |
235 | UString s; |
236 | if (x < 0) { |
237 | s.append('-'); |
238 | x = -x; |
239 | } else if (x == -0.0) |
240 | x = 0; |
241 | |
242 | if (x >= pow(10.0, 21.0)) |
243 | return jsString(s + UString::from(x)); |
244 | |
245 | const double tenToTheF = pow(10.0, f); |
246 | double n = floor(x * tenToTheF); |
247 | if (fabs(n / tenToTheF - x) >= fabs((n + 1) / tenToTheF - x)) |
248 | n++; |
249 | |
250 | UString m = integer_part_noexp(n); |
251 | |
252 | int k = m.size(); |
253 | if (k <= f) { |
254 | UString z; |
255 | for (int i = 0; i < f + 1 - k; i++) |
256 | z.append('0'); |
257 | m = z + m; |
258 | k = f + 1; |
259 | ASSERT(k == m.size()); |
260 | } |
261 | int kMinusf = k - f; |
262 | if (kMinusf < m.size()) |
263 | return jsString(s + m.substr(0, kMinusf) + "." + m.substr(kMinusf)); |
264 | return jsString(s + m.substr(0, kMinusf)); |
265 | } |
266 | |
267 | void fractionalPartToString(char* buf, int& i, const char* result, int resultLength, int fractionalDigits) |
268 | { |
269 | if (fractionalDigits <= 0) |
270 | return; |
271 | |
272 | int fDigitsInResult = static_cast<int>(resultLength) - 1; |
273 | buf[i++] = '.'; |
274 | if (fDigitsInResult > 0) { |
275 | if (fractionalDigits < fDigitsInResult) { |
276 | strncpy(buf + i, result + 1, fractionalDigits); |
277 | i += fractionalDigits; |
278 | } else { |
279 | strcpy(buf + i, result + 1); |
280 | i += static_cast<int>(resultLength) - 1; |
281 | } |
282 | } |
283 | |
284 | for (int j = 0; j < fractionalDigits - fDigitsInResult; j++) |
285 | buf[i++] = '0'; |
286 | } |
287 | |
288 | void exponentialPartToString(char* buf, int& i, int decimalPoint) |
289 | { |
290 | buf[i++] = 'e'; |
291 | buf[i++] = (decimalPoint >= 0) ? '+' : '-'; |
292 | // decimalPoint can't be more than 3 digits decimal given the |
293 | // nature of float representation |
294 | int exponential = decimalPoint - 1; |
295 | if (exponential < 0) |
296 | exponential *= -1; |
297 | if (exponential >= 100) |
298 | buf[i++] = static_cast<char>('0' + exponential / 100); |
299 | if (exponential >= 10) |
300 | buf[i++] = static_cast<char>('0' + (exponential % 100) / 10); |
301 | buf[i++] = static_cast<char>('0' + exponential % 10); |
302 | } |
303 | |
304 | static JSValue* numberToExponential(ExecState* exec, JSValue* v, const List& args) |
305 | { |
306 | double x = v->toNumber(exec); |
307 | |
308 | if (isNaN(x) || isInf(x)) |
309 | return jsString(UString::from(x)); |
310 | |
311 | JSValue* fractionalDigitsValue = args[0]; |
312 | double df = fractionalDigitsValue->toInteger(exec); |
313 | if (!(df >= 0 && df <= 20)) |
314 | return throwError(exec, RangeError, "toExponential() argument must between 0 and 20" ); |
315 | int fractionalDigits = (int)df; |
316 | bool includeAllDigits = fractionalDigitsValue->isUndefined(); |
317 | |
318 | int decimalAdjust = 0; |
319 | if (x && !includeAllDigits) { |
320 | double logx = floor(log10(fabs(x))); |
321 | x /= pow(10.0, logx); |
322 | const double tenToTheF = pow(10.0, fractionalDigits); |
323 | double fx = floor(x * tenToTheF) / tenToTheF; |
324 | double cx = ceil(x * tenToTheF) / tenToTheF; |
325 | |
326 | if (fabs(fx - x) < fabs(cx - x)) |
327 | x = fx; |
328 | else |
329 | x = cx; |
330 | |
331 | decimalAdjust = static_cast<int>(logx); |
332 | } |
333 | |
334 | if (isNaN(x)) |
335 | return jsString("NaN" ); |
336 | |
337 | if (x == -0.0) // (-0.0).toExponential() should print as 0 instead of -0 |
338 | x = 0; |
339 | |
340 | int decimalPoint; |
341 | int sign; |
342 | char* result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL); |
343 | size_t resultLength = strlen(result); |
344 | decimalPoint += decimalAdjust; |
345 | |
346 | int i = 0; |
347 | char buf[80]; // digit + '.' + fractionDigits (max 20) + 'e' + sign + exponent (max?) |
348 | if (sign) |
349 | buf[i++] = '-'; |
350 | |
351 | if (decimalPoint == 999) // ? 9999 is the magical "result is Inf or NaN" value. what's 999?? |
352 | strcpy(buf + i, result); |
353 | else { |
354 | buf[i++] = result[0]; |
355 | |
356 | if (includeAllDigits) |
357 | fractionalDigits = static_cast<int>(resultLength) - 1; |
358 | |
359 | fractionalPartToString(buf, i, result, resultLength, fractionalDigits); |
360 | exponentialPartToString(buf, i, decimalPoint); |
361 | buf[i++] = '\0'; |
362 | } |
363 | ASSERT(i <= 80); |
364 | |
365 | kjs_freedtoa(result); |
366 | |
367 | return jsString(buf); |
368 | } |
369 | |
370 | static JSValue* numberToPrecision(ExecState* exec, JSValue* v, const List& args) |
371 | { |
372 | double doublePrecision = args[0]->toIntegerPreserveNaN(exec); |
373 | double x = v->toNumber(exec); |
374 | if (args[0]->isUndefined() || isNaN(x) || isInf(x)) |
375 | return jsString(v->toString(exec)); |
376 | |
377 | UString s; |
378 | if (x < 0) { |
379 | s = "-" ; |
380 | x = -x; |
381 | } |
382 | |
383 | if (!(doublePrecision >= 1 && doublePrecision <= 21)) // true for NaN |
384 | return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21" ); |
385 | int precision = (int)doublePrecision; |
386 | |
387 | int e = 0; |
388 | UString m; |
389 | if (x) { |
390 | e = static_cast<int>(log10(x)); |
391 | double tens = intPow10(e - precision + 1); |
392 | double n = floor(x / tens); |
393 | if (n < intPow10(precision - 1)) { |
394 | e = e - 1; |
395 | tens = intPow10(e - precision + 1); |
396 | n = floor(x / tens); |
397 | } |
398 | |
399 | if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x)) |
400 | ++n; |
401 | // maintain n < 10^(precision) |
402 | if (n >= intPow10(precision)) { |
403 | n /= 10.0; |
404 | e += 1; |
405 | } |
406 | ASSERT(intPow10(precision - 1) <= n); |
407 | ASSERT(n < intPow10(precision)); |
408 | |
409 | m = integer_part_noexp(n); |
410 | if (e < -6 || e >= precision) { |
411 | if (m.size() > 1) |
412 | m = m.substr(0, 1) + "." + m.substr(1); |
413 | if (e >= 0) |
414 | return jsString(s + m + "e+" + UString::from(e)); |
415 | return jsString(s + m + "e-" + UString::from(-e)); |
416 | } |
417 | } else { |
418 | m = char_sequence('0', precision); |
419 | e = 0; |
420 | } |
421 | |
422 | if (e == precision - 1) |
423 | return jsString(s + m); |
424 | else if (e >= 0) { |
425 | if (e + 1 < m.size()) |
426 | return jsString(s + m.substr(0, e + 1) + "." + m.substr(e + 1)); |
427 | return jsString(s + m); |
428 | } |
429 | return jsString(s + "0." + char_sequence('0', -(e + 1)) + m); |
430 | } |
431 | |
432 | // ECMA 15.7.4.2 - 15.7.4.7 |
433 | JSValue* NumberProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args) |
434 | { |
435 | // no generic function. "this" has to be a Number object |
436 | if (!thisObj->inherits(&NumberInstance::info)) |
437 | return throwError(exec, TypeError); |
438 | |
439 | JSValue* v = static_cast<NumberInstance*>(thisObj)->internalValue(); |
440 | switch (id) { |
441 | case ToString: |
442 | return numberToString(exec, v, args); |
443 | case ToLocaleString: /* TODO */ |
444 | return jsString(v->toString(exec)); |
445 | case ValueOf: |
446 | return jsNumber(v->toNumber(exec)); |
447 | case ToFixed: |
448 | return numberToFixed(exec, v, args); |
449 | case ToExponential: |
450 | return numberToExponential(exec, v, args); |
451 | case ToPrecision: |
452 | return numberToPrecision(exec, v, args); |
453 | } |
454 | return 0; |
455 | } |
456 | |
457 | // ------------------------------ NumberObjectImp ------------------------------ |
458 | |
459 | const ClassInfo NumberObjectImp::info = {"Function" , &InternalFunctionImp::info, &numberTable, 0}; |
460 | |
461 | /* Source for number_object.lut.h |
462 | @begin numberTable 5 |
463 | NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly |
464 | NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly |
465 | POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly |
466 | MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly |
467 | MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly |
468 | |
469 | MAX_SAFE_INTEGER NumberObjectImp::MaxSafeInteger DontEnum|DontDelete|ReadOnly |
470 | MIN_SAFE_INTEGER NumberObjectImp::MinSafeInteger DontEnum|DontDelete|ReadOnly |
471 | isFinite NumberObjectImp::IsFinite DontEnum|Function 1 |
472 | isInteger NumberObjectImp::IsInteger DontEnum|Function 1 |
473 | isNaN NumberObjectImp::IsNaN DontEnum|Function 1 |
474 | isSafeInteger NumberObjectImp::IsSafeInteger DontEnum|Function 1 |
475 | parseInt NumberObjectImp::ParseInt DontEnum|Function 2 |
476 | parseFloat NumberObjectImp::ParseFloat DontEnum|Function 1 |
477 | @end |
478 | */ |
479 | NumberObjectImp::NumberObjectImp(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto) |
480 | : InternalFunctionImp(funcProto) |
481 | { |
482 | // Number.Prototype |
483 | putDirect(exec->propertyNames().prototype, numberProto, DontEnum|DontDelete|ReadOnly); |
484 | |
485 | // no. of arguments for constructor |
486 | putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum); |
487 | } |
488 | |
489 | bool NumberObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) |
490 | { |
491 | return getStaticPropertySlot<NumberFuncImp, NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot); |
492 | } |
493 | |
494 | JSValue* NumberObjectImp::getValueProperty(ExecState*, int token) const |
495 | { |
496 | // ECMA 15.7.3 |
497 | switch (token) { |
498 | case NaNValue: |
499 | return jsNaN(); |
500 | case NegInfinity: |
501 | return jsNumberCell(-Inf); |
502 | case PosInfinity: |
503 | return jsNumberCell(Inf); |
504 | case MaxValue: |
505 | return jsNumberCell(1.7976931348623157E+308); |
506 | case MinValue: |
507 | return jsNumberCell(5E-324); |
508 | case MaxSafeInteger: |
509 | return jsNumber(MAX_SAFE_INTEGER); |
510 | case MinSafeInteger: |
511 | return jsNumber(MIN_SAFE_INTEGER); |
512 | } |
513 | return jsNull(); |
514 | } |
515 | |
516 | bool NumberObjectImp::implementsConstruct() const |
517 | { |
518 | return true; |
519 | } |
520 | |
521 | // ECMA 15.7.1 |
522 | JSObject* NumberObjectImp::construct(ExecState* exec, const List& args) |
523 | { |
524 | JSObject* proto = exec->lexicalInterpreter()->builtinNumberPrototype(); |
525 | NumberInstance* obj = new NumberInstance(proto); |
526 | |
527 | double n = args.isEmpty() ? 0 : args[0]->toNumber(exec); |
528 | obj->setInternalValue(jsNumber(n)); |
529 | return obj; |
530 | } |
531 | |
532 | // ECMA 15.7.2 |
533 | JSValue* NumberObjectImp::callAsFunction(ExecState* exec, JSObject*, const List& args) |
534 | { |
535 | double n = args.isEmpty() ? 0 : args[0]->toNumber(exec); |
536 | return jsNumber(n); |
537 | } |
538 | |
539 | NumberFuncImp::NumberFuncImp(ExecState* exec, int i, int l, const Identifier& name) |
540 | : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) |
541 | , id(i) |
542 | { |
543 | putDirect(exec->propertyNames().length, l, DontDelete|ReadOnly|DontEnum); |
544 | } |
545 | |
546 | JSValue* NumberFuncImp::callAsFunction(ExecState* exec, JSObject* /*thisObj*/, const List& args) |
547 | { |
548 | double arg = args[0]->toNumber(exec); |
549 | |
550 | switch (id) { |
551 | case NumberObjectImp::IsFinite: |
552 | if (args[0]->type() != NumberType) |
553 | return jsBoolean(false); |
554 | return jsBoolean(!isNaN(arg) && !isInf(arg)); |
555 | |
556 | case NumberObjectImp::IsInteger: |
557 | { |
558 | if (args[0]->type() != NumberType) |
559 | return jsBoolean(false); |
560 | if (isNaN(arg) || isInf(arg)) |
561 | return jsBoolean(false); |
562 | double num = args[0]->toInteger(exec); |
563 | return jsBoolean(num == arg); |
564 | } |
565 | case NumberObjectImp::IsNaN: |
566 | if (args[0]->type() != NumberType) |
567 | return jsBoolean(false); |
568 | return jsBoolean(isNaN(arg)); |
569 | |
570 | case NumberObjectImp::IsSafeInteger: |
571 | { |
572 | if (args[0]->type() != NumberType) |
573 | return jsBoolean(false); |
574 | if (isNaN(arg) || isInf(arg)) |
575 | return jsBoolean(false); |
576 | double num = args[0]->toInteger(exec); |
577 | if (num != arg) |
578 | return jsBoolean(false); |
579 | return jsBoolean(abs(num) < MAX_SAFE_INTEGER); |
580 | } |
581 | case NumberObjectImp::ParseInt: |
582 | return jsNumber(KJS::parseInt(args[0]->toString(exec), args[1]->toInt32(exec))); |
583 | case NumberObjectImp::ParseFloat: |
584 | return jsNumber(KJS::parseFloat(args[0]->toString(exec))); |
585 | |
586 | } |
587 | return jsUndefined(); |
588 | } |
589 | |
590 | |
591 | } // namespace KJS |
592 | |