/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"vm/Compartment-inl.h"// For js::UnwrapAndTypeCheckThis #include"vm/GeckoProfiler-inl.h" #include"vm/NativeObject-inl.h" #include"vm/NumberObject-inl.h" #include"vm/StringType-inl.h"
usingnamespace js;
using mozilla::Abs; using mozilla::AsciiAlphanumericToNumber; using mozilla::IsAsciiAlphanumeric; using mozilla::IsAsciiDigit; using mozilla::MaxNumberValue; using mozilla::Maybe; using mozilla::MinNumberValue; using mozilla::NegativeInfinity; using mozilla::NumberEqualsInt32; using mozilla::PositiveInfinity; using mozilla::RangedPtr; using mozilla::Utf8AsUnsignedChars; using mozilla::Utf8Unit;
using JS::AutoCheckCannotGC; using JS::GenericNaN; using JS::ToInt16; using JS::ToInt32; using JS::ToInt64; using JS::ToInt8; using JS::ToUint16; using JS::ToUint32; using JS::ToUint64; using JS::ToUint8;
staticbool EnsureDtoaState(JSContext* cx) { if (!cx->dtoaState) {
cx->dtoaState = NewDtoaState(); if (!cx->dtoaState) { returnfalse;
}
} returntrue;
}
template <typename CharT> staticinlinevoid AssertWellPlacedNumericSeparator(const CharT* s, const CharT* start, const CharT* end) {
MOZ_ASSERT(start < end, "string is non-empty");
MOZ_ASSERT(s > start, "number can't start with a separator");
MOZ_ASSERT(s + 1 < end, "final character in a numeric literal can't be a separator");
MOZ_ASSERT(*(s + 1) != '_', "separator can't be followed by another separator");
MOZ_ASSERT(*(s - 1) != '_', "separator can't be preceded by another separator");
}
namespace {
template <typename CharT> class BinaryDigitReader { constint base; /* Base of number; must be a power of 2 */ int digit; /* Current digit value in radix given by base */ int digitMask; /* Mask to extract the next bit from digit */ const CharT* cur; /* Pointer to the remaining digits */ const CharT* start; /* Pointer to the start of the string */ const CharT* end; /* Pointer to first non-digit */
/* Return the next binary digit from the number, or -1 if done. */ int nextDigit() { if (digitMask == 0) { if (cur == end) { return -1;
}
int c = *cur++; if (c == '_') {
AssertWellPlacedNumericSeparator(cur - 1, start, end);
c = *cur++;
}
MOZ_ASSERT(IsAsciiAlphanumeric(c));
digit = AsciiAlphanumericToNumber(c);
digitMask = base >> 1;
}
int bit = (digit & digitMask) != 0;
digitMask >>= 1; return bit;
}
};
} /* anonymous namespace */
/* * The fast result might also have been inaccurate for power-of-two bases. This * happens if the addition in value * 2 + digit causes a round-down to an even * least significant mantissa bit when the first dropped bit is a one. If any * of the following digits in the number (which haven't been added in yet) are * nonzero, then the correct action would have been to round up instead of * down. An example occurs when reading the number 0x1000000000000081, which * rounds to 0x1000000000000000 instead of 0x1000000000000100.
*/ template <typename CharT> staticdouble ComputeAccurateBinaryBaseInteger(const CharT* start, const CharT* end, int base) {
BinaryDigitReader<CharT> bdr(base, start, end);
/* Skip leading zeroes. */ int bit; do {
bit = bdr.nextDigit();
} while (bit == 0);
MOZ_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
/* Gather the 53 significant bits (including the leading 1). */ double value = 1.0; for (int j = 52; j > 0; j--) {
bit = bdr.nextDigit(); if (bit < 0) { return value;
}
value = value * 2 + bit;
}
/* bit2 is the 54th bit (the first dropped from the mantissa). */ int bit2 = bdr.nextDigit(); if (bit2 >= 0) { double factor = 2.0; int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ int bit3;
while ((bit3 = bdr.nextDigit()) >= 0) {
sticky |= bit3;
factor *= 2;
}
value += bit2 & (bit | sticky);
value *= factor;
}
return value;
}
template <typename CharT> double js::ParseDecimalNumber(const mozilla::Range<const CharT> chars) {
MOZ_ASSERT(chars.length() > 0);
uint64_t dec = 0;
RangedPtr<const CharT> s = chars.begin(), end = chars.end(); do {
CharT c = *s;
MOZ_ASSERT('0' <= c && c <= '9');
uint8_t digit = c - '0';
uint64_t next = dec * 10 + digit;
MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT, "next value won't be an integrally-precise double");
dec = next;
} while (++s < end); returnstatic_cast<double>(dec);
}
/* If we haven't reached the limit of integer precision, we're done. */ if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) { returntrue;
}
/* * Otherwise compute the correct integer from the prefix of valid digits * if we're computing for base ten or a power of two. Don't worry about * other bases; see ES2018, 18.2.5 `parseInt(string, radix)`, step 13.
*/ if (base == 10) { returnfalse;
}
// Can only fail for base 10.
MOZ_ASSERT(base == 10);
// If we're accumulating a decimal number and the number is >= 2^53, then the // fast result from the loop in GetPrefixIntegerImpl may be inaccurate. Call // GetDecimal to get the correct answer. return GetDecimal(start, *endp, dp);
}
double d = 0.0; for (const CharT* s = start; s < end; s++) {
CharT c = *s; if (c == '_') {
AssertWellPlacedNumericSeparator(s, start, end); continue;
}
MOZ_ASSERT(IsAsciiDigit(c)); int digit = c - '0';
d = d * 10 + digit;
}
// If we haven't reached the limit of integer precision, we're done. if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
*dp = d; returntrue;
}
// Otherwise compute the correct integer using GetDecimal. return GetDecimal(start, end, dp);
}
// If there are no underscores, we don't need to copy the chars. bool hasUnderscore = std::any_of(start, end, [](auto c) { return c == '_'; }); if (!hasUnderscore) { if constexpr (std::is_same_v<CharT, char16_t>) {
*dp = convert(reinterpret_cast<const uc16*>(start), length);
} else {
static_assert(std::is_same_v<CharT, Latin1Char>);
*dp = convert(reinterpret_cast<constchar*>(start), length);
} returntrue;
}
Vector<char, 32, SystemAllocPolicy> chars; if (!chars.growByUninitialized(length)) { returnfalse;
}
const CharT* s = start;
size_t i = 0; for (; s < end; s++) {
CharT c = *s; if (c == '_') {
AssertWellPlacedNumericSeparator(s, start, end); continue;
}
MOZ_ASSERT(IsAsciiDigit(c) || c == '.' || c == 'e' || c == 'E' ||
c == '+' || c == '-');
chars[i++] = char(c);
}
/* * Step 1 is |inputString = ToString(string)|. When string >= * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of * the word, which would mean the result of parseInt(string) should be |N|. * * To preserve this behaviour, we can't use the fast-path when string >= * 1e21, or else the result would be |NeM|. * * The same goes for values smaller than 1.0e-6, because the string would be * in the form of "Ne-M".
*/ if (args[0].isDouble()) { double d = args[0].toDouble(); if (DOUBLE_DECIMAL_IN_SHORTEST_LOW <= d &&
d < DOUBLE_DECIMAL_IN_SHORTEST_HIGH) {
args.rval().setNumber(floor(d)); returntrue;
} if (-DOUBLE_DECIMAL_IN_SHORTEST_HIGH < d &&
d <= -DOUBLE_DECIMAL_IN_SHORTEST_LOW) {
args.rval().setNumber(-floor(-d)); returntrue;
} if (d == 0.0) {
args.rval().setInt32(0); returntrue;
}
}
if (args[0].isString()) {
JSString* str = args[0].toString(); if (str->hasIndexValue()) {
args.rval().setNumber(str->getIndexValue()); returntrue;
}
}
}
// On-off helper function for the self-hosted Number_toLocaleString method. // This only exists to produce an error message with the right method name. bool js::ThisNumberValueForToLocaleString(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
double d; if (!ThisNumberValue(cx, args, "toLocaleString", &d)) { returnfalse;
}
// Subtract one from DTOSTR_STANDARD_BUFFER_SIZE to exclude the null-character.
static_assert(
double_conversion::DoubleToStringConverter::kMaxCharsEcmaScriptShortest ==
DTOSTR_STANDARD_BUFFER_SIZE - 1, "double_conversion and dtoa both agree how large the longest string " "can be");
static_assert(DTOSTR_STANDARD_BUFFER_SIZE <= JS::MaximumNumberToStringLength, "MaximumNumberToStringLength is large enough to hold the longest " "string produced by a conversion");
/* Returns the number of digits written. */ template <typename T, size_t Base, size_t Length> static size_t Int32ToCString(char (&out)[Length], T i) { // The buffer needs to be large enough to hold the largest number, including // the sign and the terminating null-character. if constexpr (Base == 10) {
static_assert(std::numeric_limits<T>::digits10 + 1 + std::is_signed_v<T> <
Length);
} else { // Compute digits16 analog to std::numeric_limits::digits10, which is // defined as |std::numeric_limits::digits * std::log10(2)| for integer // types. // Note: log16(2) is 1/4.
static_assert(Base == 16);
static_assert(((std::numeric_limits<T>::digits + std::is_signed_v<T>) / 4 +
std::is_signed_v<T>) < Length);
}
// -1 to leave space for the terminating null-character. auto result = std::to_chars(out, std::end(out) - 1, i, Base);
MOZ_ASSERT(result.ec == std::errc());
// Null-terminate the result.
*result.ptr = '\0';
return result.ptr - out;
}
/* Returns the number of digits written. */ template <typename T, size_t Base = 10> static size_t Int32ToCString(ToCStringBuf* cbuf, T i) { return Int32ToCString<T, Base>(cbuf->sbuf, i);
}
/* Returns the number of digits written. */ template <typename T, size_t Base = 10> static size_t Int32ToCString(Int32ToCStringBuf* cbuf, T i) { return Int32ToCString<T, Base>(cbuf->sbuf, i);
}
double d; if (!ThisNumberValue(cx, args, "toLocaleString", &d)) { returnfalse;
}
RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10)); if (!str) { returnfalse;
}
/* * Create the string, move back to bytes to make string twiddling * a bit easier and so we can insert platform charset seperators.
*/
UniqueChars numBytes = EncodeAscii(cx, str); if (!numBytes) { returnfalse;
} constchar* num = numBytes.get(); if (!num) { returnfalse;
}
/* * Find the first non-integer value, whether it be a letter as in * 'Infinity', a decimal point, or an 'e' from exponential notation.
*/ constchar* nint = num; if (*nint == '-') {
nint++;
} while (*nint >= '0' && *nint <= '9') {
nint++;
} int digits = nint - num; constchar* end = num + digits; if (!digits) {
args.rval().setString(str); returntrue;
}
/* Figure out how long resulting string will be. */ int buflen = strlen(num); if (*nint == '.') {
buflen += decimalLength - 1; /* -1 to account for existing '.' */
}
constchar* numGrouping; constchar* tmpGroup;
numGrouping = tmpGroup = rt->numGrouping; int remainder = digits; if (*num == '-') {
remainder--;
}
// Steps 7-10 for very large numbers. if (d <= -1e21 || d >= 1e+21) {
JSString* s = NumberToString<CanGC>(cx, d); if (!s) { returnfalse;
}
args.rval().setString(s); returntrue;
}
// Steps 7-12.
// DoubleToStringConverter::ToFixed is documented as requiring a buffer size // of: // // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint + 1 // (one additional character for the sign, one for the decimal point, // and one for the null terminator) // // We already ensured there are at most 21 digits before the point, and // MAX_PRECISION digits after the point.
static_assert(1 + 21 + 1 + MAX_PRECISION + 1 <= DoubleToStrResultBufSize);
// The double-conversion library by default has a kMaxFixedDigitsAfterPoint of // 60. Assert our modified version supports at least MAX_PRECISION (100). using DToSConverter = double_conversion::DoubleToStringConverter;
static_assert(DToSConverter::kMaxFixedDigitsAfterPoint >= MAX_PRECISION);
// Step 5. int precision = 0; if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) { returnfalse;
}
// Steps 6-15.
// DoubleToStringConverter::ToExponential is documented as adding at most 8 // characters on top of the requested digits: "the sign, the digit before the // decimal point, the decimal point, the exponent character, the exponent's // sign, and at most 3 exponent digits". In addition, the buffer must be able // to hold the trailing '\0' character.
static_assert(MAX_PRECISION + 8 + 1 <= DoubleToStrResultBufSize);
// Step 5. int precision = 0; if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision)) { returnfalse;
}
// Steps 6-14.
// DoubleToStringConverter::ToPrecision is documented as adding at most 7 // characters on top of the requested digits: "the sign, the decimal point, // the exponent character, the exponent's sign, and at most 3 exponent // digits". In addition, the buffer must be able to hold the trailing '\0' // character.
static_assert(MAX_PRECISION + 7 + 1 <= DoubleToStrResultBufSize);
bool js::InitRuntimeNumberState(JSRuntime* rt) { // XXX If JS_HAS_INTL_API becomes true all the time at some point, // js::InitRuntimeNumberState is no longer fallible, and we should // change its return type. #if !JS_HAS_INTL_API /* Copy locale-specific separators into the runtime strings. */ constchar* thousandsSeparator; constchar* decimalPoint; constchar* grouping; # ifdef HAVE_LOCALECONV struct lconv* locale = localeconv();
thousandsSeparator = locale->thousands_sep;
decimalPoint = locale->decimal_point;
grouping = locale->grouping; # else
thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
decimalPoint = getenv("LOCALE_DECIMAL_POINT");
grouping = getenv("LOCALE_GROUPING"); # endif if (!thousandsSeparator) {
thousandsSeparator = "'";
} if (!decimalPoint) {
decimalPoint = ".";
} if (!grouping) {
grouping = "\3\0";
}
/* * We use single malloc to get the memory for all separator and grouping * strings.
*/
size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
size_t decimalPointSize = strlen(decimalPoint) + 1;
size_t groupingSize = strlen(grouping) + 1;
if (!JS_DefineFunctions(cx, global, number_functions)) { returnfalse;
}
// Number.parseInt should be the same function object as global parseInt.
RootedId parseIntId(cx, NameToId(cx->names().parseInt));
JSFunction* parseInt =
DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING); if (!parseInt) { returnfalse;
}
parseInt->setJitInfo(&jit::JitInfo_NumberParseInt);
// Number.parseFloat should be the same function object as global // parseFloat.
RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
num_parseFloat, 1, JSPROP_RESOLVING); if (!parseFloat) { returnfalse;
}
RootedValue parseFloatValue(cx, ObjectValue(*parseFloat)); if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) { returnfalse;
}
/* * This is V8's implementation of the algorithm described in the * following paper: * * Printing floating-point numbers quickly and accurately with integers. * Florian Loitsch, PLDI 2010.
*/ const double_conversion::DoubleToStringConverter& converter =
double_conversion::DoubleToStringConverter::EcmaScriptConverter();
double_conversion::StringBuilder builder(cbuf->sbuf, std::size(cbuf->sbuf));
MOZ_ALWAYS_TRUE(converter.ToShortest(d, &builder));
size_t len = builder.position(); #ifdef DEBUG char* result = #endif
builder.Finalize();
MOZ_ASSERT(cbuf->sbuf == result); return len;
}
auto& dtoaCache = cx->realm()->dtoaCache; double d = i; if (JSLinearString* str = dtoaCache.lookup(base, d)) { return str;
}
// Plus two to include the largest number and the sign.
constexpr size_t MaximumLength = std::numeric_limits<int32_t>::digits + 2;
char buf[MaximumLength] = {};
// Use explicit cases for base 10 and base 16 to make it more likely the // compiler will generate optimized code for these two common bases.
std::to_chars_result result; switch (base) { case 10: {
result = std::to_chars(buf, std::end(buf), i, 10); break;
} case 16: {
result = std::to_chars(buf, std::end(buf), i, 16); break;
} default: {
MOZ_ASSERT(base >= 2 && base <= 36);
result = std::to_chars(buf, std::end(buf), i, base); break;
}
}
MOZ_ASSERT(result.ec == std::errc());
JSLinearString* s; if (base == 10) { // We use a faster algorithm for base 10.
ToCStringBuf cbuf;
size_t numStrLen = FracNumberToCString(&cbuf, d);
MOZ_ASSERT(numStrLen == strlen(cbuf.sbuf));
s = NewStringCopyN<allowGC>(cx, cbuf.sbuf, numStrLen); if (!s) { return nullptr;
}
} else { if (!EnsureDtoaState(cx)) { if constexpr (allowGC) {
ReportOutOfMemory(cx);
} return nullptr;
}
UniqueChars numStr(js_dtobasestr(cx->dtoaState, base, d)); if (!numStr) { if constexpr (allowGC) {
ReportOutOfMemory(cx);
} return nullptr;
}
s = NewStringCopyZ<allowGC>(cx, numStr.get()); if (!s) { return nullptr;
}
}
template <typename CharT> inlinedouble CharToNumber(CharT c) { if ('0' <= c && c <= '9') { return c - '0';
} if (unicode::IsSpace(c)) { return 0.0;
} return GenericNaN();
}
// It's probably a non-decimal number. Accept if there's at least one digit // after the 0b|0o|0x, and if no non-whitespace characters follow all the // digits. const CharT* endptr; double d;
MOZ_ALWAYS_TRUE(GetPrefixIntegerImpl(
start + 2, end, radix, IntegerSeparatorHandling::None, &endptr, &d)); if (endptr == start + 2 || SkipSpace(endptr, end) != end) {
*result = GenericNaN();
} else {
*result = d;
} returntrue;
}
/* * Note that ECMA doesn't treat a string beginning with a '0' as * an octal number here. This works because all such numbers will * be interpreted as decimal by js_strtod. Also, any hex numbers * that have made it here (which can only be negative ones) will * be treated as 0 without consuming the 'x' by js_strtod.
*/ const CharT* ep; double d = js_strtod(start, end, &ep); if (SkipSpace(ep, end) != end) { return GenericNaN();
} return d;
}
// Step 1. if (!vp.isPrimitive()) { if (!ToPrimitive(cx, JSTYPE_NUMBER, vp)) { returnfalse;
}
}
// Step 2. if (vp.isBigInt()) { returntrue;
}
// Step 3. return ToNumber(cx, vp);
}
/* * Convert a value to an int8_t, according to the WebIDL rules for byte * conversion. Return converted value in *out on success, false on failure.
*/
JS_PUBLIC_API bool js::ToInt8Slow(JSContext* cx, const HandleValue v,
int8_t* out) {
MOZ_ASSERT(!v.isInt32()); double d; if (v.isDouble()) {
d = v.toDouble();
} else { if (!ToNumberSlow(cx, v, &d)) { returnfalse;
}
}
*out = ToInt8(d); returntrue;
}
/* * Convert a value to an uint8_t, according to the ToUInt8() function in ES6 * ECMA-262, 7.1.10. Return converted value in *out on success, false on * failure.
*/
JS_PUBLIC_API bool js::ToUint8Slow(JSContext* cx, const HandleValue v,
uint8_t* out) {
MOZ_ASSERT(!v.isInt32()); double d; if (v.isDouble()) {
d = v.toDouble();
} else { if (!ToNumberSlow(cx, v, &d)) { returnfalse;
}
}
*out = ToUint8(d); returntrue;
}
/* * Convert a value to an int16_t, according to the WebIDL rules for short * conversion. Return converted value in *out on success, false on failure.
*/
JS_PUBLIC_API bool js::ToInt16Slow(JSContext* cx, const HandleValue v,
int16_t* out) {
MOZ_ASSERT(!v.isInt32()); double d; if (v.isDouble()) {
d = v.toDouble();
} else { if (!ToNumberSlow(cx, v, &d)) { returnfalse;
}
}
*out = ToInt16(d); returntrue;
}
/* * Convert a value to an int64_t, according to the WebIDL rules for long long * conversion. Return converted value in *out on success, false on failure.
*/
JS_PUBLIC_API bool js::ToInt64Slow(JSContext* cx, const HandleValue v,
int64_t* out) {
MOZ_ASSERT(!v.isInt32()); double d; if (v.isDouble()) {
d = v.toDouble();
} else { if (!ToNumberSlow(cx, v, &d)) { returnfalse;
}
}
*out = ToInt64(d); returntrue;
}
/* * Convert a value to an uint64_t, according to the WebIDL rules for unsigned * long long conversion. Return converted value in *out on success, false on * failure.
*/
JS_PUBLIC_API bool js::ToUint64Slow(JSContext* cx, const HandleValue v,
uint64_t* out) {
MOZ_ASSERT(!v.isInt32()); double d; if (v.isDouble()) {
d = v.toDouble();
} else { if (!ToNumberSlow(cx, v, &d)) { returnfalse;
}
}
*out = ToUint64(d); returntrue;
}
if (processed > 0) {
*dEnd = s + processed; return d;
}
}
// Try to parse +Infinity, -Infinity or Infinity. Note that we do this here // instead of using StringToDoubleConverter's infinity_symbol because it's // faster: the code below is less generic and not on the fast path for regular // doubles. static constexpr std::string_view Infinity = "Infinity"; if (length >= Infinity.length()) { const CharT* afterSign = s; bool negative = (*afterSign == '-'); if (negative || *afterSign == '+') {
afterSign++;
}
MOZ_ASSERT(afterSign < end); if (*afterSign == 'I' && size_t(end - afterSign) >= Infinity.length() &&
EqualChars(afterSign, Infinity.data(), Infinity.length())) {
*dEnd = afterSign + Infinity.length(); return negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.