kjs Library API Documentation

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 }
KDE Logo
This file is part of the documentation for kjs Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 22 14:23:47 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003