Fix build by adding all (hopefully) the missing includes.
[WebKit-https.git] / JavaScriptCore / kjs / NumberPrototype.cpp
1 /*
2  *  Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
18  *  USA
19  *
20  */
21
22 #include "config.h"
23 #include "NumberPrototype.h"
24
25 #include "FunctionPrototype.h"
26 #include "JSFunction.h"
27 #include "ObjectPrototype.h"
28 #include "dtoa.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
37 static JSValue* numberProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&);
38 static JSValue* numberProtoFuncToLocaleString(ExecState*, JSObject*, JSValue*, const ArgList&);
39 static JSValue* numberProtoFuncValueOf(ExecState*, JSObject*, JSValue*, const ArgList&);
40 static JSValue* numberProtoFuncToFixed(ExecState*, JSObject*, JSValue*, const ArgList&);
41 static JSValue* numberProtoFuncToExponential(ExecState*, JSObject*, JSValue*, const ArgList&);
42 static JSValue* numberProtoFuncToPrecision(ExecState*, JSObject*, JSValue*, const ArgList&);
43
44 // ECMA 15.7.4
45
46 NumberPrototype::NumberPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype)
47     : NumberObject(objectPrototype)
48 {
49     setInternalValue(jsNumber(exec, 0));
50
51     // The constructor will be added later, after NumberConstructor has been constructed
52
53     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum);
54     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum);
55     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum);
56     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum);
57     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum);
58     putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum);
59 }
60
61 // ------------------------------ Functions ---------------------------
62
63 // ECMA 15.7.4.2 - 15.7.4.7
64
65 static UString integer_part_noexp(double d)
66 {
67     int decimalPoint;
68     int sign;
69     char* result = dtoa(d, 0, &decimalPoint, &sign, NULL);
70     bool resultIsInfOrNan = (decimalPoint == 9999);
71     size_t length = strlen(result);
72
73     UString str = sign ? "-" : "";
74     if (resultIsInfOrNan)
75         str += result;
76     else if (decimalPoint <= 0)
77         str += "0";
78     else {
79         Vector<char, 1024> buf(decimalPoint + 1);
80
81         if (static_cast<int>(length) <= decimalPoint) {
82             strcpy(buf.data(), result);
83             memset(buf.data() + length, '0', decimalPoint - length);
84         } else
85             strncpy(buf.data(), result, decimalPoint);
86
87         buf[decimalPoint] = '\0';
88         str.append(buf.data());
89     }
90
91     freedtoa(result);
92
93     return str;
94 }
95
96 static UString char_sequence(char c, int count)
97 {
98     Vector<char, 2048> buf(count + 1, c);
99     buf[count] = '\0';
100
101     return UString(buf.data());
102 }
103
104 static double intPow10(int e)
105 {
106     // This function uses the "exponentiation by squaring" algorithm and
107     // long double to quickly and precisely calculate integer powers of 10.0.
108
109     // This is a handy workaround for <rdar://problem/4494756>
110
111     if (e == 0)
112         return 1.0;
113
114     bool negative = e < 0;
115     unsigned exp = negative ? -e : e;
116
117     long double result = 10.0;
118     bool foundOne = false;
119     for (int bit = 31; bit >= 0; bit--) {
120         if (!foundOne) {
121             if ((exp >> bit) & 1)
122                 foundOne = true;
123         } else {
124             result = result * result;
125             if ((exp >> bit) & 1)
126                 result = result * 10.0;
127         }
128     }
129
130     if (negative)
131         return static_cast<double>(1.0 / result);
132     return static_cast<double>(result);
133 }
134
135
136 JSValue* numberProtoFuncToString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
137 {
138     JSValue* v = thisValue->getJSNumber();
139     if (!v)
140         return throwError(exec, TypeError);
141
142     double radixAsDouble = args[0]->toInteger(exec); // nan -> 0
143     if (radixAsDouble == 10 || args[0]->isUndefined())
144         return jsString(exec, v->toString(exec));
145
146     if (radixAsDouble < 2 || radixAsDouble > 36)
147         return throwError(exec, RangeError, "toString() radix argument must be between 2 and 36");
148
149     int radix = static_cast<int>(radixAsDouble);
150     const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
151     // INT_MAX results in 1024 characters left of the dot with radix 2
152     // give the same space on the right side. safety checks are in place
153     // unless someone finds a precise rule.
154     char s[2048 + 3];
155     const char* lastCharInString = s + sizeof(s) - 1;
156     double x = v->uncheckedGetNumber();
157     if (isnan(x) || isinf(x))
158         return jsString(exec, UString::from(x));
159
160     bool isNegative = x < 0.0;
161     if (isNegative)
162         x = -x;
163
164     double integerPart = floor(x);
165     char* decimalPoint = s + sizeof(s) / 2;
166
167     // convert integer portion
168     char* p = decimalPoint;
169     double d = integerPart;
170     do {
171         int remainderDigit = static_cast<int>(fmod(d, radix));
172         *--p = digits[remainderDigit];
173         d /= radix;
174     } while ((d <= -1.0 || d >= 1.0) && s < p);
175
176     if (isNegative)
177         *--p = '-';
178     char* startOfResultString = p;
179     ASSERT(s <= startOfResultString);
180
181     d = x - integerPart;
182     p = decimalPoint;
183     const double epsilon = 0.001; // TODO: guessed. base on radix ?
184     bool hasFractionalPart = (d < -epsilon || d > epsilon);
185     if (hasFractionalPart) {
186         *p++ = '.';
187         do {
188             d *= radix;
189             const int digit = static_cast<int>(d);
190             *p++ = digits[digit];
191             d -= digit;
192         } while ((d < -epsilon || d > epsilon) && p < lastCharInString);
193     }
194     *p = '\0';
195     ASSERT(p < s + sizeof(s));
196
197     return jsString(exec, startOfResultString);
198 }
199
200 JSValue* numberProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
201 {
202     // FIXME: Not implemented yet.
203
204     JSValue* v = thisValue->getJSNumber();
205     if (!v)
206         return throwError(exec, TypeError);
207
208     return jsString(exec, v->toString(exec));
209 }
210
211 JSValue* numberProtoFuncValueOf(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)
212 {
213     JSValue* v = thisValue->getJSNumber();
214     if (!v)
215         return throwError(exec, TypeError);
216
217     return v;
218 }
219
220 JSValue* numberProtoFuncToFixed(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
221 {
222     JSValue* v = thisValue->getJSNumber();
223     if (!v)
224         return throwError(exec, TypeError);
225
226     JSValue* fractionDigits = args[0];
227     double df = fractionDigits->toInteger(exec);
228     if (!(df >= 0 && df <= 20))
229         return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
230     int f = (int)df;
231
232     double x = v->uncheckedGetNumber();
233     if (isnan(x))
234         return jsString(exec, "NaN");
235
236     UString s;
237     if (x < 0) {
238         s.append('-');
239         x = -x;
240     } else if (x == -0.0)
241         x = 0;
242
243     if (x >= pow(10.0, 21.0))
244         return jsString(exec, s + UString::from(x));
245
246     const double tenToTheF = pow(10.0, f);
247     double n = floor(x * tenToTheF);
248     if (fabs(n / tenToTheF - x) >= fabs((n + 1) / tenToTheF - x))
249         n++;
250
251     UString m = integer_part_noexp(n);
252
253     int k = m.size();
254     if (k <= f) {
255         UString z;
256         for (int i = 0; i < f + 1 - k; i++)
257             z.append('0');
258         m = z + m;
259         k = f + 1;
260         ASSERT(k == m.size());
261     }
262     int kMinusf = k - f;
263     if (kMinusf < m.size())
264         return jsString(exec, s + m.substr(0, kMinusf) + "." + m.substr(kMinusf));
265     return jsString(exec, s + m.substr(0, kMinusf));
266 }
267
268 static void fractionalPartToString(char* buf, int& i, const char* result, int resultLength, int fractionalDigits)
269 {
270     if (fractionalDigits <= 0)
271         return;
272
273     int fDigitsInResult = static_cast<int>(resultLength) - 1;
274     buf[i++] = '.';
275     if (fDigitsInResult > 0) {
276         if (fractionalDigits < fDigitsInResult) {
277             strncpy(buf + i, result + 1, fractionalDigits);
278             i += fractionalDigits;
279         } else {
280             strcpy(buf + i, result + 1);
281             i += static_cast<int>(resultLength) - 1;
282         }
283     }
284
285     for (int j = 0; j < fractionalDigits - fDigitsInResult; j++)
286         buf[i++] = '0';
287 }
288
289 static void exponentialPartToString(char* buf, int& i, int decimalPoint)
290 {
291     buf[i++] = 'e';
292     buf[i++] = (decimalPoint >= 0) ? '+' : '-';
293     // decimalPoint can't be more than 3 digits decimal given the
294     // nature of float representation
295     int exponential = decimalPoint - 1;
296     if (exponential < 0)
297         exponential *= -1;
298     if (exponential >= 100)
299         buf[i++] = static_cast<char>('0' + exponential / 100);
300     if (exponential >= 10)
301         buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
302     buf[i++] = static_cast<char>('0' + exponential % 10);
303 }
304
305 JSValue* numberProtoFuncToExponential(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
306 {
307     JSValue* v = thisValue->getJSNumber();
308     if (!v)
309         return throwError(exec, TypeError);
310
311     double x = v->uncheckedGetNumber();
312
313     if (isnan(x) || isinf(x))
314         return jsString(exec, UString::from(x));
315
316     JSValue* fractionalDigitsValue = args[0];
317     double df = fractionalDigitsValue->toInteger(exec);
318     if (!(df >= 0 && df <= 20))
319         return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
320     int fractionalDigits = (int)df;
321     bool includeAllDigits = fractionalDigitsValue->isUndefined();
322
323     int decimalAdjust = 0;
324     if (x && !includeAllDigits) {
325         double logx = floor(log10(fabs(x)));
326         x /= pow(10.0, logx);
327         const double tenToTheF = pow(10.0, fractionalDigits);
328         double fx = floor(x * tenToTheF) / tenToTheF;
329         double cx = ceil(x * tenToTheF) / tenToTheF;
330
331         if (fabs(fx - x) < fabs(cx - x))
332             x = fx;
333         else
334             x = cx;
335
336         decimalAdjust = static_cast<int>(logx);
337     }
338
339     if (isnan(x))
340         return jsString(exec, "NaN");
341
342     if (x == -0.0) // (-0.0).toExponential() should print as 0 instead of -0
343         x = 0;
344
345     int decimalPoint;
346     int sign;
347     char* result = dtoa(x, 0, &decimalPoint, &sign, NULL);
348     size_t resultLength = strlen(result);
349     decimalPoint += decimalAdjust;
350
351     int i = 0;
352     char buf[80]; // digit + '.' + fractionDigits (max 20) + 'e' + sign + exponent (max?)
353     if (sign)
354         buf[i++] = '-';
355
356     if (decimalPoint == 999) // ? 9999 is the magical "result is Inf or NaN" value.  what's 999??
357         strcpy(buf + i, result);
358     else {
359         buf[i++] = result[0];
360
361         if (includeAllDigits)
362             fractionalDigits = static_cast<int>(resultLength) - 1;
363
364         fractionalPartToString(buf, i, result, resultLength, fractionalDigits);
365         exponentialPartToString(buf, i, decimalPoint);
366         buf[i++] = '\0';
367     }
368     ASSERT(i <= 80);
369
370     freedtoa(result);
371
372     return jsString(exec, buf);
373 }
374
375 JSValue* numberProtoFuncToPrecision(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)
376 {
377     JSValue* v = thisValue->getJSNumber();
378     if (!v)
379         return throwError(exec, TypeError);
380
381     double doublePrecision = args[0]->toIntegerPreserveNaN(exec);
382     double x = v->uncheckedGetNumber();
383     if (args[0]->isUndefined() || isnan(x) || isinf(x))
384         return jsString(exec, v->toString(exec));
385
386     UString s;
387     if (x < 0) {
388         s = "-";
389         x = -x;
390     }
391
392     if (!(doublePrecision >= 1 && doublePrecision <= 21)) // true for NaN
393         return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");
394     int precision = (int)doublePrecision;
395
396     int e = 0;
397     UString m;
398     if (x) {
399         e = static_cast<int>(log10(x));
400         double tens = intPow10(e - precision + 1);
401         double n = floor(x / tens);
402         if (n < intPow10(precision - 1)) {
403             e = e - 1;
404             tens = intPow10(e - precision + 1);
405             n = floor(x / tens);
406         }
407
408         if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x))
409             ++n;
410         // maintain n < 10^(precision)
411         if (n >= intPow10(precision)) {
412             n /= 10.0;
413             e += 1;
414         }
415         ASSERT(intPow10(precision - 1) <= n);
416         ASSERT(n < intPow10(precision));
417
418         m = integer_part_noexp(n);
419         if (e < -6 || e >= precision) {
420             if (m.size() > 1)
421                 m = m.substr(0, 1) + "." + m.substr(1);
422             if (e >= 0)
423                 return jsString(exec, s + m + "e+" + UString::from(e));
424             return jsString(exec, s + m + "e-" + UString::from(-e));
425         }
426     } else {
427         m = char_sequence('0', precision);
428         e = 0;
429     }
430
431     if (e == precision - 1)
432         return jsString(exec, s + m);
433     if (e >= 0) {
434         if (e + 1 < m.size())
435             return jsString(exec, s + m.substr(0, e + 1) + "." + m.substr(e + 1));
436         return jsString(exec, s + m);
437     }
438     return jsString(exec, s + "0." + char_sequence('0', -(e + 1)) + m);
439 }
440
441 } // namespace KJS