NumberPrototype.cpp [plain text]
#include "config.h"
#include "NumberPrototype.h"
#include "BigInteger.h"
#include "Error.h"
#include "JSFunction.h"
#include "JSString.h"
#include "Operations.h"
#include "Uint16WithFraction.h"
#include "dtoa.h"
#include <wtf/Assertions.h>
#include <wtf/DecimalNumber.h>
#include <wtf/MathExtras.h>
#include <wtf/Vector.h>
namespace JSC {
static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*);
static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*);
static EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState*);
static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*);
static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*);
static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*);
}
#include "NumberPrototype.lut.h"
namespace JSC {
const ClassInfo NumberPrototype::s_info = { "Number", &NumberObject::s_info, 0, ExecState::numberPrototypeTable };
ASSERT_CLASS_FITS_IN_CELL(NumberPrototype);
NumberPrototype::NumberPrototype(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
: NumberObject(exec->globalData(), structure)
{
setInternalValue(exec->globalData(), jsNumber(0));
ASSERT(inherits(&s_info));
putAnonymousValue(globalObject->globalData(), 0, globalObject);
}
bool NumberPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot &slot)
{
return getStaticFunctionSlot<NumberObject>(exec, ExecState::numberPrototypeTable(exec), this, propertyName, slot);
}
bool NumberPrototype::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
{
return getStaticFunctionDescriptor<NumberObject>(exec, ExecState::numberPrototypeTable(exec), this, propertyName, descriptor);
}
static ALWAYS_INLINE bool toThisNumber(JSValue thisValue, double &x)
{
JSValue v = thisValue.getJSNumber();
if (UNLIKELY(!v))
return false;
x = v.uncheckedGetNumber();
return true;
}
static ALWAYS_INLINE bool getIntegerArgumentInRange(ExecState* exec, int low, int high, int& result, bool& isUndefined)
{
result = 0;
isUndefined = false;
JSValue argument0 = exec->argument(0);
if (argument0.isUndefined()) {
isUndefined = true;
return true;
}
double asDouble = argument0.toInteger(exec);
if (asDouble < low || asDouble > high)
return false;
result = static_cast<int>(asDouble);
return true;
}
typedef char RadixBuffer[2180];
static const char* const radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz";
static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix)
{
ASSERT(isfinite(number));
ASSERT(radix >= 2 && radix <= 36);
char* decimalPoint = buffer + sizeof(buffer) / 2;
char* startOfResultString = decimalPoint;
bool isNegative = number < 0;
if (signbit(number))
number = -number;
double integerPart = floor(number);
bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1;
ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2)));
bool isOddInOddRadix = integerPartIsOdd;
uint32_t digit = integerPartIsOdd;
double fractionPart = number - integerPart;
if (fractionPart) {
*decimalPoint = '.';
Uint16WithFraction fraction(fractionPart);
bool needsRoundingUp = false;
char* endOfResultString = decimalPoint + 1;
double nextNumber = nextafter(number, std::numeric_limits<double>::infinity());
double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity());
ASSERT(isfinite(nextNumber) && !signbit(nextNumber));
ASSERT(isfinite(lastNumber) && !signbit(lastNumber));
double deltaNextDouble = nextNumber - number;
double deltaLastDouble = number - lastNumber;
ASSERT(isfinite(deltaNextDouble) && !signbit(deltaNextDouble));
ASSERT(isfinite(deltaLastDouble) && !signbit(deltaLastDouble));
if (deltaNextDouble != deltaLastDouble) {
Uint16WithFraction halfDeltaNext(deltaNextDouble, 1);
Uint16WithFraction halfDeltaLast(deltaLastDouble, 1);
while (true) {
int dComparePoint5 = fraction.comparePoint5();
if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
if (fraction.sumGreaterThanOne(halfDeltaNext)) {
needsRoundingUp = true;
break;
}
} else {
if (fraction < halfDeltaLast)
break;
}
ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
fraction *= radix;
digit = fraction.floorAndSubtract();
*endOfResultString++ = radixDigits[digit];
if (digit & 1)
isOddInOddRadix = !isOddInOddRadix;
halfDeltaNext *= radix;
halfDeltaLast *= radix;
}
} else {
Uint16WithFraction halfDelta(deltaNextDouble, 1);
while (true) {
int dComparePoint5 = fraction.comparePoint5();
if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
if (fraction.sumGreaterThanOne(halfDelta)) {
needsRoundingUp = true;
break;
}
} else if (fraction < halfDelta)
break;
ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
fraction *= radix;
digit = fraction.floorAndSubtract();
if (digit & 1)
isOddInOddRadix = !isOddInOddRadix;
*endOfResultString++ = radixDigits[digit];
halfDelta *= radix;
}
}
if (needsRoundingUp) {
while (endOfResultString[-1] == radixDigits[radix - 1])
--endOfResultString;
if (endOfResultString[-1] == '9')
endOfResultString[-1] = 'a';
else if (endOfResultString[-1] != '.')
++endOfResultString[-1];
else {
--endOfResultString;
ASSERT((integerPart + 1) - integerPart == 1);
++integerPart;
}
} else {
while (endOfResultString[-1] == '0')
--endOfResultString;
}
*endOfResultString = '\0';
ASSERT(endOfResultString < buffer + sizeof(buffer));
} else
*decimalPoint = '\0';
BigInteger units(integerPart);
do {
ASSERT(buffer < startOfResultString);
digit = units.divide(radix);
*--startOfResultString = radixDigits[digit];
} while (!!units);
if (isNegative)
*--startOfResultString = '-';
ASSERT(buffer <= startOfResultString);
return startOfResultString;
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState* exec)
{
double x;
if (!toThisNumber(exec->hostThisValue(), x))
return throwVMTypeError(exec);
int decimalPlacesInExponent;
bool isUndefined;
if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlacesInExponent, isUndefined))
return throwVMError(exec, createRangeError(exec, "toExponential() argument must be between 0 and 20"));
if (!isfinite(x))
return JSValue::encode(jsString(exec, UString::number(x)));
NumberToStringBuffer buffer;
unsigned length = isUndefined
? DecimalNumber(x).toStringExponential(buffer, WTF::NumberToStringBufferLength)
: DecimalNumber(x, RoundingSignificantFigures, decimalPlacesInExponent + 1).toStringExponential(buffer, WTF::NumberToStringBufferLength);
return JSValue::encode(jsString(exec, UString(buffer, length)));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSValue v = thisValue.getJSNumber();
if (!v)
return throwVMTypeError(exec);
double x = v.uncheckedGetNumber();
int decimalPlaces;
bool isUndefined; if (!getIntegerArgumentInRange(exec, 0, 20, decimalPlaces, isUndefined))
return throwVMError(exec, createRangeError(exec, "toFixed() argument must be between 0 and 20"));
if (!(fabs(x) < 1e+21))
return JSValue::encode(jsString(exec, UString::number(x)));
ASSERT(isfinite(x));
NumberToStringBuffer buffer;
unsigned length = DecimalNumber(x, RoundingDecimalPlaces, decimalPlaces).toStringDecimal(buffer, WTF::NumberToStringBufferLength);
return JSValue::encode(jsString(exec, UString(buffer, length)));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSValue v = thisValue.getJSNumber();
if (!v)
return throwVMTypeError(exec);
double x = v.uncheckedGetNumber();
int significantFigures;
bool isUndefined;
if (!getIntegerArgumentInRange(exec, 1, 21, significantFigures, isUndefined))
return throwVMError(exec, createRangeError(exec, "toPrecision() argument must be between 1 and 21"));
if (isUndefined)
return JSValue::encode(jsString(exec, UString::number(x)));
if (!isfinite(x))
return JSValue::encode(jsString(exec, UString::number(x)));
DecimalNumber number(x, RoundingSignificantFigures, significantFigures);
NumberToStringBuffer buffer;
unsigned length = number.exponent() >= -6 && number.exponent() < significantFigures
? number.toStringDecimal(buffer, WTF::NumberToStringBufferLength)
: number.toStringExponential(buffer, WTF::NumberToStringBufferLength);
return JSValue::encode(jsString(exec, UString(buffer, length)));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSValue v = thisValue.getJSNumber();
if (!v)
return throwVMTypeError(exec);
JSValue radixValue = exec->argument(0);
int radix;
if (radixValue.isInt32())
radix = radixValue.asInt32();
else if (radixValue.isUndefined())
radix = 10;
else
radix = static_cast<int>(radixValue.toInteger(exec));
if (radix == 10)
return JSValue::encode(jsString(exec, v.toString(exec)));
if (radix == 36) {
if (v.isInt32()) {
int x = v.asInt32();
if (static_cast<unsigned>(x) < 36) { JSGlobalData* globalData = &exec->globalData();
return JSValue::encode(globalData->smallStrings.singleCharacterString(globalData, radixDigits[x]));
}
}
}
if (radix < 2 || radix > 36)
return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36"));
double x = v.uncheckedGetNumber();
if (!isfinite(x))
return JSValue::encode(jsString(exec, UString::number(x)));
RadixBuffer s;
return JSValue::encode(jsString(exec, toStringWithRadix(s, x, radix)));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSValue v = thisValue.getJSNumber();
if (!v)
return throwVMTypeError(exec);
return JSValue::encode(jsString(exec, v.toString(exec)));
}
EncodedJSValue JSC_HOST_CALL numberProtoFuncValueOf(ExecState* exec)
{
JSValue thisValue = exec->hostThisValue();
JSValue v = thisValue.getJSNumber();
if (!v)
return throwVMTypeError(exec);
return JSValue::encode(v);
}
}