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       // apply algorithm on absolute value. add sign later.
00162       bool neg = false;
00163       if (x < 0.0) {
00164         neg = true;
00165         x = -x;
00166       }
00167       // convert integer portion
00168       double f = floor(x);
00169       double d = f;
00170       char *dot = s + sizeof(s) / 2;
00171       char *p = dot;
00172       *p = '\0';
00173       do {
00174         *--p = digits[int(fmod(d, double(radix)))];
00175         d /= radix;
00176       } while ((d <= -1.0 || d >= 1.0) && p > s);
00177       // any decimal fraction ?
00178       d = x - f;
00179       const double eps = 0.001; // TODO: guessed. base on radix ?
00180       if (d < -eps || d > eps) {
00181         *dot++ = '.';
00182         do {
00183           d *= radix;
00184           *dot++ = digits[int(d)];
00185           d -= int(d);
00186         } while ((d < -eps || d > eps) && dot - s < int(sizeof(s)) - 1);
00187         *dot = '\0';
00188       }
00189       // add sign if negative
00190       if (neg)
00191         *--p = '-';
00192       result = String(p);
00193     }
00194     break;
00195   }
00196   case ToLocaleString: /* TODO */
00197     result = String(v.toString(exec));
00198     break;
00199   case ValueOf:
00200     result = Number(v.toNumber(exec));
00201     break;
00202   case ToFixed: {
00203     Value fractionDigits = args[0];
00204     int f = fractionDigits.toInteger(exec);
00205     if (f < 0 || f > 20) {
00206       Object err = Error::create(exec,RangeError);
00207       exec->setException(err);
00208       return err;
00209     }
00210 
00211     double x = v.toNumber(exec);
00212     if (isNaN(x))
00213       return String("NaN");
00214 
00215     UString s = "";
00216     if (x < 0) {
00217       s += "-";
00218       x = -x;
00219     }
00220 
00221     if (x >= 1e21)
00222       return String(s+UString::from(x));
00223 
00224     double n = floor(x*pow(10.0,f));
00225     if (fabs(n/pow(10.0,f)-x) > fabs((n+1)/pow(10.0,f)-x))
00226       n++;
00227 
00228     UString m = integer_part_noexp(n);
00229 
00230     int k = m.size();
00231     if (m.size() < f) {
00232       UString z = "";
00233       for (int i = 0; i < f+1-k; i++)
00234     z += "0";
00235       m = z + m;
00236       k = f + 1;
00237       assert(k == m.size());
00238     }
00239     if (k-f < m.size())
00240       return String(s+m.substr(0,k-f)+"."+m.substr(k-f));
00241     else
00242       return String(s+m.substr(0,k-f));
00243   }
00244   case ToExponential: {
00245     double x = v.toNumber(exec);
00246 
00247     if (isNaN(x) || isInf(x))
00248       return String(UString::from(x));
00249 
00250     Value fractionDigits = args[0];
00251     int f = fractionDigits.toInteger(exec);
00252     if (f < 0 || f > 20) {
00253       Object err = Error::create(exec,RangeError);
00254       exec->setException(err);
00255       return err;
00256     }
00257 
00258     int decimalAdjust = 0;
00259     if (!fractionDigits.isA(UndefinedType)) {
00260       double logx = floor(log10(x));
00261       x /= pow(10.0,logx);
00262       double fx = floor(x*pow(10.0,f))/pow(10.0,f);
00263       double cx = ceil(x*pow(10.0,f))/pow(10.0,f);
00264 
00265       if (fabs(fx-x) < fabs(cx-x))
00266     x = fx;
00267       else
00268     x = cx;
00269 
00270       decimalAdjust = int(logx);
00271     }
00272 
00273     char buf[80];
00274     int decimalPoint;
00275     int sign;
00276 
00277     if (isNaN(x))
00278       return String("NaN");
00279 
00280     char *result = kjs_dtoa(x, 0, 0, &decimalPoint, &sign, NULL);
00281     int length = strlen(result);
00282     decimalPoint += decimalAdjust;
00283 
00284     int i = 0;
00285     if (sign) {
00286       buf[i++] = '-';
00287     }
00288 
00289     if (decimalPoint == 999) {
00290       strcpy(buf + i, result);
00291     } else {
00292       buf[i++] = result[0];
00293 
00294       if (fractionDigits.isA(UndefinedType))
00295     f = length-1;
00296 
00297       if (length > 1 && f > 0) {
00298     buf[i++] = '.';
00299     int haveFDigits = length-1;
00300     if (f < haveFDigits) {
00301       strncpy(buf+i,result+1, f);
00302       i += f;
00303     }
00304     else {
00305       strcpy(buf+i,result+1);
00306       i += length-1;
00307       for (int j = 0; j < f-haveFDigits; j++)
00308         buf[i++] = '0';
00309     }
00310       }
00311 
00312       buf[i++] = 'e';
00313       buf[i++] = (decimalPoint >= 0) ? '+' : '-';
00314       // decimalPoint can't be more than 3 digits decimal given the
00315       // nature of float representation
00316       int exponential = decimalPoint - 1;
00317       if (exponential < 0) {
00318     exponential = exponential * -1;
00319       }
00320       if (exponential >= 100) {
00321     buf[i++] = '0' + exponential / 100;
00322       }
00323       if (exponential >= 10) {
00324     buf[i++] = '0' + (exponential % 100) / 10;
00325       }
00326       buf[i++] = '0' + exponential % 10;
00327       buf[i++] = '\0';
00328     }
00329 
00330     assert(i <= 80);
00331 
00332     kjs_freedtoa(result);
00333 
00334     return String(UString(buf));
00335   }
00336   case ToPrecision: {
00337     int e = 0;
00338     UString m;
00339 
00340     int p = args[0].toInteger(exec);
00341     double x = v.toNumber(exec);
00342     if (args[0].isA(UndefinedType) || isNaN(x) || isInf(x))
00343       return String(v.toString(exec));
00344 
00345     UString s = "";
00346     if (x < 0) {
00347       s = "-";
00348       x = -x;
00349     }
00350 
00351     if (p < 1 || p > 21) {
00352       Object err = Error::create(exec,RangeError);
00353       exec->setException(err);
00354       return err;
00355     }
00356 
00357     if (x != 0) {
00358       e = int(log10(x));
00359       double n = floor(x/pow(10.0,e-p+1));
00360       if (n < pow(10.0,p-1)) {
00361     e = e - 1;
00362     n = floor(x/pow(10.0,e-p+1));
00363       }
00364 
00365       if (fabs((n+1)*pow(10.0,e-p+1)-x) < fabs(n*pow(10.0,e-p+1)-x))
00366     n++;
00367       assert(pow(10.0,p-1) <= n);
00368       assert(n < pow(10.0,p));
00369 
00370       m = integer_part_noexp(n);
00371       if (e < -6 || e >= p) {
00372     if (m.size() > 1)
00373       m = m.substr(0,1)+"."+m.substr(1);
00374     if (e >= 0)
00375       return String(s+m+"e+"+UString::from(e));
00376     else
00377       return String(s+m+"e-"+UString::from(-e));
00378       }
00379     }
00380     else {
00381       m = char_sequence('0',p);
00382       e = 0;
00383     }
00384 
00385     if (e == p-1) {
00386       return String(s+m);
00387     }
00388     else if (e >= 0) {
00389       if (e+1 < m.size())
00390     return String(s+m.substr(0,e+1)+"."+m.substr(e+1));
00391       else
00392     return String(s+m.substr(0,e+1));
00393     }
00394     else {
00395       return String(s+"0."+char_sequence('0',-(e+1))+m);
00396     }
00397   }
00398   }
00399 
00400   return result;
00401 }
00402 
00403 // ------------------------------ NumberObjectImp ------------------------------
00404 
00405 const ClassInfo NumberObjectImp::info = {"Function", &InternalFunctionImp::info, &numberTable, 0};
00406 
00407 /* Source for number_object.lut.h
00408 @begin numberTable 5
00409   NaN           NumberObjectImp::NaNValue   DontEnum|DontDelete|ReadOnly
00410   NEGATIVE_INFINITY NumberObjectImp::NegInfinity    DontEnum|DontDelete|ReadOnly
00411   POSITIVE_INFINITY NumberObjectImp::PosInfinity    DontEnum|DontDelete|ReadOnly
00412   MAX_VALUE     NumberObjectImp::MaxValue   DontEnum|DontDelete|ReadOnly
00413   MIN_VALUE     NumberObjectImp::MinValue   DontEnum|DontDelete|ReadOnly
00414 @end
00415 */
00416 NumberObjectImp::NumberObjectImp(ExecState */*exec*/,
00417                                  FunctionPrototypeImp *funcProto,
00418                                  NumberPrototypeImp *numberProto)
00419   : InternalFunctionImp(funcProto)
00420 {
00421   Value protect(this);
00422   // Number.Prototype
00423   putDirect(prototypePropertyName, numberProto, DontEnum|DontDelete|ReadOnly);
00424 
00425   // no. of arguments for constructor
00426   putDirect(lengthPropertyName, NumberImp::one(), ReadOnly|DontDelete|DontEnum);
00427 }
00428 
00429 Value NumberObjectImp::get(ExecState *exec, const Identifier &propertyName) const
00430 {
00431   return lookupGetValue<NumberObjectImp, InternalFunctionImp>( exec, propertyName, &numberTable, this );
00432 }
00433 
00434 Value NumberObjectImp::getValueProperty(ExecState *, int token) const
00435 {
00436   // ECMA 15.7.3
00437   switch(token) {
00438   case NaNValue:
00439     return Number(NaN);
00440   case NegInfinity:
00441     return Number(-Inf);
00442   case PosInfinity:
00443     return Number(Inf);
00444   case MaxValue:
00445     return Number(1.7976931348623157E+308);
00446   case MinValue:
00447     return Number(5E-324);
00448   }
00449   return Null();
00450 }
00451 
00452 bool NumberObjectImp::implementsConstruct() const
00453 {
00454   return true;
00455 }
00456 
00457 
00458 // ECMA 15.7.1
00459 Object NumberObjectImp::construct(ExecState *exec, const List &args)
00460 {
00461   ObjectImp *proto = exec->interpreter()->builtinNumberPrototype().imp();
00462   Object obj(new NumberInstanceImp(proto));
00463 
00464   Number n;
00465   if (args.isEmpty())
00466     n = Number(0);
00467   else
00468     n = args[0].toNumber(exec);
00469 
00470   obj.setInternalValue(n);
00471 
00472   return obj;
00473 }
00474 
00475 bool NumberObjectImp::implementsCall() const
00476 {
00477   return true;
00478 }
00479 
00480 // ECMA 15.7.2
00481 Value NumberObjectImp::call(ExecState *exec, Object &/*thisObj*/, const List &args)
00482 {
00483   if (args.isEmpty())
00484     return Number(0);
00485   else
00486     return Number(args[0].toNumber(exec));
00487 }
KDE Logo
This file is part of the documentation for kjs Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 4 12:34:40 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003