Issue #1240 - Part 3c - Fast-forward to the V8 version of BigIntType. Disabling some sections temporarily since the dependencies are not there yet. Based on the following: https://bugzilla.mozilla.org/show_bug.cgi?id=1502797 https://bugzilla.mozilla.org/show_bug.cgi?id=1471134 https://bugzilla.mozilla.org/show_bug.cgi?id=1441098 Part 3 & 4 Add structured clone support for BigInt and Enable BigInt wrapping from DOM bindings. https://bugzilla.mozilla.org/show_bug.cgi?id=1522738

This commit is contained in:
Brian Smith
2023-07-13 04:07:05 -05:00
committed by roytam1
parent d42d5ce138
commit 07b6c03dc7
12 changed files with 3586 additions and 147 deletions
+11 -4
View File
@@ -1037,11 +1037,18 @@ MaybeWrapValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
return MaybeWrapStringValue(cx, rval);
}
if (!rval.isObject()) {
return true;
if (rval.isObject()) {
return MaybeWrapObjectValue(cx, rval);
}
return MaybeWrapObjectValue(cx, rval);
// This could be optimized by checking the zone first, similar to
// the way strings are handled. At present, this is used primarily
// for structured cloning, so avoiding the overhead of JS_WrapValue
// calls is less important than for other types.
if (rval.isBigInt()) {
return JS_WrapValue(cx, rval);
}
MOZ_ASSERT(rval.isSymbol());
return true;
}
namespace binding_detail {
-1
View File
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
-1
View File
@@ -1,5 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
+1 -1
View File
@@ -19,7 +19,7 @@ FRAGMENT(jsval, simple) {
RootedString hello(cx, JS_NewStringCopyZ(cx, "Hello!"));
RootedValue friendly_string(cx, StringValue(hello));
RootedValue symbol(cx, SymbolValue(GetSymbolFor(cx, hello)));
RootedValue bi(cx, BigIntValue(BigInt::create(cx)));
RootedValue bi(cx, BigIntValue(BigInt::zero(cx)));
RootedValue global(cx);
global.setObject(*CurrentGlobalOrNull(cx));
+1
View File
@@ -623,6 +623,7 @@ MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyn
// BigInt
MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt")
MSG_DEF(JSMSG_BIGINT_TOO_LARGE, 0, JSEXN_RANGEERR, "BigInt is too large to allocate")
MSG_DEF(JSMSG_BIGINT_DIVISION_BY_ZERO, 0, JSEXN_RANGEERR, "BigInt division by zero")
MSG_DEF(JSMSG_BIGINT_NEGATIVE_EXPONENT, 0, JSEXN_RANGEERR, "BigInt negative exponent")
MSG_DEF(JSMSG_BIGINT_INVALID_SYNTAX, 0, JSEXN_SYNTAXERR, "invalid BigInt syntax")
+2 -1
View File
@@ -487,7 +487,8 @@ ToAtomSlow(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::HandleTyp
return nullptr;
}
if (v.isBigInt()) {
JSAtom* atom = BigIntToAtom(cx, v.toBigInt());
RootedBigInt i(cx, v.toBigInt());
JSAtom* atom = BigIntToAtom(cx, i);
if (!allowGC && !atom)
cx->recoverFromOutOfMemory();
return atom;
+1 -1
View File
@@ -172,7 +172,7 @@ js::ToBooleanSlow(HandleValue v)
if (v.isString())
return v.toString()->length() != 0;
if (v.isBigInt())
return v.toBigInt()->toBoolean();
return !v.toBigInt()->isZero();
MOZ_ASSERT(v.isObject());
return !EmulatesUndefined(&v.toObject());
+2 -1
View File
@@ -3737,7 +3737,8 @@ js::ToStringSlow(ExclusiveContext* cx, typename MaybeRooted<Value, allowGC>::Han
} else if (v.isBigInt()) {
if (!allowGC)
return nullptr;
str = BigInt::toString(cx, v.toBigInt(), 10);
RootedBigInt i(cx, v.toBigInt());
str = BigInt::toString(cx, i, 10);
} else {
MOZ_ASSERT(v.isUndefined());
str = cx->names().undefined;
+3147 -94
View File
File diff suppressed because it is too large Load Diff
+354 -42
View File
@@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
@@ -6,62 +6,374 @@
#ifndef vm_BigIntType_h
#define vm_BigIntType_h
#include "mozilla/Range.h"
#include "mozilla/Span.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "gc/Heap.h"
#include "js/GCHashTable.h"
#include "js/RootingAPI.h"
#include "js/TraceKind.h"
#include "js/TypeDecls.h"
#include "vm/String.h"
#include "vm/Xdr.h"
// Handle future js::gc::Cell::ReservedBits, we have no reserved bits...
#define js_gc_Cell_ReservedBits 0
// Handle future js::gc::MinCellSize, 16 bytes, twice our js:gc:CellSize
#define js_gc_MinCellSize (js::gc::CellSize*2)
namespace JS {
class BigInt final : public js::gc::TenuredCell
{
private:
// The minimum allocation size is currently 16 bytes (see
// SortedArenaList in gc/ArenaList.h).
uint8_t unused_[16 + (16 % js::gc::CellSize)];
#if 0 // Future XDR support
class BigInt;
public:
// Allocate and initialize a BigInt value
static BigInt* create(js::ExclusiveContext* cx);
static BigInt* create(js::ExclusiveContext* cx, double d);
static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
void traceChildren(JSTracer* trc);
void finalize(js::FreeOp* fop);
js::HashNumber hash();
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
bool toBoolean();
static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
static JSLinearString* toString(js::ExclusiveContext* cx, BigInt* x, uint8_t radix);
};
static_assert(sizeof(BigInt) >= js::gc::CellSize,
"sizeof(BigInt) must be greater than the minimum allocation size");
} // namespace JS
} // namespace JS
namespace js {
extern JSAtom*
BigIntToAtom(ExclusiveContext* cx, JS::BigInt* bi);
template <XDRMode mode>
XDRResult XDRBigInt(XDRState<mode>* xdr, MutableHandle<JS::BigInt*> bi);
extern JS::BigInt*
NumberToBigInt(ExclusiveContext* cx, double d);
} // namespace js
extern JS::BigInt*
ToBigInt(ExclusiveContext* cx, JS::Handle<JS::Value> v);
} // namespace js
namespace JS {
#endif
class BigInt final : public js::gc::TenuredCell {
public:
using Digit = uintptr_t;
private:
// The low js::gc::Cell::ReservedBits are reserved.
static constexpr uintptr_t SignBit = JS_BIT(js_gc_Cell_ReservedBits);
static constexpr uintptr_t LengthShift = js_gc_Cell_ReservedBits + 1;
static constexpr size_t InlineDigitsLength =
(js_gc_MinCellSize - sizeof(uintptr_t)) / sizeof(Digit);
uintptr_t lengthSignAndReservedBits_;
// The digit storage starts with the least significant digit (little-endian
// digit order). Byte order within a digit is of course native endian.
union {
Digit* heapDigits_;
Digit inlineDigits_[InlineDigitsLength];
};
public:
static const JS::TraceKind TraceKind = JS::TraceKind::BigInt;
size_t digitLength() const {
return lengthSignAndReservedBits_ >> LengthShift;
}
bool hasInlineDigits() const { return digitLength() <= InlineDigitsLength; }
bool hasHeapDigits() const { return !hasInlineDigits(); }
using Digits = mozilla::Span<Digit>;
Digits digits() {
return Digits(hasInlineDigits() ? inlineDigits_ : heapDigits_,
digitLength());
}
Digit digit(size_t idx) { return digits()[idx]; }
void setDigit(size_t idx, Digit digit) { digits()[idx] = digit; }
bool isZero() const { return digitLength() == 0; }
bool isNegative() const { return lengthSignAndReservedBits_ & SignBit; }
void initializeDigitsToZero();
void traceChildren(JSTracer* trc);
void finalize(js::FreeOp* fop);
js::HashNumber hash();
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
static BigInt* createUninitialized(js::ExclusiveContext* cx, size_t length,
bool isNegative);
static BigInt* createFromDouble(js::ExclusiveContext* cx, double d);
static BigInt* createFromUint64(js::ExclusiveContext* cx, uint64_t n);
static BigInt* createFromInt64(js::ExclusiveContext* cx, int64_t n);
// FIXME: Cache these values.
static BigInt* zero(js::ExclusiveContext* cx);
static BigInt* one(js::ExclusiveContext* cx);
static BigInt* copy(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* add(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* sub(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* mul(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* div(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* mod(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* pow(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* neg(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* lsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* rsh(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitAnd(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitXor(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitOr(js::ExclusiveContext* cx, Handle<BigInt*> x, Handle<BigInt*> y);
static BigInt* bitNot(js::ExclusiveContext* cx, Handle<BigInt*> x);
static int64_t toInt64(BigInt* x);
static uint64_t toUint64(BigInt* x);
static BigInt* asIntN(js::ExclusiveContext* cx, Handle<BigInt*> x, uint64_t bits);
static BigInt* asUintN(js::ExclusiveContext* cx, Handle<BigInt*> x, uint64_t bits);
// Type-checking versions of arithmetic operations. These methods
// must be called with at least one BigInt operand. Binary
// operations will throw a TypeError if one of the operands is not a
// BigInt value.
static bool add(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool sub(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool mul(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool div(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool mod(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool pow(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool neg(js::ExclusiveContext* cx, Handle<Value> operand,
MutableHandle<Value> res);
static bool lsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool rsh(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool bitAnd(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool bitXor(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool bitOr(js::ExclusiveContext* cx, Handle<Value> lhs, Handle<Value> rhs,
MutableHandle<Value> res);
static bool bitNot(js::ExclusiveContext* cx, Handle<Value> operand,
MutableHandle<Value> res);
static double numberValue(BigInt* x);
static JSLinearString* toString(js::ExclusiveContext* cx, Handle<BigInt*> x,
uint8_t radix);
template <typename CharT>
static BigInt* parseLiteral(js::ExclusiveContext* cx,
const mozilla::Range<const CharT> chars,
bool* haveParseError);
template <typename CharT>
static BigInt* parseLiteralDigits(js::ExclusiveContext* cx,
const mozilla::Range<const CharT> chars,
unsigned radix, bool isNegative,
bool* haveParseError);
static int8_t compare(BigInt* lhs, BigInt* rhs);
static bool equal(BigInt* lhs, BigInt* rhs);
static JS::Result<bool> looselyEqual(js::ExclusiveContext* cx, Handle<BigInt*> lhs,
HandleValue rhs);
static bool lessThan(BigInt* x, BigInt* y);
// These methods return Nothing when the non-BigInt operand is NaN
// or a string that can't be interpreted as a BigInt.
static mozilla::Maybe<bool> lessThan(BigInt* lhs, double rhs);
static mozilla::Maybe<bool> lessThan(double lhs, BigInt* rhs);
static bool lessThan(js::ExclusiveContext* cx, Handle<BigInt*> lhs, HandleString rhs,
mozilla::Maybe<bool>& res);
static bool lessThan(js::ExclusiveContext* cx, HandleString lhs, Handle<BigInt*> rhs,
mozilla::Maybe<bool>& res);
static bool lessThan(js::ExclusiveContext* cx, HandleValue lhs, HandleValue rhs,
mozilla::Maybe<bool>& res);
private:
static constexpr size_t DigitBits = sizeof(Digit) * CHAR_BIT;
static constexpr size_t HalfDigitBits = DigitBits / 2;
static constexpr Digit HalfDigitMask = (1ull << HalfDigitBits) - 1;
static_assert(DigitBits == 32 || DigitBits == 64,
"Unexpected BigInt Digit size");
// The maximum number of digits that the current implementation supports
// would be 0x7fffffff / DigitBits. However, we use a lower limit for now,
// because raising it later is easier than lowering it. Support up to 1
// million bits.
static constexpr size_t MaxBitLength = 1024 * 1024;
static constexpr size_t MaxDigitLength = MaxBitLength / DigitBits;
// BigInts can be serialized to strings of radix between 2 and 36. For a
// given bigint, radix 2 will take the most characters (one per bit).
// Ensure that the max bigint size is small enough so that we can fit the
// corresponding character count into a size_t, with space for a possible
// sign prefix.
static_assert(MaxBitLength <= std::numeric_limits<size_t>::max() - 1,
"BigInt max length must be small enough to be serialized as a "
"binary string");
static size_t calculateMaximumCharactersRequired(HandleBigInt x,
unsigned radix);
static MOZ_MUST_USE bool calculateMaximumDigitsRequired(js::ExclusiveContext* cx,
uint8_t radix,
size_t charCount,
size_t* result);
static bool absoluteDivWithDigitDivisor(
js::ExclusiveContext* cx, Handle<BigInt*> x, Digit divisor,
const mozilla::Maybe<MutableHandle<BigInt*>>& quotient, Digit* remainder,
bool quotientNegative);
static void internalMultiplyAdd(BigInt* source, Digit factor, Digit summand,
unsigned, BigInt* result);
static void multiplyAccumulate(BigInt* multiplicand, Digit multiplier,
BigInt* accumulator,
unsigned accumulatorIndex);
static bool absoluteDivWithBigIntDivisor(
js::ExclusiveContext* cx, Handle<BigInt*> dividend, Handle<BigInt*> divisor,
const mozilla::Maybe<MutableHandle<BigInt*>>& quotient,
const mozilla::Maybe<MutableHandle<BigInt*>>& remainder,
bool quotientNegative);
enum class LeftShiftMode { SameSizeResult, AlwaysAddOneDigit };
static BigInt* absoluteLeftShiftAlwaysCopy(js::ExclusiveContext* cx, Handle<BigInt*> x,
unsigned shift, LeftShiftMode);
static bool productGreaterThan(Digit factor1, Digit factor2, Digit high,
Digit low);
static BigInt* lshByAbsolute(js::ExclusiveContext* cx, HandleBigInt x, HandleBigInt y);
static BigInt* rshByAbsolute(js::ExclusiveContext* cx, HandleBigInt x, HandleBigInt y);
static BigInt* rshByMaximum(js::ExclusiveContext* cx, bool isNegative);
static BigInt* truncateAndSubFromPowerOfTwo(js::ExclusiveContext* cx, HandleBigInt x,
uint64_t bits,
bool resultNegative);
Digit absoluteInplaceAdd(BigInt* summand, unsigned startIndex);
Digit absoluteInplaceSub(BigInt* subtrahend, unsigned startIndex);
void inplaceRightShiftLowZeroBits(unsigned shift);
void inplaceMultiplyAdd(Digit multiplier, Digit part);
// The result of an SymmetricTrim bitwise op has as many digits as the
// smaller operand. A SymmetricFill bitwise op result has as many digits as
// the larger operand, with high digits (if any) copied from the larger
// operand. AsymmetricFill is like SymmetricFill, except the result has as
// many digits as the first operand; this kind is used for the and-not
// operation.
enum class BitwiseOpKind { SymmetricTrim, SymmetricFill, AsymmetricFill };
template <BitwiseOpKind kind, typename BitwiseOp>
static BigInt* absoluteBitwiseOp(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, BitwiseOp&& op);
// Return `|x| & |y|`.
static BigInt* absoluteAnd(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| | |y|`.
static BigInt* absoluteOr(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| & ~|y|`.
static BigInt* absoluteAndNot(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `|x| ^ |y|`.
static BigInt* absoluteXor(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y);
// Return `(|x| + 1) * (resultNegative ? -1 : +1)`.
static BigInt* absoluteAddOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
bool resultNegative);
// Return `(|x| - 1) * (resultNegative ? -1 : +1)`, with the precondition that
// |x| != 0.
static BigInt* absoluteSubOne(js::ExclusiveContext* cx, Handle<BigInt*> x,
unsigned resultLength);
// Return `a + b`, incrementing `*carry` if the addition overflows.
static inline Digit digitAdd(Digit a, Digit b, Digit* carry) {
Digit result = a + b;
*carry += static_cast<Digit>(result < a);
return result;
}
// Return `left - right`, incrementing `*borrow` if the addition overflows.
static inline Digit digitSub(Digit left, Digit right, Digit* borrow) {
Digit result = left - right;
*borrow += static_cast<Digit>(result > left);
return result;
}
// Compute `a * b`, returning the low half of the result and putting the
// high half in `*high`.
static Digit digitMul(Digit a, Digit b, Digit* high);
// Divide `(high << DigitBits) + low` by `divisor`, returning the quotient
// and storing the remainder in `*remainder`, with the precondition that
// `high < divisor` so that the result fits in a Digit.
static Digit digitDiv(Digit high, Digit low, Digit divisor, Digit* remainder);
// Return `(|x| + |y|) * (resultNegative ? -1 : +1)`.
static BigInt* absoluteAdd(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, bool resultNegative);
// Return `(|x| - |y|) * (resultNegative ? -1 : +1)`, with the precondition
// that |x| >= |y|.
static BigInt* absoluteSub(js::ExclusiveContext* cx, Handle<BigInt*> x,
Handle<BigInt*> y, bool resultNegative);
// If `|x| < |y|` return -1; if `|x| == |y|` return 0; otherwise return 1.
static int8_t absoluteCompare(BigInt* lhs, BigInt* rhs);
static int8_t compare(BigInt* lhs, double rhs);
static bool equal(BigInt* lhs, double rhs);
static JSLinearString* toStringBasePowerOfTwo(js::ExclusiveContext* cx, Handle<BigInt*>,
unsigned radix);
static JSLinearString* toStringGeneric(js::ExclusiveContext* cx, Handle<BigInt*>,
unsigned radix);
static BigInt* trimHighZeroDigits(js::ExclusiveContext* cx, Handle<BigInt*> x);
static BigInt* destructivelyTrimHighZeroDigits(js::ExclusiveContext* cx,
Handle<BigInt*> x);
friend struct JSStructuredCloneReader;
friend struct JSStructuredCloneWriter;
#if 0 // Future XDR support
template <js::XDRMode mode>
friend js::XDRResult js::XDRBigInt(js::XDRState<mode>* xdr,
MutableHandle<BigInt*> bi);
#endif
BigInt() = delete;
BigInt(const BigInt& other) = delete;
void operator=(const BigInt& other) = delete;
};
static_assert(
sizeof(BigInt) >= js_gc_MinCellSize,
"sizeof(BigInt) must be greater than the minimum allocation size");
static_assert(
sizeof(BigInt) == js_gc_MinCellSize,
"sizeof(BigInt) intended to be the same as the minimum allocation size");
} // namespace JS
namespace js {
extern JSAtom* BigIntToAtom(js::ExclusiveContext* cx, JS::HandleBigInt bi);
extern JS::BigInt* NumberToBigInt(js::ExclusiveContext* cx, double d);
// Parse a BigInt from a string, using the method specified for StringToBigInt.
// Used by the BigInt constructor among other places.
extern JS::Result<JS::BigInt*, JS::OOM&> StringToBigInt(
js::ExclusiveContext* cx, JS::Handle<JSString*> str);
// Parse a BigInt from an already-validated numeric literal. Used by the
// parser. Can only fail in out-of-memory situations.
extern JS::BigInt* ParseBigIntLiteral(
js::ExclusiveContext* cx, const mozilla::Range<const char16_t>& chars);
extern JS::BigInt* ToBigInt(js::ExclusiveContext* cx, JS::Handle<JS::Value> v);
} // namespace js
#undef js_gc_MinCellSize
#undef js_gc_Cell_ReservedBits
#endif
+2 -1
View File
@@ -171,7 +171,8 @@ js::ValueToStringBufferSlow(JSContext* cx, const Value& arg, StringBuffer& sb)
return false;
}
if (v.isBigInt()) {
JSLinearString* str = BigInt::toString(cx, v.toBigInt(), 10);
RootedBigInt i(cx, v.toBigInt());
JSLinearString* str = BigInt::toString(cx, i, 10);
if (!str)
return false;
return sb.append(str);
+65
View File
@@ -31,6 +31,7 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/RangedPtr.h"
#include <algorithm>
@@ -39,6 +40,8 @@
#include "jsdate.h"
#include "jswrapper.h"
#include "vm/BigIntType.h"
#include "builtin/MapObject.h"
#include "js/Date.h"
#include "js/GCHashTable.h"
@@ -57,6 +60,7 @@ using mozilla::IsNaN;
using mozilla::LittleEndian;
using mozilla::NativeEndian;
using mozilla::NumbersAreIdentical;
using mozilla::RangedPtr;
using JS::CanonicalizeNaN;
// When you make updates here, make sure you consider whether you need to bump the
@@ -104,6 +108,9 @@ enum StructuredDataType : uint32_t {
SCTAG_SHARED_ARRAY_BUFFER_OBJECT,
SCTAG_BIGINT,
SCTAG_BIGINT_OBJECT,
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
@@ -349,6 +356,8 @@ struct JSStructuredCloneReader {
JSString* readStringImpl(uint32_t nchars);
JSString* readString(uint32_t data);
BigInt* readBigInt(uint32_t data);
bool checkDouble(double d);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
bool v1Read = false);
@@ -439,6 +448,8 @@ struct JSStructuredCloneWriter {
bool traverseSet(HandleObject obj);
bool traverseSavedFrame(HandleObject obj);
bool writeBigInt(uint32_t tag, BigInt* bi);
bool reportDataCloneError(uint32_t errorId);
bool parseTransferable();
@@ -1055,6 +1066,23 @@ JSStructuredCloneWriter::writeString(uint32_t tag, JSString* str)
: out.writeChars(linear->twoByteChars(nogc), length);
}
bool
JSStructuredCloneWriter::writeBigInt(uint32_t tag, BigInt* bi)
{
bool signBit = bi->isNegative();
size_t length = bi->digitLength();
// The length must fit in 31 bits to leave room for a sign bit.
if (length > size_t(INT32_MAX)) {
return false;
}
uint32_t lengthAndSign = length | (static_cast<uint32_t>(signBit) << 31);
if (!out.writePair(tag, lengthAndSign)) {
return false;
}
return out.writeArray(bi->digits().data(), length);
}
inline void
JSStructuredCloneWriter::checkStack()
{
@@ -1388,6 +1416,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return out.writePair(SCTAG_NULL, 0);
} else if (v.isUndefined()) {
return out.writePair(SCTAG_UNDEFINED, 0);
} else if (v.isBigInt()) {
return writeBigInt(SCTAG_BIGINT, v.toBigInt());
} else if (v.isObject()) {
RootedObject obj(context(), &v.toObject());
@@ -1441,6 +1471,12 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
} else if (cls == ESClass::Map) {
return traverseMap(obj);
} else if (cls == ESClass::BigInt) {
RootedValue unboxed(context());
if (!Unbox(context(), obj, &unboxed)) {
return false;
}
return writeBigInt(SCTAG_BIGINT_OBJECT, unboxed.toBigInt());
} else if (cls == ESClass::Set) {
return traverseSet(obj);
} else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
@@ -1745,6 +1781,22 @@ JSStructuredCloneReader::readString(uint32_t data)
return latin1 ? readStringImpl<Latin1Char>(nchars) : readStringImpl<char16_t>(nchars);
}
BigInt* JSStructuredCloneReader::readBigInt(uint32_t data) {
size_t length = data & JS_BITMASK(31);
bool isNegative = data & (1 << 31);
if (length == 0) {
return BigInt::zero(context());
}
BigInt* result = BigInt::createUninitialized(context(), length, isNegative);
if (!result) {
return nullptr;
}
if (!in.readArray(result->digits().data(), length)) {
return nullptr;
}
return result;
}
static uint32_t
TagToV1ArrayType(uint32_t tag)
{
@@ -2021,6 +2073,19 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
break;
}
case SCTAG_BIGINT:
case SCTAG_BIGINT_OBJECT: {
RootedBigInt bi(context(), readBigInt(data));
if (!bi) {
return false;
}
vp.setBigInt(bi);
if (tag == SCTAG_BIGINT_OBJECT && !PrimitiveToObject(context(), vp)) {
return false;
}
break;
}
case SCTAG_DATE_OBJECT: {
double d;
if (!in.readDouble(&d) || !checkDouble(d))