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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 12 15:08:29 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003