[web snapshot] Support BigInt

Bug: v8:11525
Change-Id: I69c08f3cc4ee6b391e462a5d49de750f34bbc8cf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3815487
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: 王澳 <wangao.james@bytedance.com>
Cr-Commit-Position: refs/heads/main@{#82345}
This commit is contained in:
jameslahm 2022-08-10 20:27:56 +08:00 committed by V8 LUCI CQ
parent 45a74f3a51
commit 96a42ce526
5 changed files with 276 additions and 4 deletions

View File

@ -488,6 +488,7 @@ class RuntimeCallTimer final {
V(WebSnapshotDeserialize) \
V(WebSnapshotDeserialize_Arrays) \
V(WebSnapshotDeserialize_ArrayBuffers) \
V(WebSnapshotDeserialize_BigInts) \
V(WebSnapshotDeserialize_BuiltinObjects) \
V(WebSnapshotDeserialize_Classes) \
V(WebSnapshotDeserialize_Contexts) \

View File

@ -32,6 +32,9 @@ bool MutableBigInt_AbsoluteMulAndCanonicalize(Address result_addr,
class BigInt;
class ValueDeserializer;
class ValueSerializer;
class WebSnapshotSerializerDeserializer;
class WebSnapshotSerializer;
class WebSnapshotDeserializer;
#include "torque-generated/src/objects/bigint-tq.inc"
@ -254,6 +257,9 @@ class BigInt : public BigIntBase {
friend class StringToBigIntHelper;
friend class ValueDeserializer;
friend class ValueSerializer;
friend class WebSnapshotSerializerDeserializer;
friend class WebSnapshotSerializer;
friend class WebSnapshotDeserializer;
// Special functions for StringToBigIntHelper:
template <typename IsolateT>

View File

@ -15,6 +15,7 @@
#include "src/base/platform/wrappers.h"
#include "src/handles/handles.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/bigint.h"
#include "src/objects/contexts-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-regexp-inl.h"
@ -261,6 +262,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
: WebSnapshotSerializerDeserializer(isolate),
string_serializer_(isolate_, nullptr),
symbol_serializer_(isolate_, nullptr),
bigint_serializer_(isolate_, nullptr),
map_serializer_(isolate_, nullptr),
builtin_object_serializer_(isolate_, nullptr),
context_serializer_(isolate_, nullptr),
@ -275,6 +277,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
external_object_ids_(isolate_->heap()),
string_ids_(isolate_->heap()),
symbol_ids_(isolate_->heap()),
bigint_ids_(isolate_->heap()),
map_ids_(isolate_->heap()),
context_ids_(isolate_->heap()),
function_ids_(isolate_->heap()),
@ -290,6 +293,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
auto empty_array_list = factory()->empty_array_list();
strings_ = empty_array_list;
symbols_ = empty_array_list;
bigints_ = empty_array_list;
maps_ = empty_array_list;
contexts_ = empty_array_list;
functions_ = empty_array_list;
@ -412,6 +416,11 @@ void WebSnapshotSerializer::SerializePendingItems() {
SerializeSymbol(symbol);
}
for (int i = 0; i < bigints_->Length(); ++i) {
Handle<BigInt> bigint = handle(BigInt::cast(bigints_->Get(i)), isolate_);
SerializeBigInt(bigint);
}
for (int i = 0; i < maps_->Length(); ++i) {
Handle<Map> map = handle(Map::cast(maps_->Get(i)), isolate_);
SerializeMap(map);
@ -501,14 +510,14 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
ValueSerializer total_serializer(isolate_, nullptr);
size_t needed_size =
sizeof(kMagicNumber) + string_serializer_.buffer_size_ +
symbol_serializer_.buffer_size_ +
symbol_serializer_.buffer_size_ + bigint_serializer_.buffer_size_ +
builtin_object_serializer_.buffer_size_ + map_serializer_.buffer_size_ +
context_serializer_.buffer_size_ + function_serializer_.buffer_size_ +
class_serializer_.buffer_size_ + array_serializer_.buffer_size_ +
array_buffer_serializer_.buffer_size_ +
typed_array_serializer_.buffer_size_ +
data_view_serializer_.buffer_size_ + object_serializer_.buffer_size_ +
export_serializer_.buffer_size_ + 13 * sizeof(uint32_t);
export_serializer_.buffer_size_ + 14 * sizeof(uint32_t);
if (total_serializer.ExpandBuffer(needed_size).IsNothing()) {
Throw("Out of memory");
return;
@ -517,6 +526,7 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
total_serializer.WriteRawBytes(kMagicNumber, 4);
WriteObjects(total_serializer, string_count(), string_serializer_, "strings");
WriteObjects(total_serializer, symbol_count(), symbol_serializer_, "symbols");
WriteObjects(total_serializer, bigint_count(), bigint_serializer_, "bigints");
WriteObjects(total_serializer, builtin_object_count(),
builtin_object_serializer_, "builtin_objects");
WriteObjects(total_serializer, map_count(), map_serializer_, "maps");
@ -606,6 +616,22 @@ void WebSnapshotSerializer::SerializeSymbol(Handle<Symbol> symbol) {
}
}
// Format (serialized bigint)
// - BigIntFlags, including sign and byte length.
// - digit bytes.
void WebSnapshotSerializer::SerializeBigInt(Handle<BigInt> bigint) {
uint32_t flags = BigIntSignAndLengthToFlags(bigint);
bigint_serializer_.WriteUint32(flags);
int byte_length = BigIntLengthBitField::decode(flags);
uint8_t* dest;
if (bigint_serializer_.ReserveRawBytes(byte_length).To(&dest)) {
bigint->SerializeDigits(dest);
} else {
Throw("Serialize BigInt failed");
return;
}
}
bool WebSnapshotSerializer::ShouldBeSerialized(Handle<Name> key) {
// Don't serialize class_positions_symbol property in Class.
if (key->Equals(*factory()->class_positions_symbol())) {
@ -865,6 +891,9 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
case SYMBOL_TYPE:
DiscoverSymbol(Handle<Symbol>::cast(object));
break;
case BIGINT_TYPE:
DiscoverBigInt(Handle<BigInt>::cast(object));
break;
case ODDBALL_TYPE:
case HEAP_NUMBER_TYPE:
// Can't contain references to other objects.
@ -1380,6 +1409,14 @@ void WebSnapshotSerializer::DiscoverSymbol(Handle<Symbol> symbol) {
}
}
void WebSnapshotSerializer::DiscoverBigInt(Handle<BigInt> bigint) {
uint32_t id;
if (InsertIntoIndexMap(bigint_ids_, *bigint, id)) return;
DCHECK_EQ(id, bigints_->Length());
bigints_ = ArrayList::Add(isolate_, bigints_, bigint);
}
// Format (serialized function):
// - 0 if there's no context, 1 + context id otherwise
// - String id (source snippet)
@ -1659,6 +1696,24 @@ uint8_t WebSnapshotSerializerDeserializer::ArrayBufferKindToFlags(
ResizableBitField::encode(array_buffer->is_resizable());
}
uint32_t WebSnapshotSerializerDeserializer::BigIntSignAndLengthToFlags(
Handle<BigInt> bigint) {
uint32_t bitfield = bigint->GetBitfieldForSerialization();
int byte_length = BigInt::DigitsByteLengthForBitfield(bitfield);
int sign = BigInt::SignBits::decode(bitfield);
return BigIntSignBitField::encode(sign) |
BigIntLengthBitField::encode(byte_length);
}
uint32_t WebSnapshotSerializerDeserializer::BigIntFlagsToBitField(
uint32_t flags) {
int byte_length = BigIntLengthBitField::decode(flags);
int sign = BigIntSignBitField::decode(flags);
return BigInt::SignBits::encode(sign) |
BigInt::LengthBits::encode(byte_length);
}
// Format (serialized array buffer):
// - ArrayBufferFlags, including was_detached, is_shared and is_resizable.
// - Byte length
@ -1855,6 +1910,10 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
serializer.WriteByte(ValueType::SYMBOL_ID);
serializer.WriteUint32(GetSymbolId(Symbol::cast(*heap_object)));
break;
case BIGINT_TYPE:
serializer.WriteByte(ValueType::BIGINT_ID);
serializer.WriteUint32(GetBigIntId(BigInt::cast(*heap_object)));
break;
case JS_REG_EXP_TYPE: {
Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(heap_object);
if (regexp->map() != isolate_->regexp_function()->initial_map()) {
@ -1946,6 +2005,14 @@ uint32_t WebSnapshotSerializer::GetSymbolId(Symbol symbol) {
return static_cast<uint32_t>(id);
}
uint32_t WebSnapshotSerializer::GetBigIntId(BigInt bigint) {
int id;
bool return_value = bigint_ids_.Lookup(bigint, &id);
DCHECK(return_value);
USE(return_value);
return static_cast<uint32_t>(id);
}
uint32_t WebSnapshotSerializer::GetMapId(Map map) {
int id;
bool return_value = map_ids_.Lookup(map, &id);
@ -2077,6 +2144,7 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
Handle<FixedArray> empty_array = factory()->empty_fixed_array();
strings_handle_ = empty_array;
symbols_handle_ = empty_array;
bigints_handle_ = empty_array;
builtin_objects_handle_ = empty_array;
maps_handle_ = empty_array;
contexts_handle_ = empty_array;
@ -2099,6 +2167,7 @@ WebSnapshotDeserializer::~WebSnapshotDeserializer() {
void WebSnapshotDeserializer::UpdatePointers() {
strings_ = *strings_handle_;
symbols_ = *symbols_handle_;
bigints_ = *bigints_handle_;
builtin_objects_ = *builtin_objects_handle_;
maps_ = *maps_handle_;
contexts_ = *contexts_handle_;
@ -2169,6 +2238,7 @@ WebSnapshotDeserializer::ExtractScriptBuffer(
void WebSnapshotDeserializer::Throw(const char* message) {
string_count_ = 0;
symbol_count_ = 0;
bigint_count_ = 0;
map_count_ = 0;
builtin_object_count_ = 0;
context_count_ = 0;
@ -2273,6 +2343,7 @@ bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
DeserializeStrings();
DeserializeSymbols();
DeserializeBigInts();
DeserializeBuiltinObjects();
DeserializeMaps();
DeserializeContexts();
@ -2407,7 +2478,7 @@ String WebSnapshotDeserializer::ReadInPlaceString(
}
Object WebSnapshotDeserializer::ReadSymbol() {
DCHECK(!strings_handle_->is_null());
DCHECK(!symbols_handle_->is_null());
uint32_t symbol_id;
if (!deserializer_->ReadUint32(&symbol_id) || symbol_id >= symbol_count_) {
Throw("malformed symbol id\n");
@ -2416,6 +2487,50 @@ Object WebSnapshotDeserializer::ReadSymbol() {
return symbols_.get(symbol_id);
}
Object WebSnapshotDeserializer::ReadBigInt() {
DCHECK(!bigints_handle_->is_null());
uint32_t bigint_id;
if (!deserializer_->ReadUint32(&bigint_id) || bigint_id >= bigint_count_) {
Throw("malformed bigint id\n");
return roots_.undefined_value();
}
return bigints_.get(bigint_id);
}
void WebSnapshotDeserializer::DeserializeBigInts() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_BigInts);
if (!ReadCount(bigint_count_)) {
Throw("Malformed bigint table");
return;
}
static_assert(kMaxItemCount <= FixedArray::kMaxLength);
bigints_handle_ = factory()->NewFixedArray(bigint_count_);
bigints_ = *bigints_handle_;
for (uint32_t i = 0; i < bigint_count_; ++i) {
uint32_t flags;
if (!deserializer_->ReadUint32(&flags)) {
Throw("malformed bigint flag");
return;
}
int byte_length = BigIntLengthBitField::decode(flags);
base::Vector<const uint8_t> digits_storage;
if (!deserializer_->ReadRawBytes(byte_length).To(&digits_storage)) {
Throw("malformed bigint");
return;
}
Handle<BigInt> bigint;
// BigIntFlags are browser independent, so we explicity convert BigIntFlags
// to BigInt bitfield here though they are same now.
if (!BigInt::FromSerializedDigits(isolate_, BigIntFlagsToBitField(flags),
digits_storage)
.ToHandle(&bigint)) {
Throw("malformed bigint");
return;
}
bigints_.set(i, *bigint);
}
}
void WebSnapshotDeserializer::DeserializeSymbols() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Symbols);
if (!ReadCount(symbol_count_)) {
@ -3761,6 +3876,8 @@ std::tuple<Object, bool> WebSnapshotDeserializer::ReadValue(
return std::make_tuple(ReadRegexp(), false);
case ValueType::SYMBOL_ID:
return std::make_tuple(ReadSymbol(), false);
case ValueType::BIGINT_ID:
return std::make_tuple(ReadBigInt(), false);
case ValueType::EXTERNAL_ID:
return std::make_tuple(ReadExternalReference(), false);
case ValueType::BUILTIN_OBJECT_ID:

View File

@ -8,6 +8,7 @@
#include <queue>
#include "src/handles/handles.h"
#include "src/objects/bigint.h"
#include "src/objects/value-serializer.h"
#include "src/snapshot/serializer.h" // For ObjectCacheIndexMap
@ -61,7 +62,8 @@ class WebSnapshotSerializerDeserializer {
IN_PLACE_STRING_ID,
ARRAY_BUFFER_ID,
TYPED_ARRAY_ID,
DATA_VIEW_ID
DATA_VIEW_ID,
BIGINT_ID
};
enum SymbolType : uint8_t {
@ -111,6 +113,8 @@ class WebSnapshotSerializerDeserializer {
uint8_t ArrayBufferKindToFlags(Handle<JSArrayBuffer> array_buffer);
uint32_t BigIntSignAndLengthToFlags(Handle<BigInt> bigint);
uint32_t BigIntFlagsToBitField(uint32_t flags);
// The maximum count of items for each value type (strings, objects etc.)
static constexpr uint32_t kMaxItemCount =
static_cast<uint32_t>(FixedArray::kMaxLength - 1);
@ -150,6 +154,12 @@ class WebSnapshotSerializerDeserializer {
// constructed without the specified length argument.
using LengthTrackingBitField = base::BitField<bool, 0, 1, uint8_t>;
// Encode BigInt's sign and digits length.
using BigIntSignBitField = base::BitField<bool, 0, 1>;
using BigIntLengthBitField =
BigIntSignBitField::Next<int, BigInt::kLengthFieldBits>;
static_assert(BigIntLengthBitField::kSize == BigInt::LengthBits::kSize);
private:
WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) =
delete;
@ -193,6 +203,10 @@ class V8_EXPORT WebSnapshotSerializer
return static_cast<uint32_t>(symbol_ids_.size());
}
uint32_t bigint_count() const {
return static_cast<uint32_t>(bigint_ids_.size());
}
uint32_t map_count() const { return static_cast<uint32_t>(map_ids_.size()); }
uint32_t builtin_object_count() const {
@ -261,6 +275,7 @@ class V8_EXPORT WebSnapshotSerializer
void DiscoverString(Handle<String> string,
AllowInPlace can_be_in_place = AllowInPlace::No);
void DiscoverSymbol(Handle<Symbol> symbol);
void DiscoverBigInt(Handle<BigInt> bigint);
void DiscoverMap(Handle<Map> map, bool allow_property_in_descriptor = false);
void DiscoverPropertyKey(Handle<Name> key);
void DiscoverMapForFunction(Handle<JSFunction> function);
@ -287,6 +302,7 @@ class V8_EXPORT WebSnapshotSerializer
ValueSerializer& serializer);
void SerializeString(Handle<String> string, ValueSerializer& serializer);
void SerializeSymbol(Handle<Symbol> symbol);
void SerializeBigInt(Handle<BigInt> bigint);
void SerializeMap(Handle<Map> map);
void SerializeBuiltinObject(uint32_t name_id);
void SerializeObjectPrototype(Handle<Map> map, ValueSerializer& serializer);
@ -313,6 +329,7 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t GetStringId(Handle<String> string, bool& in_place);
uint32_t GetSymbolId(Symbol symbol);
uint32_t GetBigIntId(BigInt bigint);
uint32_t GetMapId(Map map);
uint32_t GetFunctionId(JSFunction function);
uint32_t GetClassId(JSFunction function);
@ -329,6 +346,7 @@ class V8_EXPORT WebSnapshotSerializer
ValueSerializer string_serializer_;
ValueSerializer symbol_serializer_;
ValueSerializer bigint_serializer_;
ValueSerializer map_serializer_;
ValueSerializer builtin_object_serializer_;
ValueSerializer context_serializer_;
@ -344,6 +362,7 @@ class V8_EXPORT WebSnapshotSerializer
// These are needed for being able to serialize items in order.
Handle<ArrayList> strings_;
Handle<ArrayList> symbols_;
Handle<ArrayList> bigints_;
Handle<ArrayList> maps_;
Handle<ArrayList> contexts_;
Handle<ArrayList> functions_;
@ -364,6 +383,7 @@ class V8_EXPORT WebSnapshotSerializer
// have a lower ID and will be deserialized first.
ObjectCacheIndexMap string_ids_;
ObjectCacheIndexMap symbol_ids_;
ObjectCacheIndexMap bigint_ids_;
ObjectCacheIndexMap map_ids_;
ObjectCacheIndexMap context_ids_;
ObjectCacheIndexMap function_ids_;
@ -467,6 +487,7 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeStrings();
void DeserializeSymbols();
void DeserializeBigInts();
void DeserializeMaps();
void DeserializeBuiltinObjects();
void DeserializeContexts();
@ -516,6 +537,7 @@ class V8_EXPORT WebSnapshotDeserializer
String ReadInPlaceString(
InternalizeStrings internalize_strings = InternalizeStrings::kNo);
Object ReadSymbol();
Object ReadBigInt();
std::tuple<Object, bool> ReadArray(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadArrayBuffer(Handle<HeapObject> container,
@ -559,6 +581,9 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> symbols_handle_;
FixedArray symbols_;
Handle<FixedArray> bigints_handle_;
FixedArray bigints_;
Handle<FixedArray> builtin_objects_handle_;
FixedArray builtin_objects_;
@ -610,6 +635,7 @@ class V8_EXPORT WebSnapshotDeserializer
uint32_t string_count_ = 0;
uint32_t symbol_count_ = 0;
uint32_t bigint_count_ = 0;
uint32_t map_count_ = 0;
uint32_t builtin_object_count_ = 0;
uint32_t context_count_ = 0;

View File

@ -0,0 +1,122 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-d8-web-snapshot-api --allow-natives-syntax
'use strict';
d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
(function TestBigInt() {
function createObjects() {
const b = 100n;
const c = 2n ** 222n;
globalThis.foo = { bar: b, bar1: c };
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(100n, foo.bar);
assertEquals(2n ** 222n , foo.bar1)
})();
(function TestBigIntInArray() {
function createObjects() {
const b = 100n;
const c = 2n ** 222n;
globalThis.foo = [b, c];
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals([100n, 2n ** 222n], foo)
})();
(function TestBigIntInFunctionContext() {
function createObjects() {
globalThis.foo = {
key: (function () {
const b = 100n;
const c = 2n ** 222n;
function inner() {
return [b, c];
}
return inner;
})()
};
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals([100n, 2n**222n], foo.key());
})();
(function TestBigIntInFunctionContextWithParentContext() {
function createObjects() {
globalThis.foo = {
key: (function () {
const b = 100n;
function inner() {
const c = 2n ** 222n;
function innerinner() {
return [b, c]
}
return innerinner
}
return inner();
})()
};
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals([100n, 2n**222n], foo.key());
})();
(function TestBigIntInTopLevelFunctionWithContext() {
function createObjects() {
globalThis.foo = (function () {
const b = 100n;
const c = 2n ** 222n;
function inner() { return [b, c]; }
return inner;
})();
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals([100n, 2n**222n], foo());
})();
(function TestBigIntInClassStaticProperty() {
function createObjects() {
globalThis.foo = class Foo {
static b = 100n;
static c = 2n ** 222n;
};
}
const { foo: Foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals([100n, 2n**222n], [Foo.b, Foo.c]);
})();
(function TestBigIntInClassWithConstructor() {
function createObjects() {
globalThis.foo = class Foo {
constructor() {
this.b = 100n;
this.c = 2n ** 222n;
}
};
}
const { foo: Foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
const foo = new Foo()
assertEquals([100n, 2n**222n], [foo.b, foo.c]);
})();
(async function TestBigIntInClassWithMethods() {
function createObjects() {
globalThis.foo = class Foo {
b() {
return 100n;
}
async c() {
return 2n ** 222n;
}
};
}
const { foo: Foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
const foo = new Foo()
assertEquals([100n, 2n**222n], [foo.b(), await foo.c()]);
})();