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 sign;
00097   char *result = kjs_dtoa(d, 0, 0, &decimalPoint, &sign, NULL);
00098   int length = strlen(result);
00099 
00100   UString str = sign ? "-" : "";
00101   if (decimalPoint == 9999) {
00102     str += UString(result);
00103   } else if (decimalPoint <= 0) {
00104     str += UString("0");
00105   } else {
00106     char *buf;
00107 
00108     if (length <= decimalPoint) {
00109       buf = (char*)malloc(decimalPoint+1);
00110       strcpy(buf,result);
00111       memset(buf+length,'0',decimalPoint-length);
00112     } else {
00113       buf = (char*)malloc(decimalPoint+1);
00114       strncpy(buf,result,decimalPoint);
00115     }
00116 
00117     buf[decimalPoint] = '\0';
00118     str += UString(buf);
00119     free(buf);
00120   }
00121 
00122   kjs_freedtoa(result);
00123 
00124   return str;
00125 }
00126 
00127 static UString char_sequence(char c, int count)
00128 {
00129   char *buf = (char*)malloc(count+1);
00130   memset(buf,c,count);
00131   buf[count] = '\0';
00132   UString s(buf);
00133   free(buf);
00134   return s;
00135 }
00136 
00137 // ECMA 15.7.4.2 - 15.7.4.7
00138 Value NumberProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
00139 {
00140   Value result;
00141 
00142   // no generic function. "this" has to be a Number object
00143   KJS_CHECK_THIS( NumberInstanceImp, thisObj );
00144 
00145   // execute "toString()" or "valueOf()", respectively
00146   Value v = thisObj.internalValue();
00147   switch (id) {
00148   case ToString: {
00149     int radix = 10;
00150     if (!args.isEmpty() && args[0].type() != UndefinedType)
00151       radix = args[0].toInteger(exec);
00152     if (radix < 2 || radix > 36 || radix == 10)
00153       result = String(v.toString(exec));
00154     else {
00155       const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
00156       // INT_MAX results in 1024 characters left of the dot with radix 2
00157       // give the same space on the right side. safety checks are in place
00158       // unless someone finds a precise rule.
00159       char s[2048 + 3];
00160       double x = v.toNumber(exec);
00161       if (isNaN(x) || isInf(x))
00162         return String(UString::from(x));
00163       // apply algorithm on absolute value. add sign later.
00164       bool neg = false;
00165       if (x < 0.0) {
00166         neg = true;
00167         x = -x;
00168       }
00169       // convert integer portion
00170       double f = floor(x);
00171       double d = f;
00172       char *dot = s + sizeof(s) / 2;
00173       char *p = dot;
00174       *p = '\0';
00175       do {
00176         *--p = digits[int(fmod(d, double(radix)))];
00177         d /= radix;
00178       } while ((d <= -1.0 || d >= 1.0) && p > s);
00179       // any decimal fraction ?
00180       d = x - f;
00181       const double eps = 0.001; // TODO: guessed. base on radix ?
00182       if (d < -eps || d > eps) {
00183         *dot++ = '.';
00184         do {
00185           d *= radix;
00186           *dot++ = digits[int(d)];
00187           d -= int(d);
00188         } while ((d < -eps || d > eps) && dot - s < int(sizeof(s)) - 1);
00189         *dot = '\0';
00190       }
00191       // add sign if negative
00192       if (neg)
00193         *--p = '-';
00194       result = String(p);
00195     }
00196     break;
00197   }
00198   case ToLocaleString: /* TODO */
00199     result = String(v.toString(exec));
00200     break;
00201   case ValueOf:
00202     result = Number(v.toNumber(exec));
00203     break;
00204   case ToFixed:
00205   {
00206     // FIXME: firefox works for all values, not just 0..20.  This includes
00207     // NaN, infinity, undefined, etc.  This is just a hack to pass our regression
00208     // suite.
00209     Value fractionDigits = args[0];
00210     int f = -1;
00211     double fd = fractionDigits.toNumber(exec);
00212     if (isNaN(fd)) {
00213       f = 0;
00214     } else if (!isInf(fd)) {
00215       f = int(fd);
00216     }
00217     if (f < 0 || f > 20) {
00218       Object err = Error::create(exec,RangeError);
00219       exec->setException(err);
00220       return err;
00221     }
00222 
00223     double x = v.toNumber(exec);
00224     if (isNaN(x))
00225       return String("NaN");
00226 
00227     UString s = "";
00228     if (x < 0) {
00229       s += "-";
00230       x = -x;
00231     }
00232 
00233     if (x >= 1e21)
00234       return String(s+UString::from(x));
00235 
00236     double n = floor(x*pow(10.0,f));
00237     if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-x))
00238       n++;
00239 
00240     UString m = integer_part_noexp(n);
00241 
00242     int k = m.size();
00243     if (m.size() < f) {
00244       UString z = "";
00245       for (int i = 0; i < f+1-k; i++)
00246     z += "0";
00247       m = z + m;
00248       k = f + 1;
00249       assert(k == m.size());
00250     }
00251     if (k-f < m.size())
00252       return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
00253     else
00254       return String(s+m.substr(0,k-f));
00255   }
00256   case ToExponential: {
00257     double x = v.toNumber(exec);
00258 
00259     if (isNaN(x) || isInf(x))
00260       return String(UString::from(x));
00261 
00262     int f = 1;
00263     Value fractionDigits = args[0];
00264     if (args.size() > 0) {
00265       f = fractionDigits.toInteger(exec);
00266       if (f < 0 || f > 20) {
00267         Object err = Error::create(exec,RangeError);
00268         exec->setException(err);
00269         return err;
00270       }
00271     }
00272 
00273     int decimalAdjust = 0;
00274     if (!fractionDigits.isA(UndefinedType)) {
00275       double logx = floor(log10(x));
00276       x /= pow(10.0,logx);
00277       double fx = floor(x*pow(10.0,f))/pow(10.0,f);
00278       double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
00279 
00280       if (fabs(fx-x) < fabs(cx-x))
00281     x = fx;
00282       else
00283     x = cx;
00284 
00285       decimalAdjust = int(logx);
00286     }
00287 
00288     char buf[80];
00289     int decimalPoint;
00290     int sign;
00291 
00292     if (isNaN(x))
00293       return String("NaN");
00294 
00295     char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
00296     int length = strlen(result);
00297     decimalPoint += decimalAdjust;
00298 
00299     int i = 0;
00300     if (sign) {
00301       buf[i++] = '-';
00302     }
00303 
00304     if (decimalPoint == 999) {
00305       strcpy(buf + i, result);
00306     } else {
00307       buf[i++] = result[0];
00308 
00309       if (fractionDigits.isA(UndefinedType))
00310     f = length-1;
00311 
00312       if (length > 1 && f > 0) {
00313     buf[i++] = '.';
00314     int haveFDigits = length-1;
00315     if (f < haveFDigits) {
00316       strncpy(buf+i,result+1, f);
00317       i += f;
00318     }
00319     else {
00320       strcpy(buf+i,result+1);
00321       i += length-1;
00322       for (int j = 0; j < f-haveFDigits; j++)
00323         buf[i++] = '0';
00324     }
00325       }
00326 
00327       buf[i++] = 'e';
00328       buf[i++] = (decimalPoint >= 0) ? '+' : '-';
00329       // decimalPoint can't be more than 3 digits decimal given the
00330       // nature of float representation
00331       int exponential = decimalPoint - 1;
00332       if (exponential < 0) {
00333     exponential = exponential * -1;
00334       }
00335       if (exponential >= 100) {
00336     buf[i++] = '0' + exponential / 100;
00337       }
00338       if (exponential >= 10) {
00339     buf[i++] = '0' + (exponential % 100) / 10;
00340       }
00341       buf[i++] = '0' + exponential % 10;
00342       buf[i++] = '\0';
00343     }
00344 
00345     assert(i <= 80);
00346 
00347     kjs_freedtoa(result);
00348 
00349     return String(UString(buf));
00350   }
00351   case ToPrecision:
00352   {
00353     int e = 0;
00354     UString m;
00355 
00356     int p = args[0].toInteger(exec);
00357     double x = v.toNumber(exec);
00358     if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
00359       return String(v.toString(exec));
00360 
00361     UString s = "";
00362     if (x < 0) {
00363       s = "-";
00364       x = -x;
00365     }
00366 
00367     if (p < 1 || p > 21) {
00368       Object err = Error::create(exec, RangeError,
00369                  "toPrecision() argument must be between 1 and 21");
00370       exec->setException(err);
00371       return err;
00372     }
00373 
00374     if (x != 0) {
00375       e = int(log10(x));
00376       double n = floor(x/pow(10.0,e-p+1));
00377       if (n < pow(10.0,p-1)) {
00378     e = e - 1;
00379     n = floor(x/pow(10.0,e-p+1));
00380       }
00381 
00382       if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
00383     n++;
00384       assert(pow(10.0,p-1) <= n);
00385       assert(n < pow(10.0,p));
00386 
00387       m = integer_part_noexp(n);
00388       if (e < -6 || e >= p) {
00389     if (m.size() > 1)
00390       m = m.substr(0,1)+"."+m.substr(1);
00391     if (e >= 0)
00392       return String(s+m+"e+"+UString::from(e));
00393     else
00394       return String(s+m+"e-"+UString::from(-e));
00395       }
00396     }
00397     else {
00398       m = char_sequence('0',p);
00399       e = 0;
00400     }
00401 
00402     if (e == p-1) {
00403       return String(s+m);
00404     }
00405     else if (e >= 0) {
00406       if (e+1 < m.size())
00407     return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
00408       else
00409     return String(s+m.substr(0,e+1));
00410     }
00411     else {
00412       return String(s+"0."+char_sequence('0',-(e+1))+m);
00413     }
00414   }
00415   }
00416 
00417   return result;
00418 }
00419 
00420 // ------------------------------ NumberObjectImp ------------------------------
00421 
00422 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
00423 
00424 /* Source for number_object.lut.h
00425 @begin numberTable 5
00426   NaN           NumberObjectImp::NaNValue   DontEnum|DontDelete|ReadOnly
00427   NEGATIVE_INFINITY NumberObjectImp::NegInfinity    DontEnum|DontDelete|ReadOnly
00428   POSITIVE_INFINITY NumberObjectImp::PosInfinity    DontEnum|DontDelete|ReadOnly
00429   MAX_VALUE     NumberObjectImp::MaxValue   DontEnum|DontDelete|ReadOnly
00430   MIN_VALUE     NumberObjectImp::MinValue   DontEnum|DontDelete|ReadOnly
00431 @end
00432 */
00433 NumberObjectImp::NumberObjectImp(ExecState * /*exec*/,
00434                                  FunctionPrototypeImp *funcProto,
00435                                  NumberPrototypeImp *numberProto)
00436   : InternalFunctionImp(funcProto)
00437 {
00438   Value protect(this);
00439   // Number.Prototype
00440   putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
00441 
00442   // no. of arguments for constructor
00443   putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
00444 }
00445 
00446 Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
00447 {
00448   return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
00449 }
00450 
00451 Value NumberObjectImp::getValueProperty(ExecState *, int token) const
00452 {
00453   // ECMA 15.7.3
00454   switch(token) {
00455   case NaNValue:
00456     return Number(NaN);
00457   case NegInfinity:
00458     return Number(-Inf);
00459   case PosInfinity:
00460     return Number(Inf);
00461   case MaxValue:
00462     return Number(1.7976931348623157E+308);
00463   case MinValue:
00464     return Number(5E-324);
00465   }
00466   return Null();
00467 }
00468 
00469 bool NumberObjectImp::implementsConstruct() const
00470 {
00471   return true;
00472 }
00473 
00474 
00475 // ECMA 15.7.1
00476 Object NumberObjectImp::construct(ExecState *exec, const List &args)
00477 {
00478   ObjectImp *proto = exec->lexicalInterpreter()->builtinNumberPrototype().imp();
00479   Object obj(new NumberInstanceImp(proto));
00480 
00481   Number n;
00482   if (args.isEmpty())
00483     n = Number(0);
00484   else
00485     n = args[0].toNumber(exec);
00486 
00487   obj.setInternalValue(n);
00488 
00489   return obj;
00490 }
00491 
00492 bool NumberObjectImp::implementsCall() const
00493 {
00494   return true;
00495 }
00496 
00497 // ECMA 15.7.2
00498 Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00499 {
00500   if (args.isEmpty())
00501     return Number(0);
00502   else
00503     return Number(args[0].toNumber(exec));
00504 }
KDE Home | KDE Accessibility Home | Description of Access Keys