number_object.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /*
00003  *  This file is part of the KDE libraries
00004  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
00005  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
00006  *
00007  *  This library is free software; you can redistribute it and/or
00008  *  modify it under the terms of the GNU Lesser General Public
00009  *  License as published by the Free Software Foundation; either
00010  *  version 2 of the License, or (at your option) any later version.
00011  *
00012  *  This library is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  *  Lesser General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU Lesser General Public
00018  *  License along with this library; if not, write to the Free Software
00019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020  *
00021  */
00022 
00023 #include "value.h"
00024 #include "object.h"
00025 #include "types.h"
00026 #include "interpreter.h"
00027 #include "operations.h"
00028 #include "number_object.h"
00029 #include "error_object.h"
00030 #include "dtoa.h"
00031 
00032 #include "number_object.lut.h"
00033 
00034 #include <assert.h>
00035 #include <math.h>
00036 
00037 using namespace KJS;
00038 
00039 // ------------------------------ NumberInstanceImp ----------------------------
00040 
00041 const ClassInfo NumberInstanceImp::info = {"Number", 0, 0, 0};
00042 
00043 NumberInstanceImp::NumberInstanceImp(ObjectImp *proto)
00044   : ObjectImp(proto)
00045 {
00046 }
00047 // ------------------------------ NumberPrototypeImp ---------------------------
00048 
00049 // ECMA 15.7.4
00050 
00051 NumberPrototypeImp::NumberPrototypeImp(ExecState *exec,
00052                                        ObjectPrototypeImp *objProto,
00053                                        FunctionPrototypeImp *funcProto)
00054   : NumberInstanceImp(objProto)
00055 {
00056   Value protect(this);
00057   setInternalValue(NumberImp::zero());
00058 
00059   // The constructor will be added later, after NumberObjectImp has been constructed
00060 
00061   putDirect(toStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToString,
00062                             1,toStringPropertyName),DontEnum);
00063   putDirect(toLocaleStringPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToLocaleString,
00064                                   0,toLocaleStringPropertyName),DontEnum);
00065   putDirect(valueOfPropertyName,new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ValueOf,
00066                                0,valueOfPropertyName),DontEnum);
00067   putDirect("toFixed", new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToFixed,
00068                           1,"toFixed"),DontEnum);
00069   putDirect("toExponential",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToExponential,
00070                            1,"toExponential"),DontEnum);
00071   putDirect("toPrecision",new NumberProtoFuncImp(exec,funcProto,NumberProtoFuncImp::ToPrecision,
00072                          1,"toPrecision"),DontEnum);
00073 }
00074 
00075 
00076 // ------------------------------ NumberProtoFuncImp ---------------------------
00077 
00078 NumberProtoFuncImp::NumberProtoFuncImp(ExecState * /*exec*/, FunctionPrototypeImp *funcProto,
00079                                        int i, int len, const Identifier &_ident)
00080   : InternalFunctionImp(funcProto), id(i)
00081 {
00082   Value protect(this);
00083   putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
00084   ident = _ident;
00085 }
00086 
00087 
00088 bool NumberProtoFuncImp::implementsCall() const
00089 {
00090   return true;
00091 }
00092 
00093 static UString integer_part_noexp(double d)
00094 {
00095   int decimalPoint;
00096   int signDummy;
00097   char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &signDummy, NULL);
00098   int length = strlen(result);
00099 
00100   // sign for non-zero, negative numbers
00101   UString str = d < 0 ? "-" : "";
00102   if (decimalPoint == 9999) {
00103     str += UString(result);
00104   } else if (decimalPoint <= 0) {
00105     str += UString("0");
00106   } else {
00107     char *buf;
00108 
00109     if (length <= decimalPoint) {
00110       buf = (char*)malloc(decimalPoint+1);
00111       strcpy(buf,result);
00112       memset(buf+length,'0',decimalPoint-length);
00113     } else {
00114       buf = (char*)malloc(decimalPoint+1);
00115       strncpy(buf,result,decimalPoint);
00116     }
00117 
00118     buf[decimalPoint] = '\0';
00119     str += UString(buf);
00120     free(buf);
00121   }
00122 
00123   kjs_freedtoa(result);
00124 
00125   return str;
00126 }
00127 
00128 static UString char_sequence(char c, int count)
00129 {
00130   char *buf = (char*)malloc(count+1);
00131   memset(buf,c,count);
00132   buf[count] = '\0';
00133   UString s(buf);
00134   free(buf);
00135   return s;
00136 }
00137 
00138 // ECMA 15.7.4.2 - 15.7.4.7
00139 Value NumberProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
00140 {
00141   Value result;
00142 
00143   // no generic function. "this" has to be a Number object
00144   KJS_CHECK_THIS( NumberInstanceImp, thisObj );
00145 
00146   // execute "toString()" or "valueOf()", respectively
00147   Value v = thisObj.internalValue();
00148   switch (id) {
00149   case ToString: {
00150     int radix = 10;
00151     if (!args.isEmpty() && args[0].type() != UndefinedType)
00152       radix = args[0].toInteger(exec);
00153     if (radix < 2 || radix > 36 || radix == 10)
00154       result = String(v.toString(exec));
00155     else {
00156       const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
00157       // INT_MAX results in 1024 characters left of the dot with radix 2
00158       // give the same space on the right side. safety checks are in place
00159       // unless someone finds a precise rule.
00160       char s[2048 + 3];
00161       double x = v.toNumber(exec);
00162       if (isNaN(x) || isInf(x))
00163         return String(UString::from(x));
00164       // apply algorithm on absolute value. add sign later.
00165       bool neg = false;
00166       if (x < 0.0) {
00167         neg = true;
00168         x = -x;
00169       }
00170       // convert integer portion
00171       double f = floor(x);
00172       double d = f;
00173       char *dot = s + sizeof(s) / 2;
00174       char *p = dot;
00175       *p = '\0';
00176       do {
00177         *--p = digits[int(fmod(d, double(radix)))];
00178         d /= radix;
00179       } while ((d <= -1.0 || d >= 1.0) && p > s);
00180       // any decimal fraction ?
00181       d = x - f;
00182       const double eps = 0.001; // TODO: guessed. base on radix ?
00183       if (d < -eps || d > eps) {
00184         *dot++ = '.';
00185         do {
00186           d *= radix;
00187           *dot++ = digits[int(d)];
00188           d -= int(d);
00189         } while ((d < -eps || d > eps) && dot - s < int(sizeof(s)) - 1);
00190         *dot = '\0';
00191       }
00192       // add sign if negative
00193       if (neg)
00194         *--p = '-';
00195       result = String(p);
00196     }
00197     break;
00198   }
00199   case ToLocaleString: /* TODO */
00200     result = String(v.toString(exec));
00201     break;
00202   case ValueOf:
00203     result = Number(v.toNumber(exec));
00204     break;
00205   case ToFixed:
00206   {
00207     // FIXME: firefox works for all values, not just 0..20.  This includes
00208     // NaN, infinity, undefined, etc.  This is just a hack to pass our regression
00209     // suite.
00210     Value fractionDigits = args[0];
00211     int f = -1;
00212     double fd = fractionDigits.toNumber(exec);
00213     if (isNaN(fd)) {
00214       f = 0;
00215     } else if (!isInf(fd)) {
00216       f = int(fd);
00217     }
00218     if (f < 0 || f > 20) {
00219       Object err = Error::create(exec,RangeError);
00220       exec->setException(err);
00221       return err;
00222     }
00223 
00224     double x = v.toNumber(exec);
00225     if (isNaN(x))
00226       return String("NaN");
00227 
00228     UString s = "";
00229     if (x < 0) {
00230       s += "-";
00231       x = -x;
00232     }
00233 
00234     if (x >= 1e21)
00235       return String(s+UString::from(x));
00236 
00237     double n = floor(x*pow(10.0,f));
00238     if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-x))
00239       n++;
00240 
00241     UString m = integer_part_noexp(n);
00242 
00243     int k = m.size();
00244     if (k <= f) {
00245       UString z = "";
00246       for (int i = 0; i < f+1-k; i++)
00247     z += "0";
00248       m = z + m;
00249       k = f + 1;
00250       assert(k == m.size());
00251     }
00252     if (k-f < m.size())
00253       return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
00254     else
00255       return String(s+m.substr(0,k-f));
00256   }
00257   case ToExponential: {
00258     double x = v.toNumber(exec);
00259 
00260     if (isNaN(x) || isInf(x))
00261       return String(UString::from(x));
00262 
00263     int f = 1;
00264     Value fractionDigits = args[0];
00265     if (args.size() > 0) {
00266       f = fractionDigits.toInteger(exec);
00267       if (f < 0 || f > 20) {
00268         Object err = Error::create(exec,RangeError);
00269         exec->setException(err);
00270         return err;
00271       }
00272     }
00273 
00274     int decimalAdjust = 0;
00275     if (!fractionDigits.isA(UndefinedType)) {
00276       double logx = floor(log10(fabs(x)));
00277       x /= pow(10.0,logx);
00278       double fx = floor(x*pow(10.0,f))/pow(10.0,f);
00279       double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
00280 
00281       if (fabs(fx-x) < fabs(cx-x))
00282     x = fx;
00283       else
00284     x = cx;
00285 
00286       decimalAdjust = int(logx);
00287     }
00288 
00289     char buf[80];
00290     int decimalPoint;
00291     int sign;
00292 
00293     if (isNaN(x))
00294       return String("NaN");
00295 
00296     char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
00297     int length = strlen(result);
00298     decimalPoint += decimalAdjust;
00299 
00300     int i = 0;
00301     if (sign) {
00302       buf[i++] = '-';
00303     }
00304 
00305     if (decimalPoint == 999) {
00306       strcpy(buf + i, result);
00307     } else {
00308       buf[i++] = result[0];
00309 
00310       if (fractionDigits.isA(UndefinedType))
00311     f = length-1;
00312 
00313       if (length > 1 && f > 0) {
00314     buf[i++] = '.';
00315     int haveFDigits = length-1;
00316     if (f < haveFDigits) {
00317       strncpy(buf+i,result+1, f);
00318       i += f;
00319     }
00320     else {
00321       strcpy(buf+i,result+1);
00322       i += length-1;
00323       for (int j = 0; j < f-haveFDigits; j++)
00324         buf[i++] = '0';
00325     }
00326       }
00327 
00328       buf[i++] = 'e';
00329       buf[i++] = (decimalPoint >= 0) ? '+' : '-';
00330       // decimalPoint can't be more than 3 digits decimal given the
00331       // nature of float representation
00332       int exponential = decimalPoint - 1;
00333       if (exponential < 0) {
00334     exponential = exponential * -1;
00335       }
00336       if (exponential >= 100) {
00337     buf[i++] = '0' + exponential / 100;
00338       }
00339       if (exponential >= 10) {
00340     buf[i++] = '0' + (exponential % 100) / 10;
00341       }
00342       buf[i++] = '0' + exponential % 10;
00343       buf[i++] = '\0';
00344     }
00345 
00346     assert(i <= 80);
00347 
00348     kjs_freedtoa(result);
00349 
00350     return String(UString(buf));
00351   }
00352   case ToPrecision:
00353   {
00354     int e = 0;
00355     UString m;
00356 
00357     int p = args[0].toInteger(exec);
00358     double x = v.toNumber(exec);
00359     if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
00360       return String(v.toString(exec));
00361 
00362     UString s = "";
00363     if (x < 0) {
00364       s = "-";
00365       x = -x;
00366     }
00367 
00368     if (p < 1 || p > 21) {
00369       Object err = Error::create(exec, RangeError,
00370                  "toPrecision() argument must be between 1 and 21");
00371       exec->setException(err);
00372       return err;
00373     }
00374 
00375     if (x != 0) {
00376       // suggestions for a better algorithm welcome!
00377       e = int(log10(x));
00378       double n = floor(x/pow(10.0,e-p+1));
00379       if (n < pow(10.0,p-1)) {
00380     // first guess was not good
00381     e = e - 1;
00382     n = floor(x/pow(10.0,e-p+1));
00383     if (n >= pow(10.0,p)) {
00384       // violated constraint. try something else.
00385       n = pow(10.0,p-1);
00386       e = int(log10(x/n)) + p - 1;
00387     }
00388       }
00389 
00390       if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
00391     n++;
00392       assert(pow(10.0,p-1) <= n);
00393       assert(n < pow(10.0,p));
00394 
00395       m = integer_part_noexp(n);
00396       if (e < -6 || e >= p) {
00397     if (m.size() > 1)
00398       m = m.substr(0,1)+"."+m.substr(1);
00399     if (e >= 0)
00400       return String(s+m+"e+"+UString::from(e));
00401     else
00402       return String(s+m+"e-"+UString::from(-e));
00403       }
00404     }
00405     else {
00406       m = char_sequence('0',p);
00407       e = 0;
00408     }
00409 
00410     if (e == p-1) {
00411       return String(s+m);
00412     }
00413     else if (e >= 0) {
00414       if (e+1 < m.size())
00415     return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
00416       else
00417     return String(s+m.substr(0,e+1));
00418     }
00419     else {
00420       return String(s+"0."+char_sequence('0',-(e+1))+m);
00421     }
00422   }
00423   }
00424 
00425   return result;
00426 }
00427 
00428 // ------------------------------ NumberObjectImp ------------------------------
00429 
00430 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
00431 
00432 /* Source for number_object.lut.h
00433 @begin numberTable 5
00434   NaN           NumberObjectImp::NaNValue   DontEnum|DontDelete|ReadOnly
00435   NEGATIVE_INFINITY NumberObjectImp::NegInfinity    DontEnum|DontDelete|ReadOnly
00436   POSITIVE_INFINITY NumberObjectImp::PosInfinity    DontEnum|DontDelete|ReadOnly
00437   MAX_VALUE     NumberObjectImp::MaxValue   DontEnum|DontDelete|ReadOnly
00438   MIN_VALUE     NumberObjectImp::MinValue   DontEnum|DontDelete|ReadOnly
00439 @end
00440 */
00441 NumberObjectImp::NumberObjectImp(ExecState * /*exec*/,
00442                                  FunctionPrototypeImp *funcProto,
00443                                  NumberPrototypeImp *numberProto)
00444   : InternalFunctionImp(funcProto)
00445 {
00446   Value protect(this);
00447   // Number.Prototype
00448   putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
00449 
00450   // no. of arguments for constructor
00451   putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
00452 }
00453 
00454 Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
00455 {
00456   return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
00457 }
00458 
00459 Value NumberObjectImp::getValueProperty(ExecState *, int token) const
00460 {
00461   // ECMA 15.7.3
00462   switch(token) {
00463   case NaNValue:
00464     return Number(NaN);
00465   case NegInfinity:
00466     return Number(-Inf);
00467   case PosInfinity:
00468     return Number(Inf);
00469   case MaxValue:
00470     return Number(1.7976931348623157E+308);
00471   case MinValue:
00472     return Number(5E-324);
00473   }
00474   return Null();
00475 }
00476 
00477 bool NumberObjectImp::implementsConstruct() const
00478 {
00479   return true;
00480 }
00481 
00482 
00483 // ECMA 15.7.1
00484 Object NumberObjectImp::construct(ExecState *exec, const List &args)
00485 {
00486   ObjectImp *proto = exec->lexicalInterpreter()->builtinNumberPrototype().imp();
00487   Object obj(new NumberInstanceImp(proto));
00488 
00489   Number n;
00490   if (args.isEmpty())
00491     n = Number(0);
00492   else
00493     n = args[0].toNumber(exec);
00494 
00495   obj.setInternalValue(n);
00496 
00497   return obj;
00498 }
00499 
00500 bool NumberObjectImp::implementsCall() const
00501 {
00502   return true;
00503 }
00504 
00505 // ECMA 15.7.2
00506 Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00507 {
00508   if (args.isEmpty())
00509     return Number(0);
00510   else
00511     return Number(args[0].toNumber(exec));
00512 }
KDE Home | KDE Accessibility Home | Description of Access Keys