Reland "[bigint] Serialization support for BigInts"
This is a reland of 609aaa5549
Originally reviewed at: https://chromium-review.googlesource.com/952626
Tbr: adamk@chromium.org
Bug: v8:6791
Change-Id: If0699fbfb280192bed61538ccc67c7c95893e691
Reviewed-on: https://chromium-review.googlesource.com/954665
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51819}
This commit is contained in:
parent
6cb5903769
commit
96e2e8588d
@ -2379,8 +2379,7 @@ TNode<HeapNumber> CodeStubAssembler::AllocateHeapNumberWithValue(
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::AllocateBigInt(TNode<IntPtrT> length) {
|
||||
TNode<BigInt> result = AllocateRawBigInt(length);
|
||||
STATIC_ASSERT(BigInt::LengthBits::kShift == 0);
|
||||
StoreBigIntBitfield(result, length);
|
||||
StoreBigIntBitfield(result, WordShl(length, BigInt::LengthBits::kShift));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1753,6 +1753,51 @@ Handle<BigInt> BigInt::Finalize(Handle<FreshlyAllocatedBigInt> x, bool sign) {
|
||||
return MutableBigInt::MakeImmutable(bigint);
|
||||
}
|
||||
|
||||
// The serialization format MUST NOT CHANGE without updating the format
|
||||
// version in value-serializer.cc!
|
||||
uint32_t BigInt::GetBitfieldForSerialization() const {
|
||||
// In order to make the serialization format the same on 32/64 bit builds,
|
||||
// we convert the length-in-digits to length-in-bytes for serialization.
|
||||
// Being able to do this depends on having enough LengthBits:
|
||||
STATIC_ASSERT(kMaxLength * kDigitSize <= LengthBits::kMax);
|
||||
int bytelength = length() * kDigitSize;
|
||||
return SignBits::encode(sign()) | LengthBits::encode(bytelength);
|
||||
}
|
||||
|
||||
int BigInt::DigitsByteLengthForBitfield(uint32_t bitfield) {
|
||||
return LengthBits::decode(bitfield);
|
||||
}
|
||||
|
||||
// The serialization format MUST NOT CHANGE without updating the format
|
||||
// version in value-serializer.cc!
|
||||
void BigInt::SerializeDigits(uint8_t* storage) {
|
||||
int bytelength = length() * kDigitSize;
|
||||
void* digits = reinterpret_cast<void*>(reinterpret_cast<Address>(this) +
|
||||
kDigitsOffset - kHeapObjectTag);
|
||||
memcpy(storage, digits, bytelength);
|
||||
}
|
||||
|
||||
// The serialization format MUST NOT CHANGE without updating the format
|
||||
// version in value-serializer.cc!
|
||||
MaybeHandle<BigInt> BigInt::FromSerializedDigits(
|
||||
Isolate* isolate, uint32_t bitfield, Vector<const uint8_t> digits_storage,
|
||||
PretenureFlag pretenure) {
|
||||
int bytelength = LengthBits::decode(bitfield);
|
||||
DCHECK(digits_storage.length() == bytelength);
|
||||
bool sign = SignBits::decode(bitfield);
|
||||
int length = (bytelength + kDigitSize - 1) / kDigitSize; // Round up.
|
||||
Handle<MutableBigInt> result =
|
||||
MutableBigInt::Cast(isolate->factory()->NewBigInt(length, pretenure));
|
||||
result->initialize_bitfield(sign, length);
|
||||
void* digits = reinterpret_cast<void*>(reinterpret_cast<Address>(*result) +
|
||||
kDigitsOffset - kHeapObjectTag);
|
||||
memcpy(digits, digits_storage.start(), bytelength);
|
||||
void* padding_start =
|
||||
reinterpret_cast<void*>(reinterpret_cast<Address>(digits) + bytelength);
|
||||
memset(padding_start, 0, length * kDigitSize - bytelength);
|
||||
return MutableBigInt::MakeImmutable(result);
|
||||
}
|
||||
|
||||
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
MaybeHandle<String> MutableBigInt::ToStringBasePowerOfTwo(Handle<BigIntBase> x,
|
||||
|
@ -16,6 +16,8 @@ namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class BigInt;
|
||||
class ValueDeserializer;
|
||||
class ValueSerializer;
|
||||
|
||||
// BigIntBase is just the raw data object underlying a BigInt. Use with care!
|
||||
// Most code should be using BigInts instead.
|
||||
@ -32,8 +34,9 @@ class BigIntBase : public HeapObject {
|
||||
|
||||
static const int kLengthFieldBits = 30;
|
||||
STATIC_ASSERT(kMaxLength <= ((1 << kLengthFieldBits) - 1));
|
||||
class LengthBits : public BitField<int, 0, kLengthFieldBits> {};
|
||||
class SignBits : public BitField<bool, LengthBits::kNext, 1> {};
|
||||
class SignBits : public BitField<bool, 0, 1> {};
|
||||
class LengthBits : public BitField<int, SignBits::kNext, kLengthFieldBits> {};
|
||||
STATIC_ASSERT(LengthBits::kNext <= 32);
|
||||
|
||||
static const int kBitfieldOffset = HeapObject::kHeaderSize;
|
||||
static const int kDigitsOffset = kBitfieldOffset + kPointerSize;
|
||||
@ -170,6 +173,8 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase {
|
||||
|
||||
private:
|
||||
friend class StringToBigIntHelper;
|
||||
friend class ValueDeserializer;
|
||||
friend class ValueSerializer;
|
||||
|
||||
// Special functions for StringToBigIntHelper:
|
||||
static Handle<BigInt> Zero(Isolate* isolate);
|
||||
@ -180,6 +185,16 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase {
|
||||
uintptr_t factor, uintptr_t summand);
|
||||
static Handle<BigInt> Finalize(Handle<FreshlyAllocatedBigInt> x, bool sign);
|
||||
|
||||
// Special functions for ValueSerializer/ValueDeserializer:
|
||||
uint32_t GetBitfieldForSerialization() const;
|
||||
static int DigitsByteLengthForBitfield(uint32_t bitfield);
|
||||
// Expects {storage} to have a length of at least
|
||||
// {DigitsByteLengthForBitfield(GetBitfieldForSerialization())}.
|
||||
void SerializeDigits(uint8_t* storage);
|
||||
MUST_USE_RESULT static MaybeHandle<BigInt> FromSerializedDigits(
|
||||
Isolate* isolate, uint32_t bitfield, Vector<const uint8_t> digits_storage,
|
||||
PretenureFlag pretenure);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(BigInt);
|
||||
};
|
||||
|
||||
|
@ -58,6 +58,9 @@ static size_t BytesNeededForVarint(T value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Note that some additional tag values are defined in Blink's
|
||||
// Source/bindings/core/v8/serialization/SerializationTag.h, which must
|
||||
// not clash with values defined here.
|
||||
enum class SerializationTag : uint8_t {
|
||||
// version:uint32_t (if at beginning of data, sets version > 0)
|
||||
kVersion = 0xFF,
|
||||
@ -80,6 +83,8 @@ enum class SerializationTag : uint8_t {
|
||||
// Number represented as a 64-bit double.
|
||||
// Host byte order is used (N.B. this makes the format non-portable).
|
||||
kDouble = 'N',
|
||||
// BigInt. Bitfield:uint32_t, then raw digits storage.
|
||||
kBigInt = 'Z',
|
||||
// byteLength:uint32_t, then raw data
|
||||
kUtf8String = 'S',
|
||||
kOneByteString = '"',
|
||||
@ -107,6 +112,8 @@ enum class SerializationTag : uint8_t {
|
||||
kFalseObject = 'x',
|
||||
// Number object. value:double
|
||||
kNumberObject = 'n',
|
||||
// BigInt object. Bitfield:uint32_t, then raw digits storage.
|
||||
kBigIntObject = 'z',
|
||||
// String object, UTF-8 encoding. byteLength:uint32_t, then raw data.
|
||||
kStringObject = 's',
|
||||
// Regular expression, UTF-8 encoding. byteLength:uint32_t, raw data,
|
||||
@ -253,6 +260,16 @@ void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
|
||||
WriteRawBytes(chars.begin(), chars.length() * sizeof(uc16));
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteBigIntContents(BigInt* bigint) {
|
||||
uint32_t bitfield = bigint->GetBitfieldForSerialization();
|
||||
int bytelength = BigInt::DigitsByteLengthForBitfield(bitfield);
|
||||
WriteVarint<uint32_t>(bitfield);
|
||||
uint8_t* dest;
|
||||
if (ReserveRawBytes(bytelength).To(&dest)) {
|
||||
bigint->SerializeDigits(dest);
|
||||
}
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteRawBytes(const void* source, size_t length) {
|
||||
uint8_t* dest;
|
||||
if (ReserveRawBytes(length).To(&dest)) {
|
||||
@ -340,6 +357,9 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
WriteHeapNumber(HeapNumber::cast(*object));
|
||||
return ThrowIfOutOfMemory();
|
||||
case BIGINT_TYPE:
|
||||
WriteBigInt(BigInt::cast(*object));
|
||||
return ThrowIfOutOfMemory();
|
||||
case JS_TYPED_ARRAY_TYPE:
|
||||
case JS_DATA_VIEW_TYPE: {
|
||||
// Despite being JSReceivers, these have their wrapped buffer serialized
|
||||
@ -403,6 +423,11 @@ void ValueSerializer::WriteHeapNumber(HeapNumber* number) {
|
||||
WriteDouble(number->value());
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteBigInt(BigInt* bigint) {
|
||||
WriteTag(SerializationTag::kBigInt);
|
||||
WriteBigIntContents(bigint);
|
||||
}
|
||||
|
||||
void ValueSerializer::WriteString(Handle<String> string) {
|
||||
string = String::Flatten(string);
|
||||
DisallowHeapAllocation no_gc;
|
||||
@ -687,6 +712,9 @@ Maybe<bool> ValueSerializer::WriteJSValue(Handle<JSValue> value) {
|
||||
} else if (inner_value->IsNumber()) {
|
||||
WriteTag(SerializationTag::kNumberObject);
|
||||
WriteDouble(inner_value->Number());
|
||||
} else if (inner_value->IsBigInt()) {
|
||||
WriteTag(SerializationTag::kBigIntObject);
|
||||
WriteBigIntContents(BigInt::cast(inner_value));
|
||||
} else if (inner_value->IsString()) {
|
||||
WriteTag(SerializationTag::kStringObject);
|
||||
WriteString(handle(String::cast(inner_value), isolate_));
|
||||
@ -1154,6 +1182,8 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
|
||||
if (number.IsNothing()) return MaybeHandle<Object>();
|
||||
return isolate_->factory()->NewNumber(number.FromJust(), pretenure_);
|
||||
}
|
||||
case SerializationTag::kBigInt:
|
||||
return ReadBigInt();
|
||||
case SerializationTag::kUtf8String:
|
||||
return ReadUtf8String();
|
||||
case SerializationTag::kOneByteString:
|
||||
@ -1176,6 +1206,7 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
|
||||
case SerializationTag::kTrueObject:
|
||||
case SerializationTag::kFalseObject:
|
||||
case SerializationTag::kNumberObject:
|
||||
case SerializationTag::kBigIntObject:
|
||||
case SerializationTag::kStringObject:
|
||||
return ReadJSValue(tag);
|
||||
case SerializationTag::kRegExp:
|
||||
@ -1223,6 +1254,18 @@ MaybeHandle<String> ValueDeserializer::ReadString() {
|
||||
return Handle<String>::cast(object);
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> ValueDeserializer::ReadBigInt() {
|
||||
uint32_t bitfield;
|
||||
if (!ReadVarint<uint32_t>().To(&bitfield)) return MaybeHandle<BigInt>();
|
||||
int bytelength = BigInt::DigitsByteLengthForBitfield(bitfield);
|
||||
Vector<const uint8_t> digits_storage;
|
||||
if (!ReadRawBytes(bytelength).To(&digits_storage)) {
|
||||
return MaybeHandle<BigInt>();
|
||||
}
|
||||
return BigInt::FromSerializedDigits(isolate_, bitfield, digits_storage,
|
||||
pretenure_);
|
||||
}
|
||||
|
||||
MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
|
||||
uint32_t utf8_length;
|
||||
Vector<const uint8_t> utf8_bytes;
|
||||
@ -1464,6 +1507,14 @@ MaybeHandle<JSValue> ValueDeserializer::ReadJSValue(SerializationTag tag) {
|
||||
value->set_value(*number_object);
|
||||
break;
|
||||
}
|
||||
case SerializationTag::kBigIntObject: {
|
||||
Handle<BigInt> bigint;
|
||||
if (!ReadBigInt().ToHandle(&bigint)) return MaybeHandle<JSValue>();
|
||||
value = Handle<JSValue>::cast(isolate_->factory()->NewJSObject(
|
||||
isolate_->bigint_function(), pretenure_));
|
||||
value->set_value(*bigint);
|
||||
break;
|
||||
}
|
||||
case SerializationTag::kStringObject: {
|
||||
Handle<String> string;
|
||||
if (!ReadString().ToHandle(&string)) return MaybeHandle<JSValue>();
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class BigInt;
|
||||
class HeapNumber;
|
||||
class Isolate;
|
||||
class JSArrayBuffer;
|
||||
@ -107,12 +108,14 @@ class ValueSerializer {
|
||||
void WriteZigZag(T value);
|
||||
void WriteOneByteString(Vector<const uint8_t> chars);
|
||||
void WriteTwoByteString(Vector<const uc16> chars);
|
||||
void WriteBigIntContents(BigInt* bigint);
|
||||
Maybe<uint8_t*> ReserveRawBytes(size_t bytes);
|
||||
|
||||
// Writing V8 objects of various kinds.
|
||||
void WriteOddball(Oddball* oddball);
|
||||
void WriteSmi(Smi* smi);
|
||||
void WriteHeapNumber(HeapNumber* number);
|
||||
void WriteBigInt(BigInt* bigint);
|
||||
void WriteString(Handle<String> string);
|
||||
Maybe<bool> WriteJSReceiver(Handle<JSReceiver> receiver) WARN_UNUSED_RESULT;
|
||||
Maybe<bool> WriteJSObject(Handle<JSObject> object) WARN_UNUSED_RESULT;
|
||||
@ -255,6 +258,7 @@ class ValueDeserializer {
|
||||
|
||||
// Reading V8 objects of specific kinds.
|
||||
// The tag is assumed to have already been read.
|
||||
MaybeHandle<BigInt> ReadBigInt() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<String> ReadUtf8String() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<String> ReadOneByteString() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<String> ReadTwoByteString() WARN_UNUSED_RESULT;
|
||||
|
@ -387,6 +387,72 @@ TEST_F(ValueSerializerTest, DecodeNumber) {
|
||||
// TODO(jbroman): Equivalent test for big-endian machines.
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTest, RoundTripBigInt) {
|
||||
Local<Value> value = RoundTripTest(BigInt::New(isolate(), -42));
|
||||
ASSERT_TRUE(value->IsBigInt());
|
||||
ExpectScriptTrue("result === -42n");
|
||||
|
||||
value = RoundTripTest(BigInt::New(isolate(), 42));
|
||||
ExpectScriptTrue("result === 42n");
|
||||
|
||||
value = RoundTripTest(BigInt::New(isolate(), 0));
|
||||
ExpectScriptTrue("result === 0n");
|
||||
|
||||
value = RoundTripTest("0x1234567890abcdef777888999n");
|
||||
ExpectScriptTrue("result === 0x1234567890abcdef777888999n");
|
||||
|
||||
value = RoundTripTest("-0x1234567890abcdef777888999123n");
|
||||
ExpectScriptTrue("result === -0x1234567890abcdef777888999123n");
|
||||
|
||||
Context::Scope scope(serialization_context());
|
||||
value = RoundTripTest(BigIntObject::New(isolate(), 23));
|
||||
ASSERT_TRUE(value->IsBigIntObject());
|
||||
ExpectScriptTrue("result == 23n");
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTest, DecodeBigInt) {
|
||||
Local<Value> value = DecodeTest({
|
||||
0xFF, 0x0D, // Version 13
|
||||
0x5A, // BigInt
|
||||
0x08, // Bitfield: sign = false, bytelength = 4
|
||||
0x2A, 0x00, 0x00, 0x00, // Digit: 42
|
||||
});
|
||||
ASSERT_TRUE(value->IsBigInt());
|
||||
ExpectScriptTrue("result === 42n");
|
||||
|
||||
value = DecodeTest({
|
||||
0xFF, 0x0D, // Version 13
|
||||
0x7A, // BigIntObject
|
||||
0x11, // Bitfield: sign = true, bytelength = 8
|
||||
0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Digit: 42
|
||||
});
|
||||
ASSERT_TRUE(value->IsBigIntObject());
|
||||
ExpectScriptTrue("result == -42n");
|
||||
|
||||
value = DecodeTest({
|
||||
0xFF, 0x0D, // Version 13
|
||||
0x5A, // BigInt
|
||||
0x10, // Bitfield: sign = false, bytelength = 8
|
||||
0xEF, 0xCD, 0xAB, 0x90, 0x78, 0x56, 0x34, 0x12 // Digit(s).
|
||||
});
|
||||
ExpectScriptTrue("result === 0x1234567890abcdefn");
|
||||
|
||||
value = DecodeTest({0xFF, 0x0D, // Version 13
|
||||
0x5A, // BigInt
|
||||
0x17, // Bitfield: sign = true, bytelength = 11
|
||||
0xEF, 0xCD, 0xAB, 0x90, // Digits.
|
||||
0x78, 0x56, 0x34, 0x12, 0x33, 0x44, 0x55});
|
||||
ExpectScriptTrue("result === -0x5544331234567890abcdefn");
|
||||
|
||||
value = DecodeTest({
|
||||
0xFF, 0x0D, // Version 13
|
||||
0x5A, // BigInt
|
||||
0x02, // Bitfield: sign = false, bytelength = 1
|
||||
0x2A, // Digit: 42
|
||||
});
|
||||
ExpectScriptTrue("result === 42n");
|
||||
}
|
||||
|
||||
// String constants (in UTF-8) used for string encoding tests.
|
||||
static const char kHelloString[] = "Hello";
|
||||
static const char kQuebecString[] = "\x51\x75\xC3\xA9\x62\x65\x63";
|
||||
|
Loading…
Reference in New Issue
Block a user