NumberPrototype.cpp [plain text]
#include "config.h"
#include "NumberPrototype.h"
#include "Error.h"
#include "JSFunction.h"
#include "JSString.h"
#include "Operations.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;
}
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 (isnan(x) || isinf(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(!isnan(x) && !isinf(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 (isnan(x) || isinf(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)));
static const char* const digits = "0123456789abcdefghijklmnopqrstuvwxyz";
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, digits[x]));
}
}
}
if (radix < 2 || radix > 36)
return throwVMError(exec, createRangeError(exec, "toString() radix argument must be between 2 and 36"));
char s[2048 + 3];
const char* lastCharInString = s + sizeof(s) - 1;
double x = v.uncheckedGetNumber();
if (isnan(x) || isinf(x))
return JSValue::encode(jsString(exec, UString::number(x)));
bool isNegative = x < 0.0;
if (isNegative)
x = -x;
double integerPart = floor(x);
char* decimalPoint = s + sizeof(s) / 2;
char* p = decimalPoint;
double d = integerPart;
do {
int remainderDigit = static_cast<int>(fmod(d, radix));
*--p = digits[remainderDigit];
d /= radix;
} while ((d <= -1.0 || d >= 1.0) && s < p);
if (isNegative)
*--p = '-';
char* startOfResultString = p;
ASSERT(s <= startOfResultString);
d = x - integerPart;
p = decimalPoint;
const double epsilon = 0.001; bool hasFractionalPart = (d < -epsilon || d > epsilon);
if (hasFractionalPart) {
*p++ = '.';
do {
d *= radix;
const int digit = static_cast<int>(d);
*p++ = digits[digit];
d -= digit;
} while ((d < -epsilon || d > epsilon) && p < lastCharInString);
}
*p = '\0';
ASSERT(p < s + sizeof(s));
return JSValue::encode(jsString(exec, startOfResultString));
}
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);
}
}