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