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