kjs Library: number_object.cpp Source File (kjs Library)

| # kjs Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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     Value fractionDigits = args[0];
    00206     int f = fractionDigits.toInteger(exec);
    00207     if (f < 0 || f > 20) {
    00208       Object err = Error::create(exec,RangeError);
    00209       exec->setException(err);
    00210       return err;
    00211     }
    00212
    00213     double x = v.toNumber(exec);
    00214     if (isNaN(x))
    00215       return String("NaN");
    00216
    00217     UString s = "";
    00218     if (x < 0) {
    00219       s += "-";
    00220       x = -x;
    00221     }
    00222
    00223     if (x >= 1e21)
    00224       return String(s+UString::from(x));
    00225
    00226     double n = floor(x*pow(10.0,f));
    00227     if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-x))
    00228       n++;
    00229
    00230     UString m = integer_part_noexp(n);
    00231
    00232     int k = m.size();
    00233     if (m.size() < f) {
    00234       UString z = "";
    00235       for (int i = 0; i < f+1-k; i++)
    00236     z += "0";
    00237       m = z + m;
    00238       k = f + 1;
    00239       assert(k == m.size());
    00240     }
    00241     if (k-f < m.size())
    00242       return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
    00243     else
    00244       return String(s+m.substr(0,k-f));
    00245   }
    00246   case ToExponential: {
    00247     double x = v.toNumber(exec);
    00248
    00249     if (isNaN(x) || isInf(x))
    00250       return String(UString::from(x));
    00251
    00252     Value fractionDigits = args[0];
    00253     int f = fractionDigits.toInteger(exec);
    00254     if (f < 0 || f > 20) {
    00255       Object err = Error::create(exec,RangeError);
    00256       exec->setException(err);
    00257       return err;
    00258     }
    00259
    00260     int decimalAdjust = 0;
    00261     if (!fractionDigits.isA(UndefinedType)) {
    00262       double logx = floor(log10(x));
    00263       x /= pow(10.0,logx);
    00264       double fx = floor(x*pow(10.0,f))/pow(10.0,f);
    00265       double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
    00266
    00267       if (fabs(fx-x) < fabs(cx-x))
    00268     x = fx;
    00269       else
    00270     x = cx;
    00271
    00272       decimalAdjust = int(logx);
    00273     }
    00274
    00275     char buf[80];
    00276     int decimalPoint;
    00277     int sign;
    00278
    00279     if (isNaN(x))
    00280       return String("NaN");
    00281
    00282     char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
    00283     int length = strlen(result);
    00284     decimalPoint += decimalAdjust;
    00285
    00286     int i = 0;
    00287     if (sign) {
    00288       buf[i++] = '-';
    00289     }
    00290
    00291     if (decimalPoint == 999) {
    00292       strcpy(buf + i, result);
    00293     } else {
    00294       buf[i++] = result[0];
    00295
    00296       if (fractionDigits.isA(UndefinedType))
    00297     f = length-1;
    00298
    00299       if (length > 1 && f > 0) {
    00300     buf[i++] = '.';
    00301     int haveFDigits = length-1;
    00302     if (f < haveFDigits) {
    00303       strncpy(buf+i,result+1, f);
    00304       i += f;
    00305     }
    00306     else {
    00307       strcpy(buf+i,result+1);
    00308       i += length-1;
    00309       for (int j = 0; j < f-haveFDigits; j++)
    00310         buf[i++] = '0';
    00311     }
    00312       }
    00313
    00314       buf[i++] = 'e';
    00315       buf[i++] = (decimalPoint >= 0) ? '+' : '-';
    00316       // decimalPoint can't be more than 3 digits decimal given the
    00317       // nature of float representation
    00318       int exponential = decimalPoint - 1;
    00319       if (exponential < 0) {
    00320     exponential = exponential * -1;
    00321       }
    00322       if (exponential >= 100) {
    00323     buf[i++] = '0' + exponential / 100;
    00324       }
    00325       if (exponential >= 10) {
    00326     buf[i++] = '0' + (exponential % 100) / 10;
    00327       }
    00328       buf[i++] = '0' + exponential % 10;
    00329       buf[i++] = '\0';
    00330     }
    00331
    00332     assert(i <= 80);
    00333
    00334     kjs_freedtoa(result);
    00335
    00336     return String(UString(buf));
    00337   }
    00338   case ToPrecision: {
    00339     int e = 0;
    00340     UString m;
    00341
    00342     int p = args[0].toInteger(exec);
    00343     double x = v.toNumber(exec);
    00344     if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
    00345       return String(v.toString(exec));
    00346
    00347     UString s = "";
    00348     if (x < 0) {
    00349       s = "-";
    00350       x = -x;
    00351     }
    00352
    00353     if (p < 1 || p > 21) {
    00354       Object err = Error::create(exec,RangeError);
    00355       exec->setException(err);
    00356       return err;
    00357     }
    00358
    00359     if (x != 0) {
    00360       e = int(log10(x));
    00361       double n = floor(x/pow(10.0,e-p+1));
    00362       if (n < pow(10.0,p-1)) {
    00363     e = e - 1;
    00364     n = floor(x/pow(10.0,e-p+1));
    00365       }
    00366
    00367       if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
    00368     n++;
    00369       assert(pow(10.0,p-1) <= n);
    00370       assert(n < pow(10.0,p));
    00371
    00372       m = integer_part_noexp(n);
    00373       if (e < -6 || e >= p) {
    00374     if (m.size() > 1)
    00375       m = m.substr(0,1)+"."+m.substr(1);
    00376     if (e >= 0)
    00377       return String(s+m+"e+"+UString::from(e));
    00378     else
    00379       return String(s+m+"e-"+UString::from(-e));
    00380       }
    00381     }
    00382     else {
    00383       m = char_sequence('0',p);
    00384       e = 0;
    00385     }
    00386
    00387     if (e == p-1) {
    00388       return String(s+m);
    00389     }
    00390     else if (e >= 0) {
    00391       if (e+1 < m.size())
    00392     return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
    00393       else
    00394     return String(s+m.substr(0,e+1));
    00395     }
    00396     else {
    00397       return String(s+"0."+char_sequence('0',-(e+1))+m);
    00398     }
    00399   }
    00400   }
    00401
    00402   return result;
    00403 }
    00404
    00405 // ------------------------------ NumberObjectImp ------------------------------
    00406
    00407 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
    00408
    00409 /* Source for number_object.lut.h
    00410 @begin numberTable 5
    00411   NaN           NumberObjectImp::NaNValue   DontEnum|DontDelete|ReadOnly
    00412   NEGATIVE_INFINITY NumberObjectImp::NegInfinity    DontEnum|DontDelete|ReadOnly
    00413   POSITIVE_INFINITY NumberObjectImp::PosInfinity    DontEnum|DontDelete|ReadOnly
    00414   MAX_VALUE     NumberObjectImp::MaxValue   DontEnum|DontDelete|ReadOnly
    00415   MIN_VALUE     NumberObjectImp::MinValue   DontEnum|DontDelete|ReadOnly
    00416 @end
    00417 */
    00418 NumberObjectImp::NumberObjectImp(ExecState * /*exec*/,
    00419                                  FunctionPrototypeImp *funcProto,
    00420                                  NumberPrototypeImp *numberProto)
    00421   : InternalFunctionImp(funcProto)
    00422 {
    00423   Value protect(this);
    00424   // Number.Prototype
    00425   putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
    00426
    00427   // no. of arguments for constructor
    00428   putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
    00429 }
    00430
    00431 Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
    00432 {
    00433   return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
    00434 }
    00435
    00436 Value NumberObjectImp::getValueProperty(ExecState *, int token) const
    00437 {
    00438   // ECMA 15.7.3
    00439   switch(token) {
    00440   case NaNValue:
    00441     return Number(NaN);
    00442   case NegInfinity:
    00443     return Number(-Inf);
    00444   case PosInfinity:
    00445     return Number(Inf);
    00446   case MaxValue:
    00447     return Number(1.7976931348623157E+308);
    00448   case MinValue:
    00449     return Number(5E-324);
    00450   }
    00451   return Null();
    00452 }
    00453
    00454 bool NumberObjectImp::implementsConstruct() const
    00455 {
    00456   return true;
    00457 }
    00458
    00459
    00460 // ECMA 15.7.1
    00461 Object NumberObjectImp::construct(ExecState *exec, const List &args)
    00462 {
    00463   ObjectImp *proto = exec->interpreter()->builtinNumberPrototype().imp();
    00464   Object obj(new NumberInstanceImp(proto));
    00465
    00466   Number n;
    00467   if (args.isEmpty())
    00468     n = Number(0);
    00469   else
    00470     n = args[0].toNumber(exec);
    00471
    00472   obj.setInternalValue(n);
    00473
    00474   return obj;
    00475 }
    00476
    00477 bool NumberObjectImp::implementsCall() const
    00478 {
    00479   return true;
    00480 }
    00481
    00482 // ECMA 15.7.2
    00483 Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
    00484 {
    00485   if (args.isEmpty())
    00486     return Number(0);
    00487   else
    00488     return Number(args[0].toNumber(exec));
    00489 }

This file is part of the documentation for kjs Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:23:07 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003