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:
Jakob Kummerow 2018-03-07 23:00:51 -08:00 committed by Commit Bot
parent 6cb5903769
commit 96e2e8588d
6 changed files with 184 additions and 4 deletions

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
};

View File

@ -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>();

View File

@ -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;

View File

@ -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";