Changeset 34854 in webkit for trunk/JavaScriptCore/kjs/NumberObject.cpp
- Timestamp:
- Jun 28, 2008, 2:22:01 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JavaScriptCore/kjs/NumberObject.cpp
r34821 r34854 22 22 #include "config.h" 23 23 #include "NumberObject.h" 24 #include "NumberObject.lut.h"25 26 #include "dtoa.h"27 #include "error_object.h"28 #include "operations.h"29 #include <wtf/Assertions.h>30 #include <wtf/MathExtras.h>31 #include <wtf/Vector.h>32 24 33 25 namespace KJS { 34 35 // ------------------------------ NumberObject ----------------------------36 26 37 27 const ClassInfo NumberObject::info = { "Number", 0, 0, 0 }; … … 47 37 } 48 38 49 // ------------------------------ NumberPrototype ---------------------------50 51 static JSValue* numberProtoFuncToString(ExecState*, JSObject*, JSValue*, const ArgList&);52 static JSValue* numberProtoFuncToLocaleString(ExecState*, JSObject*, JSValue*, const ArgList&);53 static JSValue* numberProtoFuncValueOf(ExecState*, JSObject*, JSValue*, const ArgList&);54 static JSValue* numberProtoFuncToFixed(ExecState*, JSObject*, JSValue*, const ArgList&);55 static JSValue* numberProtoFuncToExponential(ExecState*, JSObject*, JSValue*, const ArgList&);56 static JSValue* numberProtoFuncToPrecision(ExecState*, JSObject*, JSValue*, const ArgList&);57 58 // ECMA 15.7.459 60 NumberPrototype::NumberPrototype(ExecState* exec, ObjectPrototype* objectPrototype, FunctionPrototype* functionPrototype)61 : NumberObject(objectPrototype)62 {63 setInternalValue(jsNumber(exec, 0));64 65 // The constructor will be added later, after NumberConstructor has been constructed66 67 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toString, numberProtoFuncToString), DontEnum);68 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().toLocaleString, numberProtoFuncToLocaleString), DontEnum);69 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 0, exec->propertyNames().valueOf, numberProtoFuncValueOf), DontEnum);70 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toFixed, numberProtoFuncToFixed), DontEnum);71 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toExponential, numberProtoFuncToExponential), DontEnum);72 putDirectFunction(new (exec) PrototypeFunction(exec, functionPrototype, 1, exec->propertyNames().toPrecision, numberProtoFuncToPrecision), DontEnum);73 }74 75 // ------------------------------ Functions ---------------------------76 77 // ECMA 15.7.4.2 - 15.7.4.778 79 static UString integer_part_noexp(double d)80 {81 int decimalPoint;82 int sign;83 char* result = dtoa(d, 0, &decimalPoint, &sign, NULL);84 bool resultIsInfOrNan = (decimalPoint == 9999);85 size_t length = strlen(result);86 87 UString str = sign ? "-" : "";88 if (resultIsInfOrNan)89 str += result;90 else if (decimalPoint <= 0)91 str += "0";92 else {93 Vector<char, 1024> buf(decimalPoint + 1);94 95 if (static_cast<int>(length) <= decimalPoint) {96 strcpy(buf.data(), result);97 memset(buf.data() + length, '0', decimalPoint - length);98 } else99 strncpy(buf.data(), result, decimalPoint);100 101 buf[decimalPoint] = '\0';102 str.append(buf.data());103 }104 105 freedtoa(result);106 107 return str;108 }109 110 static UString char_sequence(char c, int count)111 {112 Vector<char, 2048> buf(count + 1, c);113 buf[count] = '\0';114 115 return UString(buf.data());116 }117 118 static double intPow10(int e)119 {120 // This function uses the "exponentiation by squaring" algorithm and121 // long double to quickly and precisely calculate integer powers of 10.0.122 123 // This is a handy workaround for <rdar://problem/4494756>124 125 if (e == 0)126 return 1.0;127 128 bool negative = e < 0;129 unsigned exp = negative ? -e : e;130 131 long double result = 10.0;132 bool foundOne = false;133 for (int bit = 31; bit >= 0; bit--) {134 if (!foundOne) {135 if ((exp >> bit) & 1)136 foundOne = true;137 } else {138 result = result * result;139 if ((exp >> bit) & 1)140 result = result * 10.0;141 }142 }143 144 if (negative)145 return static_cast<double>(1.0 / result);146 return static_cast<double>(result);147 }148 149 150 JSValue* numberProtoFuncToString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)151 {152 JSValue* v = thisValue->getJSNumber();153 if (!v)154 return throwError(exec, TypeError);155 156 double radixAsDouble = args[0]->toInteger(exec); // nan -> 0157 if (radixAsDouble == 10 || args[0]->isUndefined())158 return jsString(exec, v->toString(exec));159 160 if (radixAsDouble < 2 || radixAsDouble > 36)161 return throwError(exec, RangeError, "toString() radix argument must be between 2 and 36");162 163 int radix = static_cast<int>(radixAsDouble);164 const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";165 // INT_MAX results in 1024 characters left of the dot with radix 2166 // give the same space on the right side. safety checks are in place167 // unless someone finds a precise rule.168 char s[2048 + 3];169 const char* lastCharInString = s + sizeof(s) - 1;170 double x = v->uncheckedGetNumber();171 if (isnan(x) || isinf(x))172 return jsString(exec, UString::from(x));173 174 bool isNegative = x < 0.0;175 if (isNegative)176 x = -x;177 178 double integerPart = floor(x);179 char* decimalPoint = s + sizeof(s) / 2;180 181 // convert integer portion182 char* p = decimalPoint;183 double d = integerPart;184 do {185 int remainderDigit = static_cast<int>(fmod(d, radix));186 *--p = digits[remainderDigit];187 d /= radix;188 } while ((d <= -1.0 || d >= 1.0) && s < p);189 190 if (isNegative)191 *--p = '-';192 char* startOfResultString = p;193 ASSERT(s <= startOfResultString);194 195 d = x - integerPart;196 p = decimalPoint;197 const double epsilon = 0.001; // TODO: guessed. base on radix ?198 bool hasFractionalPart = (d < -epsilon || d > epsilon);199 if (hasFractionalPart) {200 *p++ = '.';201 do {202 d *= radix;203 const int digit = static_cast<int>(d);204 *p++ = digits[digit];205 d -= digit;206 } while ((d < -epsilon || d > epsilon) && p < lastCharInString);207 }208 *p = '\0';209 ASSERT(p < s + sizeof(s));210 211 return jsString(exec, startOfResultString);212 }213 214 JSValue* numberProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)215 {216 // FIXME: Not implemented yet.217 218 JSValue* v = thisValue->getJSNumber();219 if (!v)220 return throwError(exec, TypeError);221 222 return jsString(exec, v->toString(exec));223 }224 225 JSValue* numberProtoFuncValueOf(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&)226 {227 JSValue* v = thisValue->getJSNumber();228 if (!v)229 return throwError(exec, TypeError);230 231 return v;232 }233 234 JSValue* numberProtoFuncToFixed(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)235 {236 JSValue* v = thisValue->getJSNumber();237 if (!v)238 return throwError(exec, TypeError);239 240 JSValue* fractionDigits = args[0];241 double df = fractionDigits->toInteger(exec);242 if (!(df >= 0 && df <= 20))243 return throwError(exec, RangeError, "toFixed() digits argument must be between 0 and 20");244 int f = (int)df;245 246 double x = v->uncheckedGetNumber();247 if (isnan(x))248 return jsString(exec, "NaN");249 250 UString s;251 if (x < 0) {252 s.append('-');253 x = -x;254 } else if (x == -0.0)255 x = 0;256 257 if (x >= pow(10.0, 21.0))258 return jsString(exec, s + UString::from(x));259 260 const double tenToTheF = pow(10.0, f);261 double n = floor(x * tenToTheF);262 if (fabs(n / tenToTheF - x) >= fabs((n + 1) / tenToTheF - x))263 n++;264 265 UString m = integer_part_noexp(n);266 267 int k = m.size();268 if (k <= f) {269 UString z;270 for (int i = 0; i < f + 1 - k; i++)271 z.append('0');272 m = z + m;273 k = f + 1;274 ASSERT(k == m.size());275 }276 int kMinusf = k - f;277 if (kMinusf < m.size())278 return jsString(exec, s + m.substr(0, kMinusf) + "." + m.substr(kMinusf));279 return jsString(exec, s + m.substr(0, kMinusf));280 }281 282 static void fractionalPartToString(char* buf, int& i, const char* result, int resultLength, int fractionalDigits)283 {284 if (fractionalDigits <= 0)285 return;286 287 int fDigitsInResult = static_cast<int>(resultLength) - 1;288 buf[i++] = '.';289 if (fDigitsInResult > 0) {290 if (fractionalDigits < fDigitsInResult) {291 strncpy(buf + i, result + 1, fractionalDigits);292 i += fractionalDigits;293 } else {294 strcpy(buf + i, result + 1);295 i += static_cast<int>(resultLength) - 1;296 }297 }298 299 for (int j = 0; j < fractionalDigits - fDigitsInResult; j++)300 buf[i++] = '0';301 }302 303 static void exponentialPartToString(char* buf, int& i, int decimalPoint)304 {305 buf[i++] = 'e';306 buf[i++] = (decimalPoint >= 0) ? '+' : '-';307 // decimalPoint can't be more than 3 digits decimal given the308 // nature of float representation309 int exponential = decimalPoint - 1;310 if (exponential < 0)311 exponential *= -1;312 if (exponential >= 100)313 buf[i++] = static_cast<char>('0' + exponential / 100);314 if (exponential >= 10)315 buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);316 buf[i++] = static_cast<char>('0' + exponential % 10);317 }318 319 JSValue* numberProtoFuncToExponential(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)320 {321 JSValue* v = thisValue->getJSNumber();322 if (!v)323 return throwError(exec, TypeError);324 325 double x = v->uncheckedGetNumber();326 327 if (isnan(x) || isinf(x))328 return jsString(exec, UString::from(x));329 330 JSValue* fractionalDigitsValue = args[0];331 double df = fractionalDigitsValue->toInteger(exec);332 if (!(df >= 0 && df <= 20))333 return throwError(exec, RangeError, "toExponential() argument must between 0 and 20");334 int fractionalDigits = (int)df;335 bool includeAllDigits = fractionalDigitsValue->isUndefined();336 337 int decimalAdjust = 0;338 if (x && !includeAllDigits) {339 double logx = floor(log10(fabs(x)));340 x /= pow(10.0, logx);341 const double tenToTheF = pow(10.0, fractionalDigits);342 double fx = floor(x * tenToTheF) / tenToTheF;343 double cx = ceil(x * tenToTheF) / tenToTheF;344 345 if (fabs(fx - x) < fabs(cx - x))346 x = fx;347 else348 x = cx;349 350 decimalAdjust = static_cast<int>(logx);351 }352 353 if (isnan(x))354 return jsString(exec, "NaN");355 356 if (x == -0.0) // (-0.0).toExponential() should print as 0 instead of -0357 x = 0;358 359 int decimalPoint;360 int sign;361 char* result = dtoa(x, 0, &decimalPoint, &sign, NULL);362 size_t resultLength = strlen(result);363 decimalPoint += decimalAdjust;364 365 int i = 0;366 char buf[80]; // digit + '.' + fractionDigits (max 20) + 'e' + sign + exponent (max?)367 if (sign)368 buf[i++] = '-';369 370 if (decimalPoint == 999) // ? 9999 is the magical "result is Inf or NaN" value. what's 999??371 strcpy(buf + i, result);372 else {373 buf[i++] = result[0];374 375 if (includeAllDigits)376 fractionalDigits = static_cast<int>(resultLength) - 1;377 378 fractionalPartToString(buf, i, result, resultLength, fractionalDigits);379 exponentialPartToString(buf, i, decimalPoint);380 buf[i++] = '\0';381 }382 ASSERT(i <= 80);383 384 freedtoa(result);385 386 return jsString(exec, buf);387 }388 389 JSValue* numberProtoFuncToPrecision(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args)390 {391 JSValue* v = thisValue->getJSNumber();392 if (!v)393 return throwError(exec, TypeError);394 395 double doublePrecision = args[0]->toIntegerPreserveNaN(exec);396 double x = v->uncheckedGetNumber();397 if (args[0]->isUndefined() || isnan(x) || isinf(x))398 return jsString(exec, v->toString(exec));399 400 UString s;401 if (x < 0) {402 s = "-";403 x = -x;404 }405 406 if (!(doublePrecision >= 1 && doublePrecision <= 21)) // true for NaN407 return throwError(exec, RangeError, "toPrecision() argument must be between 1 and 21");408 int precision = (int)doublePrecision;409 410 int e = 0;411 UString m;412 if (x) {413 e = static_cast<int>(log10(x));414 double tens = intPow10(e - precision + 1);415 double n = floor(x / tens);416 if (n < intPow10(precision - 1)) {417 e = e - 1;418 tens = intPow10(e - precision + 1);419 n = floor(x / tens);420 }421 422 if (fabs((n + 1.0) * tens - x) <= fabs(n * tens - x))423 ++n;424 // maintain n < 10^(precision)425 if (n >= intPow10(precision)) {426 n /= 10.0;427 e += 1;428 }429 ASSERT(intPow10(precision - 1) <= n);430 ASSERT(n < intPow10(precision));431 432 m = integer_part_noexp(n);433 if (e < -6 || e >= precision) {434 if (m.size() > 1)435 m = m.substr(0, 1) + "." + m.substr(1);436 if (e >= 0)437 return jsString(exec, s + m + "e+" + UString::from(e));438 return jsString(exec, s + m + "e-" + UString::from(-e));439 }440 } else {441 m = char_sequence('0', precision);442 e = 0;443 }444 445 if (e == precision - 1)446 return jsString(exec, s + m);447 if (e >= 0) {448 if (e + 1 < m.size())449 return jsString(exec, s + m.substr(0, e + 1) + "." + m.substr(e + 1));450 return jsString(exec, s + m);451 }452 return jsString(exec, s + "0." + char_sequence('0', -(e + 1)) + m);453 }454 455 // ------------------------------ NumberConstructor ------------------------------456 457 const ClassInfo NumberConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::numberTable };458 459 /* Source for NumberObject.lut.h460 @begin numberTable461 NaN NumberConstructor::NaNValue DontEnum|DontDelete|ReadOnly462 NEGATIVE_INFINITY NumberConstructor::NegInfinity DontEnum|DontDelete|ReadOnly463 POSITIVE_INFINITY NumberConstructor::PosInfinity DontEnum|DontDelete|ReadOnly464 MAX_VALUE NumberConstructor::MaxValue DontEnum|DontDelete|ReadOnly465 MIN_VALUE NumberConstructor::MinValue DontEnum|DontDelete|ReadOnly466 @end467 */468 NumberConstructor::NumberConstructor(ExecState* exec, FunctionPrototype* funcProto, NumberPrototype* numberProto)469 : InternalFunction(funcProto, Identifier(exec, numberProto->info.className))470 {471 // Number.Prototype472 putDirect(exec->propertyNames().prototype, numberProto, DontEnum|DontDelete|ReadOnly);473 474 // no. of arguments for constructor475 putDirect(exec->propertyNames().length, jsNumber(exec, 1), ReadOnly | DontEnum | DontDelete);476 }477 478 bool NumberConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)479 {480 return getStaticValueSlot<NumberConstructor, InternalFunction>(exec, ExecState::numberTable(exec), this, propertyName, slot);481 }482 483 JSValue* NumberConstructor::getValueProperty(ExecState* exec, int token) const484 {485 // ECMA 15.7.3486 switch (token) {487 case NaNValue:488 return jsNaN(exec);489 case NegInfinity:490 return jsNumberCell(exec, -Inf);491 case PosInfinity:492 return jsNumberCell(exec, Inf);493 case MaxValue:494 return jsNumberCell(exec, 1.7976931348623157E+308);495 case MinValue:496 return jsNumberCell(exec, 5E-324);497 }498 ASSERT_NOT_REACHED();499 return jsNull();500 }501 502 // ECMA 15.7.1503 static JSObject* constructWithNumberConstructor(ExecState* exec, JSObject*, const ArgList& args)504 {505 NumberObject* obj = new (exec) NumberObject(exec->lexicalGlobalObject()->numberPrototype());506 double n = args.isEmpty() ? 0 : args[0]->toNumber(exec);507 obj->setInternalValue(jsNumber(exec, n));508 return obj;509 }510 511 ConstructType NumberConstructor::getConstructData(ConstructData& constructData)512 {513 constructData.native.function = constructWithNumberConstructor;514 return ConstructTypeNative;515 }516 517 // ECMA 15.7.2518 static JSValue* callNumberConstructor(ExecState* exec, JSObject*, JSValue*, const ArgList& args)519 {520 return jsNumber(exec, args.isEmpty() ? 0 : args[0]->toNumber(exec));521 }522 523 CallType NumberConstructor::getCallData(CallData& callData)524 {525 callData.native.function = callNumberConstructor;526 return CallTypeNative;527 }528 529 NumberObject* constructNumber(ExecState* exec, JSNumberCell* number)530 {531 NumberObject* obj = new (exec) NumberObject(exec->lexicalGlobalObject()->numberPrototype());532 obj->setInternalValue(number);533 return obj;534 }535 536 NumberObject* constructNumberFromImmediateNumber(ExecState* exec, JSValue* value)537 {538 NumberObject* obj = new (exec) NumberObject(exec->lexicalGlobalObject()->numberPrototype());539 obj->setInternalValue(value);540 return obj;541 }542 543 39 } // namespace KJS
Note:
See TracChangeset
for help on using the changeset viewer.