/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * 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/. */ #ifndef jsnum_h #define jsnum_h #include "mozilla/FloatingPoint.h" #include "mozilla/Range.h" #include "NamespaceImports.h" #include "js/Conversions.h" // This macro is should be `one' if current compiler supports builtin functions // like __builtin_sadd_overflow. #if __GNUC__ >= 5 // GCC 5 and above supports these functions. #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1 #else // For CLANG, we use its own function to check for this. #ifdef __has_builtin #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x) #endif #endif #ifndef BUILTIN_CHECKED_ARITHMETIC_SUPPORTED #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 0 #endif namespace js { class StringBuffer; void InitRuntimeNumberState(JSRuntime* rt); /* Initialize the Number class, returning its prototype object. */ extern JSObject* InitNumberClass(JSContext* cx, HandleObject obj); } /* namespace js */ /* * String constants for global function names, used in jsapi.c and jsnum.c. */ extern const char js_isNaN_str[]; extern const char js_isFinite_str[]; extern const char js_parseFloat_str[]; extern const char js_parseInt_str[]; class JSAtom; namespace js { /* * When base == 10, this function implements ToString() as specified by * ECMA-262-5 section 9.8.1; but note that it handles integers specially for * performance. See also js::NumberToCString(). */ template extern JSString* NumberToString(ExclusiveContext* cx, double d); extern JSAtom* NumberToAtom(ExclusiveContext* cx, double d); template extern JSFlatString* Int32ToString(ExclusiveContext* cx, int32_t i); extern JSAtom* Int32ToAtom(ExclusiveContext* cx, int32_t si); // ES6 15.7.3.12 extern bool IsInteger(const Value& val); /* * Convert an integer or double (contained in the given value) to a string and * append to the given buffer. */ [[nodiscard]] extern bool JS_FASTCALL NumberValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb); /* Same as js_NumberToString, different signature. */ extern JSFlatString* NumberToString(JSContext* cx, double d); extern JSFlatString* IndexToString(JSContext* cx, uint32_t index); /* * Usually a small amount of static storage is enough, but sometimes we need * to dynamically allocate much more. This struct encapsulates that. * Dynamically allocated memory will be freed when the object is destroyed. */ struct ToCStringBuf { /* * The longest possible result that would need to fit in sbuf is * (-0x80000000).toString(2), which has length 33. Longer cases are * possible, but they'll go in dbuf. */ static const size_t sbufSize = 34; char sbuf[sbufSize]; char* dbuf; ToCStringBuf(); ~ToCStringBuf(); }; /* * Convert a number to a C string. When base==10, this function implements * ToString() as specified by ECMA-262-5 section 9.8.1. It handles integral * values cheaply. Return nullptr if we ran out of memory. See also * NumberToCString(). */ extern char* NumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d, int base = 10); /* * The largest positive integer such that all positive integers less than it * may be precisely represented using the IEEE-754 double-precision format. */ const double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64_t(1) << 53; /* * Parse a decimal number encoded in |chars|. The decimal number must be * sufficiently small that it will not overflow the integrally-precise range of * the double type -- that is, the number will be smaller than * DOUBLE_INTEGRAL_PRECISION_LIMIT */ template extern double ParseDecimalNumber(const mozilla::Range chars); /* Separator handling for integers. Either skip underscores for numerical * separators (ES2021) or not */ enum class PrefixIntegerSeparatorHandling : bool { None, SkipUnderscore }; /* * Compute the positive integer of the given base described immediately at the * start of the range [start, end] -- no whitespace-skipping, no magical * leading-"0" octal or leading-"0x" hex behavior, no "+"/"-" parsing, just * reading the digits of the integer. Return the index one past the end of the * digits of the integer in *endp, and return the integer itself in *dp. If * base is 10 or a power of two the returned integer is the closest possible * double; otherwise extremely large integers may be slightly inaccurate. * * The |separatorHandling| controls whether or not numeric separators can be * part of an integer string. If the option is enabled, all individual '_' * characters in the string are ignored. * * If [start, end] does not begin with a number with the specified base, * then upon return *dp == 0 and *endp == start. */ template [[nodiscard]] extern bool GetPrefixInteger(ExclusiveContext* cx, const CharT* start, const CharT* end, int base, PrefixIntegerSeparatorHandling separatorHandling, const CharT** endp, double* dp); /* * This is like GetPrefixInteger, but only deals with base 10, always ignores * '_', and doesn't have an |endp| outparam. It should only be used when the * characters are known to only contain digits and '_'. */ [[nodiscard]] extern bool GetDecimalInteger(ExclusiveContext* cx, const char16_t* start, const char16_t* end, double* dp); /* * This is like GetDecimalInteger, but also allows non-integer numbers. It * should only be used when the characters are known to only contain digits, * '.', 'e' or 'E', '+' or '-', and '_'. */ [[nodiscard]] extern bool GetDecimalNonInteger(ExclusiveContext* cx, const char16_t* start, const char16_t* end, double* dp); [[nodiscard]] extern bool StringToNumber(ExclusiveContext* cx, JSString* str, double* result); /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */ [[nodiscard]] MOZ_ALWAYS_INLINE bool ToNumber(JSContext* cx, JS::MutableHandleValue vp) { if (vp.isNumber()) return true; double d; extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, HandleValue v, double* dp); if (!ToNumberSlow(cx, vp, &d)) return false; vp.setNumber(d); return true; } [[nodiscard]] bool num_parseInt(JSContext* cx, unsigned argc, Value* vp); } /* namespace js */ /* * Similar to strtod except that it replaces overflows with infinities of the * correct sign, and underflows with zeros of the correct sign. Guaranteed to * return the closest double number to the given input in dp. * * Also allows inputs of the form [+|-]Infinity, which produce an infinity of * the appropriate sign. The case of the "Infinity" string must match exactly. * If the string does not contain a number, set *dEnd to begin and return 0.0 * in *d. * * Return false if out of memory. */ template [[nodiscard]] extern bool js_strtod(js::ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd, double* d); namespace js { [[nodiscard]] extern bool num_toString(JSContext* cx, unsigned argc, Value* vp); [[nodiscard]] extern bool num_valueOf(JSContext* cx, unsigned argc, Value* vp); static MOZ_ALWAYS_INLINE bool ValueFitsInInt32(const Value& v, int32_t* pi) { if (v.isInt32()) { *pi = v.toInt32(); return true; } return v.isDouble() && mozilla::NumberIsInt32(v.toDouble(), pi); } /* * Returns true if the given value is definitely an index: that is, the value * is a number that's an unsigned 32-bit integer. * * This method prioritizes common-case speed over accuracy in every case. It * can produce false negatives (but not false positives): some values which are * indexes will be reported not to be indexes by this method. Users must * consider this possibility when using this method. */ static MOZ_ALWAYS_INLINE bool IsDefinitelyIndex(const Value& v, uint32_t* indexp) { if (v.isInt32() && v.toInt32() >= 0) { *indexp = v.toInt32(); return true; } int32_t i; if (v.isDouble() && mozilla::NumberIsInt32(v.toDouble(), &i) && i >= 0) { *indexp = uint32_t(i); return true; } return false; } /* ES5 9.4 ToInteger. */ [[nodiscard]] static inline bool ToInteger(JSContext* cx, HandleValue v, double* dp) { if (v.isInt32()) { *dp = v.toInt32(); return true; } if (v.isDouble()) { *dp = v.toDouble(); } else { extern JS_PUBLIC_API(bool) ToNumberSlow(JSContext* cx, HandleValue v, double* dp); if (!ToNumberSlow(cx, v, dp)) return false; } *dp = JS::ToInteger(*dp); return true; } /* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range. If the * return value is false then *overflow will be true iff the value was * not clampable to uint32_t range. * * For JSContext and ExclusiveContext. */ template [[nodiscard]] bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow); /* Convert and range check an index value as for DataView, SIMD, and Atomics * operations, eg ES7 24.2.1.1, DataView's GetViewValue(): * * 1. numericIndex = ToNumber(argument) (may throw TypeError) * 2. intIndex = ToInteger(numericIndex) * 3. if intIndex != numericIndex throw RangeError * * This function additionally bounds the range to the non-negative contiguous * integers: * * 4. if intIndex < 0 or intIndex > 2^53 throw RangeError * * Return true and set |*index| to the integer value if |argument| is a valid * array index argument. Otherwise report an TypeError or RangeError and return * false. * * The returned index will always be in the range 0 <= *index <= 2^53. */ [[nodiscard]] bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); /* ES2017 draft 7.1.17 ToIndex * * Return true and set |*index| to the integer value if |v| is a valid * integer index value. Otherwise report a RangeError and return false. * * The returned index will always be in the range 0 <= *index <= 2^53-1. */ [[nodiscard]] bool ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); [[nodiscard]] inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) { #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow) // Using compiler's builtin function. return !__builtin_sadd_overflow(one, two, res); #else // Use unsigned for the 32-bit operation since signed overflow gets // undefined behavior. *res = uint32_t(one) + uint32_t(two); int64_t ores = (int64_t)one + (int64_t)two; return ores == (int64_t)*res; #endif } [[nodiscard]] inline bool SafeSub(int32_t one, int32_t two, int32_t* res) { #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_ssub_overflow) return !__builtin_ssub_overflow(one, two, res); #else *res = uint32_t(one) - uint32_t(two); int64_t ores = (int64_t)one - (int64_t)two; return ores == (int64_t)*res; #endif } [[nodiscard]] inline bool SafeMul(int32_t one, int32_t two, int32_t* res) { #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_smul_overflow) return !__builtin_smul_overflow(one, two, res); #else *res = uint32_t(one) * uint32_t(two); int64_t ores = (int64_t)one * (int64_t)two; return ores == (int64_t)*res; #endif } [[nodiscard]] extern bool ToNumberSlow(ExclusiveContext* cx, HandleValue v, double* dp); // Variant of ToNumber which takes an ExclusiveContext instead of a JSContext. // ToNumber is part of the API and can't use ExclusiveContext directly. [[nodiscard]] MOZ_ALWAYS_INLINE bool ToNumber(ExclusiveContext* cx, HandleValue v, double* out) { if (v.isNumber()) { *out = v.toNumber(); return true; } return ToNumberSlow(cx, v, out); } bool ToNumericSlow(ExclusiveContext* cx, JS::MutableHandleValue vp); // BigInt proposal section 3.1.6 [[nodiscard]] MOZ_ALWAYS_INLINE bool ToNumeric(ExclusiveContext* cx, JS::MutableHandleValue vp) { if (vp.isNumeric()) { return true; } return ToNumericSlow(cx, vp); } bool ToInt32OrBigIntSlow(JSContext* cx, JS::MutableHandleValue vp); [[nodiscard]] MOZ_ALWAYS_INLINE bool ToInt32OrBigInt(JSContext* cx, JS::MutableHandleValue vp) { if (vp.isInt32()) { return true; } return ToInt32OrBigIntSlow(cx, vp); } void FIX_FPU(); } /* namespace js */ #endif /* jsnum_h */