1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org)
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 Steet, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "interpreter.h"
27 #include "operations.h"
28 #include "number_object.h"
29 #include "error_object.h"
32 #include "number_object.lut.h"
39 // ------------------------------ NumberInstance ----------------------------
41 const ClassInfo NumberInstance::info = {"Number", 0, 0, 0};
43 NumberInstance::NumberInstance(JSObject *proto)
47 // ------------------------------ NumberPrototype ---------------------------
51 NumberPrototype::NumberPrototype(ExecState *exec,
52 ObjectPrototype *objProto,
53 FunctionPrototype *funcProto)
54 : NumberInstance(objProto)
56 setInternalValue(jsNumber(0));
58 // The constructor will be added later, after NumberObjectImp has been constructed
60 putDirect(toStringPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ToString, 1), DontEnum);
61 putDirect(toLocaleStringPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ToLocaleString, 0), DontEnum);
62 putDirect(valueOfPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ValueOf, 0), DontEnum);
63 putDirect(toFixedPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ToFixed, 1), DontEnum);
64 putDirect(toExponentialPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ToExponential, 1), DontEnum);
65 putDirect(toPrecisionPropertyName, new NumberProtoFunc(exec,funcProto,NumberProtoFunc::ToPrecision, 1), DontEnum);
69 // ------------------------------ NumberProtoFunc ---------------------------
71 NumberProtoFunc::NumberProtoFunc(ExecState *exec,
72 FunctionPrototype *funcProto, int i, int len)
73 : InternalFunctionImp(funcProto), id(i)
75 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
79 bool NumberProtoFunc::implementsCall() const
84 static UString integer_part_noexp(double d)
88 char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL);
89 int length = strlen(result);
91 UString str = sign ? "-" : "";
92 if (decimalPoint == 9999) {
93 str += UString(result);
94 } else if (decimalPoint <= 0) {
99 if (length <= decimalPoint) {
100 buf = (char*)fastMalloc(decimalPoint+1);
102 memset(buf+length,'0',decimalPoint-length);
104 buf = (char*)fastMalloc(decimalPoint+1);
105 strncpy(buf,result,decimalPoint);
108 buf[decimalPoint] = '\0';
113 kjs_freedtoa(result);
118 static UString char_sequence(char c, int count)
120 char *buf = (char*)fastMalloc(count+1);
128 // ECMA 15.7.4.2 - 15.7.4.7
129 JSValue *NumberProtoFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
131 // no generic function. "this" has to be a Number object
132 if (!thisObj->inherits(&NumberInstance::info))
133 return throwError(exec, TypeError);
135 JSValue *v = thisObj->internalValue();
140 dradix = args[0]->toInteger(exec);
141 if (dradix >= 2 && dradix <= 36 && dradix != 10) { // false for NaN
142 int radix = static_cast<int>(dradix);
143 const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
144 // INT_MAX results in 1024 characters left of the dot with radix 2
145 // give the same space on the right side. safety checks are in place
146 // unless someone finds a precise rule.
148 double x = v->toNumber(exec);
149 if (isNaN(x) || isInf(x))
150 return jsString(UString::from(x));
152 // apply algorithm on absolute value. add sign later.
158 // convert integer portion
161 char *dot = s + sizeof(s) / 2;
165 *--p = digits[static_cast<int>(fmod(d, radix))];
167 } while ((d <= -1.0 || d >= 1.0) && p > s);
168 // any decimal fraction ?
170 const double eps = 0.001; // TODO: guessed. base on radix ?
171 if (d < -eps || d > eps) {
175 *dot++ = digits[static_cast<int>(d)];
176 d -= static_cast<int>(d);
177 } while ((d < -eps || d > eps) && dot - s < static_cast<int>(sizeof(s)) - 1);
180 // add sign if negative
185 return jsString(v->toString(exec));
187 case ToLocaleString: /* TODO */
188 return jsString(v->toString(exec));
190 return jsNumber(v->toNumber(exec));
193 JSValue *fractionDigits = args[0];
194 double df = fractionDigits->toInteger(exec);
195 if (fractionDigits->isUndefined())
197 if (!(df >= 0 && df <= 20)) // true for NaN
198 return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");
201 double x = v->toNumber(exec);
203 return jsString("NaN");
211 if (x >= pow(10.0, 21.0))
212 return jsString(s+UString::from(x));
214 double n = floor(x*pow(10.0, f));
215 if (fabs(n / pow(10.0, f) - x) >= fabs((n + 1) / pow(10.0, f) - x))
218 UString m = integer_part_noexp(n);
223 for (int i = 0; i < f+1-k; i++)
227 assert(k == m.size());
230 return jsString(s+m.substr(0,k-f)+"."+m.substr(k-f));
232 return jsString(s+m.substr(0,k-f));
234 case ToExponential: {
235 double x = v->toNumber(exec);
237 if (isNaN(x) || isInf(x))
238 return jsString(UString::from(x));
240 JSValue *fractionDigits = args[0];
241 double df = fractionDigits->toInteger(exec);
242 if (!fractionDigits->isUndefined() && !(df >= 0 && df <= 20)) // true for NaN
243 return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");
246 int decimalAdjust = 0;
247 if (!fractionDigits->isUndefined()) {
248 double logx = floor(log10(x));
249 x /= pow(10.0, logx);
250 double fx = floor(x * pow(10.0, f)) / pow(10.0,f);
251 double cx = ceil(x * pow(10.0, f)) / pow(10.0, f);
253 if (fabs(fx-x) < fabs(cx-x))
258 decimalAdjust = static_cast<int>(logx);
266 return jsString("NaN");
268 char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
269 int length = strlen(result);
270 decimalPoint += decimalAdjust;
277 if (decimalPoint == 999) {
278 strcpy(buf + i, result);
280 buf[i++] = result[0];
282 if (fractionDigits->isUndefined())
285 if (length > 1 && f > 0) {
287 int haveFDigits = length-1;
288 if (f < haveFDigits) {
289 strncpy(buf+i,result+1, f);
293 strcpy(buf+i,result+1);
295 for (int j = 0; j < f-haveFDigits; j++)
301 buf[i++] = (decimalPoint >= 0) ? '+' : '-';
302 // decimalPoint can't be more than 3 digits decimal given the
303 // nature of float representation
304 int exponential = decimalPoint - 1;
305 if (exponential < 0) {
306 exponential = exponential * -1;
308 if (exponential >= 100) {
309 buf[i++] = '0' + exponential / 100;
311 if (exponential >= 10) {
312 buf[i++] = '0' + (exponential % 100) / 10;
314 buf[i++] = '0' + exponential % 10;
320 kjs_freedtoa(result);
322 return jsString(buf);
329 double dp = args[0]->toInteger(exec);
330 double x = v->toNumber(exec);
331 if (isNaN(dp) || isNaN(x) || isInf(x))
332 return jsString(v->toString(exec));
340 if (!(dp >= 1 && dp <= 21)) // true for NaN
341 return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");
345 e = static_cast<int>(log10(x));
346 double n = floor(x / pow(10.0, e - p + 1));
347 if (n < pow(10.0, p - 1)) {
349 n = floor(x / pow(10.0, e - p + 1));
352 if (fabs((n + 1) * pow(10.0, e - p + 1) - x) < fabs(n * pow(10.0, e - p + 1) - x))
354 assert(pow(10.0, p - 1) <= n);
355 assert(n < pow(10.0, p));
357 m = integer_part_noexp(n);
358 if (e < -6 || e >= p) {
360 m = m.substr(0,1)+"."+m.substr(1);
362 return jsString(s+m+"e+"+UString::from(e));
364 return jsString(s+m+"e-"+UString::from(-e));
368 m = char_sequence('0',p);
373 return jsString(s+m);
377 return jsString(s+m.substr(0,e+1)+"."+m.substr(e+1));
379 return jsString(s+m.substr(0,e+1));
382 return jsString(s+"0."+char_sequence('0',-(e+1))+m);
390 // ------------------------------ NumberObjectImp ------------------------------
392 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
394 /* Source for number_object.lut.h
396 NaN NumberObjectImp::NaNValue DontEnum|DontDelete|ReadOnly
397 NEGATIVE_INFINITY NumberObjectImp::NegInfinity DontEnum|DontDelete|ReadOnly
398 POSITIVE_INFINITY NumberObjectImp::PosInfinity DontEnum|DontDelete|ReadOnly
399 MAX_VALUE NumberObjectImp::MaxValue DontEnum|DontDelete|ReadOnly
400 MIN_VALUE NumberObjectImp::MinValue DontEnum|DontDelete|ReadOnly
403 NumberObjectImp::NumberObjectImp(ExecState *exec,
404 FunctionPrototype *funcProto,
405 NumberPrototype *numberProto)
406 : InternalFunctionImp(funcProto)
409 putDirect(prototypePropertyName, numberProto,DontEnum|DontDelete|ReadOnly);
411 // no. of arguments for constructor
412 putDirect(lengthPropertyName, jsNumber(1), ReadOnly|DontDelete|DontEnum);
415 bool NumberObjectImp::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
417 return getStaticValueSlot<NumberObjectImp, InternalFunctionImp>(exec, &numberTable, this, propertyName, slot);
420 JSValue *NumberObjectImp::getValueProperty(ExecState *, int token) const
427 return jsNumber(-Inf);
429 return jsNumber(Inf);
431 return jsNumber(1.7976931348623157E+308);
433 return jsNumber(5E-324);
438 bool NumberObjectImp::implementsConstruct() const
445 JSObject *NumberObjectImp::construct(ExecState *exec, const List &args)
447 JSObject *proto = exec->lexicalInterpreter()->builtinNumberPrototype();
448 JSObject *obj(new NumberInstance(proto));
454 n = args[0]->toNumber(exec);
456 obj->setInternalValue(jsNumber(n));
461 bool NumberObjectImp::implementsCall() const
467 JSValue *NumberObjectImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, const List &args)
472 return jsNumber(args[0]->toNumber(exec));