1
// -*- c-basic-offset: 2 -*-
3
* Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org)
4
* Copyright (C) 2007 Apple Inc. All rights reserved.
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.
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.
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 USA
23
#include "number_object.h"
24
#include "number_object.lut.h"
27
#include "error_object.h"
28
#include "operations.h"
29
#include <wtf/MathExtras.h>
30
#include <wtf/Vector.h>
34
// ------------------------------ NumberInstance ----------------------------
36
const ClassInfo NumberInstance::info = {"Number", 0, 0, 0};
38
NumberInstance::NumberInstance(JSObject *proto)
39
: JSWrapperObject(proto)
42
// ------------------------------ NumberPrototype ---------------------------
46
NumberPrototype::NumberPrototype(ExecState *exec,
47
ObjectPrototype *objProto,
48
FunctionPrototype *funcProto)
49
: NumberInstance(objProto)
51
setInternalValue(jsNumber(0));
53
// The constructor will be added later, after NumberObjectImp has been constructed
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);
64
// ------------------------------ NumberProtoFunc ---------------------------
66
NumberProtoFunc::NumberProtoFunc(ExecState* exec, FunctionPrototype* funcProto, int i, int len, const Identifier& name)
67
: InternalFunctionImp(funcProto, name)
70
putDirect(exec->propertyNames().length, len, DontDelete|ReadOnly|DontEnum);
73
static UString integer_part_noexp(double d)
77
char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL);
78
size_t length = strlen(result);
80
UString str = sign ? "-" : "";
81
if (decimalPoint == 9999) {
82
str += UString(result);
83
} else if (decimalPoint <= 0) {
86
Vector<char, 1024> buf(decimalPoint + 1);
88
if (static_cast<int>(length) <= decimalPoint) {
89
strcpy(buf.data(), result);
90
memset(buf.data() + length, '0', decimalPoint - length);
92
strncpy(buf.data(), result, decimalPoint);
94
buf[decimalPoint] = '\0';
95
str += UString(buf.data());
103
static UString char_sequence(char c, int count)
105
Vector<char, 2048> buf(count + 1, c);
108
return UString(buf.data());
111
static double intPow10(int e)
113
// This function uses the "exponentiation by squaring" algorithm and
114
// long double to quickly and precisely calculate integer powers of 10.0.
116
// This is a handy workaround for <rdar://problem/4494756>
121
bool negative = e < 0;
122
unsigned exp = negative ? -e : e;
124
long double result = 10.0;
125
bool foundOne = false;
126
for (int bit = 31; bit >= 0; bit--) {
128
if ((exp >> bit) & 1)
131
result = result * result;
132
if ((exp >> bit) & 1)
133
result = result * 10.0;
138
return static_cast<double>(1.0 / result);
139
return static_cast<double>(result);
142
// ECMA 15.7.4.2 - 15.7.4.7
143
JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
145
// no generic function. "this" has to be a Number object
146
if (!thisObj->inherits(&NumberInstance::info))
147
return throwError(exec, TypeError);
149
JSValue *v = static_cast<NumberInstance*>(thisObj)->internalValue();
154
dradix = args[0]->toInteger(exec);
155
if (dradix >= 2 && dradix <= 36 && dradix != 10) { // false for NaN
156
int radix = static_cast<int>(dradix);
157
const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
158
// INT_MAX results in 1024 characters left of the dot with radix 2
159
// give the same space on the right side. safety checks are in place
160
// unless someone finds a precise rule.
162
double x = v->toNumber(exec);
163
if (isNaN(x) || isInf(x))
164
return jsString(UString::from(x));
166
// apply algorithm on absolute value. add sign later.
172
// convert integer portion
175
char *dot = s + sizeof(s) / 2;
179
*--p = digits[static_cast<int>(fmod(d, radix))];
181
} while ((d <= -1.0 || d >= 1.0) && p > s);
182
// any decimal fraction ?
184
const double eps = 0.001; // TODO: guessed. base on radix ?
185
if (d < -eps || d > eps) {
189
*dot++ = digits[static_cast<int>(d)];
190
d -= static_cast<int>(d);
191
} while ((d < -eps || d > eps) && dot - s < static_cast<int>(sizeof(s)) - 1);
194
// add sign if negative
199
return jsString(v->toString(exec));
201
case ToLocaleString: /* TODO */
202
return jsString(v->toString(exec));
204
return jsNumber(v->toNumber(exec));
207
JSValue *fractionDigits = args[0];
208
double df = fractionDigits->toInteger(exec);
209
if (fractionDigits->isUndefined())
211
if (!(df >= 0 && df <= 20)) // true for NaN
212
return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
215
double x = v->toNumber(exec);
217
return jsString("NaN");
225
if (x >= pow(10.0, 21.0))
226
return jsString(s+UString::from(x));
228
double n = floor(x*pow(10.0, f));
229
if (fabs(n / pow(10.0, f) - x) >= fabs((n + 1) / pow(10.0, f) - x))
232
UString m = integer_part_noexp(n);
237
for (int i = 0; i < f+1-k; i++)
241
assert(k == m.size());
244
return jsString(s+m.substr(0,k-f)+"."+m.substr(k-f));
246
return jsString(s+m.substr(0,k-f));
248
case ToExponential: {
249
double x = v->toNumber(exec);
251
if (isNaN(x) || isInf(x))
252
return jsString(UString::from(x));
254
JSValue *fractionDigits = args[0];
255
double df = fractionDigits->toInteger(exec);
256
if (!fractionDigits->isUndefined() && !(df >= 0 && df <= 20)) // true for NaN
257
return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
260
int decimalAdjust = 0;
261
if (!fractionDigits->isUndefined()) {
262
double logx = floor(log10(fabs(x)));
263
x /= pow(10.0, logx);
264
double fx = floor(x * pow(10.0, f)) / pow(10.0, f);
265
double cx = ceil(x * pow(10.0, f)) / pow(10.0, f);
267
if (fabs(fx-x) < fabs(cx-x))
272
decimalAdjust = static_cast<int>(logx);
280
return jsString("NaN");
282
char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
283
size_t length = strlen(result);
284
decimalPoint += decimalAdjust;
291
if (decimalPoint == 999) {
292
strcpy(buf + i, result);
294
buf[i++] = result[0];
296
if (fractionDigits->isUndefined())
297
f = static_cast<int>(length) - 1;
299
if (length > 1 && f > 0) {
301
int haveFDigits = static_cast<int>(length) - 1;
302
if (f < haveFDigits) {
303
strncpy(buf+i,result+1, f);
307
strcpy(buf+i,result+1);
308
i += static_cast<int>(length) - 1;
309
for (int j = 0; j < f-haveFDigits; j++)
315
buf[i++] = (decimalPoint >= 0) ? '+' : '-';
316
// decimalPoint can't be more than 3 digits decimal given the
317
// nature of float representation
318
int exponential = decimalPoint - 1;
319
if (exponential < 0) {
320
exponential = exponential * -1;
322
if (exponential >= 100) {
323
buf[i++] = static_cast<char>('0' + exponential / 100);
325
if (exponential >= 10) {
326
buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
328
buf[i++] = static_cast<char>('0' + exponential % 10);
334
kjs_freedtoa(result);
336
return jsString(buf);
343
double dp = args[0]->toInteger(exec);
344
double x = v->toNumber(exec);
345
if (isNaN(dp) || isNaN(x) || isInf(x))
346
return jsString(v->toString(exec));
354
if (!(dp >= 1 && dp <= 21)) // true for NaN
355
return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");
359
e = static_cast<int>(log10(x));
360
double tens = intPow10(e - p + 1);
361
double n = floor(x / tens);
362
if (n < intPow10(p - 1)) {
364
tens = intPow10(e - p + 1);
368
if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x))
370
assert(intPow10(p - 1) <= n);
371
assert(n < intPow10(p));
373
m = integer_part_noexp(n);
374
if (e < -6 || e >= p) {
376
m = m.substr(0,1)+"."+m.substr(1);
378
return jsString(s+m+"e+"+UString::from(e));
380
return jsString(s+m+"e-"+UString::from(-e));
384
m = char_sequence('0',p);
389
return jsString(s+m);
393
return jsString(s+m.substr(0,e+1)+"."+m.substr(e+1));
395
return jsString(s+m.substr(0,e+1));
398
return jsString(s+"0."+char_sequence('0',-(e+1))+m);
406
// ------------------------------ NumberObjectImp ------------------------------
408
const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
410
/* Source for number_object.lut.h
412
NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly
413
NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly
414
POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly
415
MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly
416
MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly
419
NumberObjectImp::NumberObjectImp(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto)
420
: InternalFunctionImp(funcProto)
423
putDirect(exec->propertyNames().prototype, numberProto,DontEnum|DontDelete|ReadOnly);
425
// no. of arguments for constructor
426
putDirect(exec->propertyNames().length, jsNumber(1), ReadOnly|DontDelete|DontEnum);
429
bool NumberObjectImp::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
431
return getStaticValueSlot<NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot);
434
JSValue *NumberObjectImp::getValueProperty(ExecState *, int token) const
441
return jsNumber(-Inf);
443
return jsNumber(Inf);
445
return jsNumber(1.7976931348623157E+308);
447
return jsNumber(5E-324);
452
bool NumberObjectImp::implementsConstruct() const
459
JSObject *NumberObjectImp::construct(ExecState *exec, const List &args)
461
JSObject *proto = exec->lexicalInterpreter()->builtinNumberPrototype();
462
NumberInstance *obj(new NumberInstance(proto));
468
n = args[0]->toNumber(exec);
470
obj->setInternalValue(jsNumber(n));
476
JSValue *NumberObjectImp::callAsFunction(ExecState *exec, JSObject* /*thisObj*/, const List &args)
481
return jsNumber(args[0]->toNumber(exec));