[web-snapshots] Add runtime function for WebSnapshot creation Part I

This CL prepares WebSnapshot for skipping and re-injecting external
references in the web snapshot. External references are encoded as
separate object type and allows us to create partial snapshots at
runtime and reconnect a deserialised snapshot to an existing
object graph.

Part II will also collect all objects which cannot be serialized by the
web-snapshot serializer.

Usage:
  snapshot = %WebSnapshotSerialize(root, skip_externals);
  object = %eWebSnapshotDeserializ(snapshot, replaced_externals);

Drive-by-changes:
- Reduce JSObject Map size in serializer (we ended up with 4 embedder
  fields)
- Avoid adding non-HeapObject to the discovery_queue_
- Split off ReadXXX handlers into separate functions

Bug: v8:11525
Change-Id: Ia6a9914259614c6c288667621b38daa0202d4d72
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3461936
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79211}
This commit is contained in:
Camillo Bruni 2022-02-22 14:12:10 +01:00 committed by V8 LUCI CQ
parent ae04947a2e
commit a7a996ab39
7 changed files with 523 additions and 259 deletions

View File

@ -29,11 +29,13 @@
#include "src/objects/js-array-inl.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/managed-inl.h"
#include "src/objects/smi.h"
#include "src/profiler/heap-snapshot-generator.h"
#include "src/regexp/regexp.h"
#include "src/runtime/runtime-utils.h"
#include "src/snapshot/snapshot.h"
#include "src/web-snapshot/web-snapshot.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-engine.h"
@ -1480,6 +1482,82 @@ RUNTIME_FUNCTION(Runtime_IsSharedString) {
Handle<String>::cast(obj)->IsShared());
}
RUNTIME_FUNCTION(Runtime_WebSnapshotSerialize) {
if (!FLAG_allow_natives_syntax) {
return ReadOnlyRoots(isolate).undefined_value();
}
HandleScope scope(isolate);
if (args.length() < 1 || args.length() > 2) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kRuntimeWrongNumArgs));
}
Handle<Object> object = args.at<Object>(0);
Handle<FixedArray> block_list = isolate->factory()->empty_fixed_array();
if (args.length() == 2) {
if (!args[1].IsJSArray()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
auto raw_blocklist = args.at<JSArray>(1);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, block_list,
JSReceiver::GetOwnValues(raw_blocklist,
PropertyFilter::ENUMERABLE_STRINGS));
}
auto snapshot_data = std::make_shared<WebSnapshotData>();
WebSnapshotSerializer serializer(isolate);
if (!serializer.TakeSnapshot(object, block_list, *snapshot_data)) {
return ReadOnlyRoots(isolate).exception();
}
i::Handle<i::Object> managed_object = Managed<WebSnapshotData>::FromSharedPtr(
isolate, snapshot_data->buffer_size, snapshot_data);
return *managed_object;
}
RUNTIME_FUNCTION(Runtime_WebSnapshotDeserialize) {
if (!FLAG_allow_natives_syntax) {
return ReadOnlyRoots(isolate).undefined_value();
}
HandleScope scope(isolate);
if (args.length() == 0 || args.length() > 2) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kRuntimeWrongNumArgs));
}
if (!args[0].IsForeign()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
Handle<Foreign> foreign_data = args.at<Foreign>(0);
Handle<FixedArray> injected_references =
isolate->factory()->empty_fixed_array();
if (args.length() == 2) {
if (!args[1].IsJSArray()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kInvalidArgument));
}
auto js_array = args.at<JSArray>(1);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, injected_references,
JSReceiver::GetOwnValues(js_array, PropertyFilter::ENUMERABLE_STRINGS));
}
auto data = Managed<WebSnapshotData>::cast(*foreign_data).get();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
WebSnapshotDeserializer deserializer(v8_isolate, data->buffer,
data->buffer_size);
if (!deserializer.Deserialize(injected_references)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kWebSnapshotError));
}
Handle<Object> object;
if (!deserializer.value().ToHandle(&object)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kWebSnapshotError));
}
return *object;
}
RUNTIME_FUNCTION(Runtime_SharedGC) {
SealHandleScope scope(isolate);
isolate->heap()->CollectSharedGarbage(GarbageCollectionReason::kTesting);

View File

@ -140,6 +140,8 @@ namespace internal {
F(HandleDebuggerStatement, 0, 1) \
F(IsBreakOnException, 1, 1) \
F(LiveEditPatchScript, 2, 1) \
F(WebSnapshotDeserialize, -1, 1) \
F(WebSnapshotSerialize, -1, 1) \
F(ProfileCreateSnapshotDataBlob, 0, 1) \
F(ScheduleBreak, 0, 1) \
F(ScriptLocationFromLine2, 4, 1) \

View File

@ -141,7 +141,7 @@ class ObjectCacheIndexMap {
// If |obj| is in the map, immediately return true. Otherwise add it to the
// map and return false. In either case set |*index_out| to the index
// associated with the map.
bool LookupOrInsert(Handle<HeapObject> obj, int* index_out) {
bool LookupOrInsert(HeapObject obj, int* index_out) {
auto find_result = map_.FindOrInsert(obj);
if (!find_result.already_exists) {
*find_result.entry = next_index_++;
@ -149,6 +149,9 @@ class ObjectCacheIndexMap {
*index_out = *find_result.entry;
return find_result.already_exists;
}
bool LookupOrInsert(Handle<HeapObject> obj, int* index_out) {
return LookupOrInsert(*obj, index_out);
}
bool Lookup(HeapObject obj, int* index_out) const {
int* index = map_.Find(obj);

View File

@ -132,7 +132,6 @@ bool IsUnexpectedCodeObject(Isolate* isolate, HeapObject obj) {
} // namespace
#endif // DEBUG
void StartupSerializer::SerializeObjectImpl(Handle<HeapObject> obj) {
PtrComprCageBase cage_base(isolate());
#ifdef DEBUG

View File

@ -41,9 +41,9 @@ void WebSnapshotSerializerDeserializer::Throw(const char* message) {
}
error_message_ = message;
if (!isolate_->has_pending_exception()) {
isolate_->Throw(*isolate_->factory()->NewError(
isolate_->Throw(*factory()->NewError(
MessageTemplate::kWebSnapshotError,
isolate_->factory()->NewStringFromAsciiChecked(error_message_)));
factory()->NewStringFromAsciiChecked(error_message_)));
}
}
@ -191,8 +191,11 @@ PropertyAttributes WebSnapshotSerializerDeserializer::FlagsToAttributes(
}
WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
: WebSnapshotSerializerDeserializer(
reinterpret_cast<v8::internal::Isolate*>(isolate)),
: WebSnapshotSerializer(reinterpret_cast<v8::internal::Isolate*>(isolate)) {
}
WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
: WebSnapshotSerializerDeserializer(isolate),
string_serializer_(isolate_, nullptr),
map_serializer_(isolate_, nullptr),
context_serializer_(isolate_, nullptr),
@ -201,16 +204,49 @@ WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
array_serializer_(isolate_, nullptr),
object_serializer_(isolate_, nullptr),
export_serializer_(isolate_, nullptr),
external_objects_ids_(isolate_->heap()),
string_ids_(isolate_->heap()),
map_ids_(isolate_->heap()),
context_ids_(isolate_->heap()),
function_ids_(isolate_->heap()),
class_ids_(isolate_->heap()),
array_ids_(isolate_->heap()),
object_ids_(isolate_->heap()) {}
object_ids_(isolate_->heap()) {
auto empty_array_list = factory()->empty_array_list();
contexts_ = empty_array_list;
functions_ = empty_array_list;
classes_ = empty_array_list;
arrays_ = empty_array_list;
objects_ = empty_array_list;
}
WebSnapshotSerializer::~WebSnapshotSerializer() {}
bool WebSnapshotSerializer::TakeSnapshot(
Handle<Object> object, MaybeHandle<FixedArray> maybe_externals,
WebSnapshotData& data_out) {
if (string_ids_.size() > 0) {
Throw("Can't reuse WebSnapshotSerializer");
return false;
}
if (!maybe_externals.is_null()) {
ShallowDiscoverExternals(*maybe_externals.ToHandleChecked());
}
if (object->IsHeapObject()) Discover(Handle<HeapObject>::cast(object));
SerializeSource();
SerializeExport(object, factory()->empty_string());
WriteSnapshot(data_out.buffer, data_out.buffer_size);
if (has_error()) {
isolate_->ReportPendingMessages();
return false;
}
return true;
}
bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context,
v8::Local<v8::PrimitiveArray> exports,
WebSnapshotData& data_out) {
@ -220,12 +256,6 @@ bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context,
}
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
contexts_ = ArrayList::New(isolate_, 30);
functions_ = ArrayList::New(isolate_, 30);
classes_ = ArrayList::New(isolate_, 30);
arrays_ = ArrayList::New(isolate_, 30);
objects_ = ArrayList::New(isolate_, 30);
std::unique_ptr<Handle<JSObject>[]> export_objects(
new Handle<JSObject>[exports->Length()]);
for (int i = 0, length = exports->Length(); i < length; ++i) {
@ -244,9 +274,8 @@ bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context,
Throw("Exported object not found");
return false;
}
export_objects[i] = Handle<JSObject>::cast(Utils::OpenHandle(*v8_object));
Discovery(export_objects[i]);
Discover(export_objects[i]);
}
SerializeSource();
@ -334,31 +363,18 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
Throw("Out of memory");
return;
}
total_serializer.WriteRawBytes(kMagicNumber, 4);
total_serializer.WriteUint32(static_cast<uint32_t>(string_count()));
total_serializer.WriteRawBytes(string_serializer_.buffer_,
string_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(map_count()));
total_serializer.WriteRawBytes(map_serializer_.buffer_,
map_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(context_count()));
total_serializer.WriteRawBytes(context_serializer_.buffer_,
context_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(function_count()));
total_serializer.WriteRawBytes(function_serializer_.buffer_,
function_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(array_count()));
total_serializer.WriteRawBytes(array_serializer_.buffer_,
array_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(object_count()));
total_serializer.WriteRawBytes(object_serializer_.buffer_,
object_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(class_count()));
total_serializer.WriteRawBytes(class_serializer_.buffer_,
class_serializer_.buffer_size_);
total_serializer.WriteUint32(export_count_);
total_serializer.WriteRawBytes(export_serializer_.buffer_,
export_serializer_.buffer_size_);
WriteObjects(total_serializer, string_count(), string_serializer_, "strings");
WriteObjects(total_serializer, map_count(), map_serializer_, "maps");
WriteObjects(total_serializer, context_count(), context_serializer_,
"contexts");
WriteObjects(total_serializer, function_count(), function_serializer_,
"functions");
WriteObjects(total_serializer, array_count(), array_serializer_, "arrays");
WriteObjects(total_serializer, object_count(), object_serializer_, "objects");
WriteObjects(total_serializer, class_count(), class_serializer_, "classes");
WriteObjects(total_serializer, export_count_, export_serializer_, "exports");
if (has_error()) {
return;
@ -368,17 +384,26 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
buffer = result.first;
buffer_size = result.second;
}
void WebSnapshotSerializer::WriteObjects(ValueSerializer& destination,
size_t count, ValueSerializer& source,
const char* name) {
if (count > std::numeric_limits<uint32_t>::max()) {
Throw("Too many objects");
return;
}
destination.WriteUint32(static_cast<uint32_t>(count));
destination.WriteRawBytes(source.buffer_, source.buffer_size_);
}
bool WebSnapshotSerializer::InsertIntoIndexMap(ObjectCacheIndexMap& map,
Handle<HeapObject> object,
HeapObject heap_object,
uint32_t& id) {
if (static_cast<uint32_t>(map.size()) >=
std::numeric_limits<uint32_t>::max()) {
Throw("Too many objects");
DisallowGarbageCollection no_gc;
int index_out;
if (external_objects_ids_.Lookup(heap_object, &index_out)) {
return true;
}
int index_out;
bool found = map.LookupOrInsert(object, &index_out);
bool found = map.LookupOrInsert(heap_object, &index_out);
id = static_cast<uint32_t>(index_out);
return found;
}
@ -388,7 +413,7 @@ bool WebSnapshotSerializer::InsertIntoIndexMap(ObjectCacheIndexMap& map,
// - Raw bytes (data)
void WebSnapshotSerializer::SerializeString(Handle<String> string,
uint32_t& id) {
if (InsertIntoIndexMap(string_ids_, string, id)) {
if (InsertIntoIndexMap(string_ids_, *string, id)) {
return;
}
@ -424,7 +449,7 @@ void WebSnapshotSerializer::SerializeString(Handle<String> string,
// - String id (name)
// - If the PropertyAttributesType is CUSTOM: attributes
void WebSnapshotSerializer::SerializeMap(Handle<Map> map, uint32_t& id) {
if (InsertIntoIndexMap(map_ids_, map, id)) {
if (InsertIntoIndexMap(map_ids_, *map, id)) {
return;
}
@ -463,7 +488,8 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map, uint32_t& id) {
? PropertyAttributesType::DEFAULT
: PropertyAttributesType::CUSTOM);
if (map->prototype() == isolate_->context().initial_object_prototype()) {
if (map->prototype() ==
isolate_->native_context()->initial_object_prototype()) {
map_serializer_.WriteUint32(0);
} else {
// TODO(v8:11525): Support non-JSObject prototypes, at least null. Recognize
@ -507,8 +533,7 @@ void WebSnapshotSerializer::SerializeSource() {
return;
}
Factory* factory = isolate_->factory();
Handle<String> source_string = factory->empty_string();
Handle<String> source_string = factory()->empty_string();
int current_interval_start = 0;
int current_interval_end = 0;
for (const auto& interval : source_intervals_) {
@ -528,10 +553,10 @@ void WebSnapshotSerializer::SerializeSource() {
current_interval_end = interval.second;
source_offset_to_compacted_source_offset_[current_interval_start] =
source_string->length();
MaybeHandle<String> new_source_string = factory->NewConsString(
MaybeHandle<String> new_source_string = factory()->NewConsString(
source_string,
factory->NewSubString(full_source_, current_interval_start,
current_interval_end));
factory()->NewSubString(full_source_, current_interval_start,
current_interval_end));
if (!new_source_string.ToHandle(&source_string)) {
Throw("Cannot construct source string");
return;
@ -581,46 +606,54 @@ void WebSnapshotSerializer::SerializeFunctionInfo(ValueSerializer* serializer,
}
}
void WebSnapshotSerializer::Discovery(Handle<Object> start_object) {
void WebSnapshotSerializer::ShallowDiscoverExternals(FixedArray externals) {
DisallowGarbageCollection no_gc;
for (int i = 0; i < externals.length(); i++) {
Object object = externals.get(i);
if (!object.IsHeapObject()) continue;
uint32_t unused_id = 0;
InsertIntoIndexMap(external_objects_ids_, HeapObject::cast(object),
unused_id);
}
}
void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
// The object discovery phase assigns IDs for objects / functions / classes /
// arrays and discovers outgoing references from them. This is needed so that
// e.g., we know all functions upfront and can construct the source code that
// covers them before serializing the functions.
// TODO(v8:11525): Serialize leaf objects first.
discovery_queue_.push(start_object);
while (!discovery_queue_.empty()) {
const Handle<Object>& object = discovery_queue_.front();
if (object->IsHeapObject()) {
switch (HeapObject::cast(*object).map().instance_type()) {
case JS_FUNCTION_TYPE:
DiscoverFunction(Handle<JSFunction>::cast(object));
break;
case JS_CLASS_CONSTRUCTOR_TYPE:
DiscoverClass(Handle<JSFunction>::cast(object));
break;
case JS_OBJECT_TYPE:
DiscoverObject(Handle<JSObject>::cast(object));
break;
case JS_ARRAY_TYPE:
DiscoverArray(Handle<JSArray>::cast(object));
break;
case ODDBALL_TYPE:
case HEAP_NUMBER_TYPE:
case JS_PRIMITIVE_WRAPPER_TYPE:
case JS_REG_EXP_TYPE:
const Handle<HeapObject>& object = discovery_queue_.front();
switch (object->map().instance_type()) {
case JS_FUNCTION_TYPE:
DiscoverFunction(Handle<JSFunction>::cast(object));
break;
case JS_CLASS_CONSTRUCTOR_TYPE:
DiscoverClass(Handle<JSFunction>::cast(object));
break;
case JS_OBJECT_TYPE:
DiscoverObject(Handle<JSObject>::cast(object));
break;
case JS_ARRAY_TYPE:
DiscoverArray(Handle<JSArray>::cast(object));
break;
case ODDBALL_TYPE:
case HEAP_NUMBER_TYPE:
case JS_PRIMITIVE_WRAPPER_TYPE:
case JS_REG_EXP_TYPE:
// Can't contain references to other objects.
break;
default:
if (object->IsString()) {
// Can't contain references to other objects.
break;
default:
if (object->IsString()) {
// Can't contain references to other objects.
break;
} else {
Throw("Unsupported object");
}
}
} else {
Throw("Unsupported object");
}
}
discovery_queue_.pop();
}
@ -628,7 +661,7 @@ void WebSnapshotSerializer::Discovery(Handle<Object> start_object) {
void WebSnapshotSerializer::DiscoverFunction(Handle<JSFunction> function) {
uint32_t id;
if (InsertIntoIndexMap(function_ids_, function, id)) {
if (InsertIntoIndexMap(function_ids_, *function, id)) {
return;
}
@ -641,7 +674,7 @@ void WebSnapshotSerializer::DiscoverFunction(Handle<JSFunction> function) {
void WebSnapshotSerializer::DiscoverClass(Handle<JSFunction> function) {
uint32_t id;
if (InsertIntoIndexMap(class_ids_, function, id)) {
if (InsertIntoIndexMap(class_ids_, *function, id)) {
return;
}
@ -682,22 +715,21 @@ void WebSnapshotSerializer::DiscoverContext(Handle<Context> context) {
}
uint32_t id;
if (InsertIntoIndexMap(context_ids_, context, id)) {
return;
}
if (InsertIntoIndexMap(context_ids_, *context, id)) return;
DCHECK_EQ(id, contexts_->Length());
contexts_ = ArrayList::Add(isolate_, contexts_, context);
Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
int count = scope_info->ContextLocalCount();
DisallowGarbageCollection no_gc;
ScopeInfo scope_info = context->scope_info();
int count = scope_info.ContextLocalCount();
for (int i = 0; i < count; ++i) {
// TODO(v8:11525): support parameters
// TODO(v8:11525): distinguish variable modes
Handle<Object> value(context->get(scope_info->ContextHeaderLength() + i),
isolate_);
discovery_queue_.push(value);
Object value = context->get(scope_info.ContextHeaderLength() + i);
if (!value.IsHeapObject()) continue;
discovery_queue_.push(handle(HeapObject::cast(value), isolate_));
}
}
@ -716,10 +748,9 @@ void WebSnapshotSerializer::DiscoverSource(Handle<JSFunction> function) {
void WebSnapshotSerializer::DiscoverArray(Handle<JSArray> array) {
uint32_t id;
if (InsertIntoIndexMap(array_ids_, array, id)) {
if (InsertIntoIndexMap(array_ids_, *array, id)) {
return;
}
DCHECK_EQ(id, arrays_->Length());
arrays_ = ArrayList::Add(isolate_, arrays_, array);
@ -730,19 +761,18 @@ void WebSnapshotSerializer::DiscoverArray(Handle<JSArray> array) {
return;
}
// TODO(v8:11525): Support sparse arrays & arrays with holes.
uint32_t length = static_cast<uint32_t>(array->length().ToSmi().value());
Handle<FixedArray> elements =
handle(FixedArray::cast(array->elements()), isolate_);
for (uint32_t i = 0; i < length; ++i) {
discovery_queue_.push(handle(elements->get(i), isolate_));
DisallowGarbageCollection no_gc;
FixedArray elements = FixedArray::cast(array->elements());
for (int i = 0; i < elements.length(); ++i) {
Object object = elements.get(i);
if (!object.IsHeapObject()) continue;
discovery_queue_.push(handle(HeapObject::cast(object), isolate_));
}
}
void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
uint32_t id;
if (InsertIntoIndexMap(object_ids_, object, id)) {
return;
}
if (InsertIntoIndexMap(object_ids_, *object, id)) return;
DCHECK_EQ(id, objects_->Length());
objects_ = ArrayList::Add(isolate_, objects_, object);
@ -757,7 +787,8 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
Handle<Map> map(object->map(), isolate_);
// Discover __proto__.
if (map->prototype() != isolate_->context().initial_object_prototype()) {
if (map->prototype() !=
isolate_->native_context()->initial_object_prototype()) {
discovery_queue_.push(handle(map->prototype(), isolate_));
}
@ -768,7 +799,8 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
Handle<Object> value = JSObject::FastPropertyAt(
isolate_, object, details.representation(), field_index);
discovery_queue_.push(value);
if (!value->IsHeapObject()) continue;
discovery_queue_.push(Handle<HeapObject>::cast(value));
}
}
@ -893,7 +925,7 @@ void WebSnapshotSerializer::SerializeArray(Handle<JSArray> array) {
// Format (serialized export):
// - String id (export name)
// - Serialized value (export value)
void WebSnapshotSerializer::SerializeExport(Handle<JSObject> object,
void WebSnapshotSerializer::SerializeExport(Handle<Object> object,
Handle<String> export_name) {
++export_count_;
uint32_t string_id = 0;
@ -922,6 +954,13 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
return;
}
int external_id;
if (external_objects_ids_.Lookup(HeapObject::cast(*object), &external_id)) {
serializer.WriteUint32(ValueType::EXTERNAL_ID);
serializer.WriteUint32(static_cast<uint32_t>(external_id));
return;
}
DCHECK(object->IsHeapObject());
switch (HeapObject::cast(*object).map().instance_type()) {
case ODDBALL_TYPE:
@ -1031,6 +1070,14 @@ uint32_t WebSnapshotSerializer::GetObjectId(JSObject object) {
return static_cast<uint32_t>(id);
}
uint32_t WebSnapshotSerializer::GetExternalId(HeapObject object) {
int id;
bool return_value = external_objects_ids_.Lookup(object, &id);
DCHECK(return_value);
USE(return_value);
return static_cast<uint32_t>(id);
}
WebSnapshotDeserializer::WebSnapshotDeserializer(v8::Isolate* isolate,
const uint8_t* data,
size_t buffer_size)
@ -1048,10 +1095,9 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
base::Vector<const uint8_t> buffer)
: WebSnapshotSerializerDeserializer(isolate),
script_name_(script_name),
deserializer_(isolate_, buffer.data(), buffer.length()) {
isolate_->heap()->AddGCEpilogueCallback(UpdatePointersCallback,
v8::kGCTypeAll, this);
Handle<FixedArray> empty_array = isolate_->factory()->empty_fixed_array();
deserializer_(isolate_, buffer.data(), buffer.length()),
roots_(isolate) {
Handle<FixedArray> empty_array = factory()->empty_fixed_array();
strings_handle_ = empty_array;
maps_handle_ = empty_array;
contexts_handle_ = empty_array;
@ -1059,6 +1105,9 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
classes_handle_ = empty_array;
arrays_handle_ = empty_array;
objects_handle_ = empty_array;
external_references_handle_ = empty_array;
isolate_->heap()->AddGCEpilogueCallback(UpdatePointersCallback,
v8::kGCTypeAll, this);
}
WebSnapshotDeserializer::~WebSnapshotDeserializer() {
@ -1073,6 +1122,7 @@ void WebSnapshotDeserializer::UpdatePointers() {
classes_ = *classes_handle_;
arrays_ = *arrays_handle_;
objects_ = *objects_handle_;
external_references_ = *external_references_handle_;
}
// static
@ -1143,8 +1193,15 @@ void WebSnapshotDeserializer::Throw(const char* message) {
WebSnapshotSerializerDeserializer::Throw(message);
}
bool WebSnapshotDeserializer::Deserialize() {
bool WebSnapshotDeserializer::Deserialize(
MaybeHandle<FixedArray> external_references) {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize);
if (external_references.ToHandle(&external_references_handle_)) {
external_references_ = *external_references_handle_;
} else {
external_references_handle_ = roots_.empty_fixed_array_handle();
}
if (deserialized_) {
Throw("Can't reuse WebSnapshotDeserializer");
return false;
@ -1163,6 +1220,7 @@ bool WebSnapshotDeserializer::Deserialize() {
if (!DeserializeScript()) {
return false;
}
if (FLAG_trace_web_snapshot) {
double ms = timer.Elapsed().InMillisecondsF();
PrintF("[Deserializing snapshot (%zu bytes) took %0.3f ms]\n", buffer_size,
@ -1239,7 +1297,7 @@ void WebSnapshotDeserializer::DeserializeStrings() {
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
strings_handle_ = isolate_->factory()->NewFixedArray(string_count_);
strings_handle_ = factory()->NewFixedArray(string_count_);
strings_ = *strings_handle_;
for (uint32_t i = 0; i < string_count_; ++i) {
MaybeHandle<String> maybe_string =
@ -1258,11 +1316,11 @@ String WebSnapshotDeserializer::ReadString(bool internalize) {
uint32_t string_id;
if (!deserializer_.ReadUint32(&string_id) || string_id >= string_count_) {
Throw("malformed string id\n");
return ReadOnlyRoots(isolate_).empty_string();
return roots_.empty_string();
}
String string = String::cast(strings_.get(string_id));
if (internalize && !string.IsInternalizedString(isolate_)) {
string = *isolate_->factory()->InternalizeString(handle(string, isolate_));
string = *factory()->InternalizeString(handle(string, isolate_));
strings_.set(string_id, string);
}
return string;
@ -1275,7 +1333,7 @@ void WebSnapshotDeserializer::DeserializeMaps() {
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
maps_handle_ = isolate_->factory()->NewFixedArray(map_count_);
maps_handle_ = factory()->NewFixedArray(map_count_);
maps_ = *maps_handle_;
for (uint32_t i = 0; i < map_count_; ++i) {
uint32_t map_type;
@ -1324,7 +1382,7 @@ void WebSnapshotDeserializer::DeserializeMaps() {
}
Handle<DescriptorArray> descriptors =
isolate_->factory()->NewDescriptorArray(property_count, 0);
factory()->NewDescriptorArray(property_count, 0);
// for (uint32_t p = 0; p < property_count; ++p) {
for (InternalIndex i : InternalIndex::Range(property_count)) {
PropertyAttributes attributes = PropertyAttributes::NONE;
@ -1348,14 +1406,14 @@ void WebSnapshotDeserializer::DeserializeMaps() {
DCHECK_EQ(descriptors->number_of_descriptors(), property_count);
descriptors->Sort();
Handle<Map> map = isolate_->factory()->NewMap(
JS_OBJECT_TYPE, JSObject::kHeaderSize * kTaggedSize, HOLEY_ELEMENTS, 0);
Handle<Map> map = factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize,
HOLEY_ELEMENTS, 0);
map->InitializeDescriptors(isolate_, *descriptors);
// TODO(v8:11525): Set 'constructor'.
if (prototype_id == 0) {
// Use Object.prototype as the prototype.
map->set_prototype(isolate_->context().initial_object_prototype(),
map->set_prototype(isolate_->native_context()->initial_object_prototype(),
UPDATE_WRITE_BARRIER);
} else {
// TODO(v8::11525): Implement stricter checks, e.g., disallow cycles.
@ -1380,7 +1438,7 @@ void WebSnapshotDeserializer::DeserializeContexts() {
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
contexts_handle_ = isolate_->factory()->NewFixedArray(context_count_);
contexts_handle_ = factory()->NewFixedArray(context_count_);
contexts_ = *contexts_handle_;
for (uint32_t i = 0; i < context_count_; ++i) {
uint32_t context_type;
@ -1445,12 +1503,10 @@ void WebSnapshotDeserializer::DeserializeContexts() {
Handle<Context> context;
switch (context_type) {
case ContextType::FUNCTION:
context =
isolate_->factory()->NewFunctionContext(parent_context, scope_info);
context = factory()->NewFunctionContext(parent_context, scope_info);
break;
case ContextType::BLOCK:
context =
isolate_->factory()->NewBlockContext(parent_context, scope_info);
context = factory()->NewBlockContext(parent_context, scope_info);
break;
default:
Throw("Unsupported context type");
@ -1518,7 +1574,7 @@ Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo(
? ScopeInfo::kPositionInfoEntries
: 0) +
(has_parent ? 1 : 0) + 2 * variable_count;
Handle<ScopeInfo> scope_info = isolate_->factory()->NewScopeInfo(length);
Handle<ScopeInfo> scope_info = factory()->NewScopeInfo(length);
{
DisallowGarbageCollection no_gc;
ScopeInfo raw = *scope_info;
@ -1541,13 +1597,12 @@ Handle<JSFunction> WebSnapshotDeserializer::CreateJSFunction(
uint32_t parameter_count, uint32_t flags, uint32_t context_id) {
// TODO(v8:11525): Deduplicate the SFIs for class methods.
FunctionKind kind = FunctionFlagsToFunctionKind(flags);
Handle<SharedFunctionInfo> shared =
isolate_->factory()->NewSharedFunctionInfo(
isolate_->factory()->empty_string(), MaybeHandle<Code>(),
Builtin::kCompileLazy, kind);
Handle<SharedFunctionInfo> shared = factory()->NewSharedFunctionInfo(
factory()->empty_string(), MaybeHandle<Code>(), Builtin::kCompileLazy,
kind);
Handle<UncompiledData> uncompiled_data =
isolate_->factory()->NewUncompiledDataWithoutPreparseData(
ReadOnlyRoots(isolate_).empty_string_handle(), start_position,
factory()->NewUncompiledDataWithoutPreparseData(
roots_.empty_string_handle(), start_position,
start_position + length);
{
DisallowGarbageCollection no_gc;
@ -1592,17 +1647,17 @@ void WebSnapshotDeserializer::DeserializeFunctions() {
return;
}
STATIC_ASSERT(kMaxItemCount + 1 <= FixedArray::kMaxLength);
functions_handle_ = isolate_->factory()->NewFixedArray(function_count_);
functions_handle_ = factory()->NewFixedArray(function_count_);
functions_ = *functions_handle_;
// Overallocate the array for SharedFunctionInfos; functions which we
// deserialize soon will create more SharedFunctionInfos when called.
shared_function_infos_handle_ = isolate_->factory()->NewWeakFixedArray(
shared_function_infos_handle_ = factory()->NewWeakFixedArray(
WeakArrayList::CapacityForLength(function_count_ + 1),
AllocationType::kOld);
shared_function_infos_ = *shared_function_infos_handle_;
shared_function_info_table_ = ObjectHashTable::New(isolate_, function_count_);
script_ = isolate_->factory()->NewScript(isolate_->factory()->empty_string());
script_ = factory()->NewScript(factory()->empty_string());
{
DisallowGarbageCollection no_gc;
Script raw = *script_;
@ -1660,7 +1715,7 @@ void WebSnapshotDeserializer::DeserializeClasses() {
return;
}
STATIC_ASSERT(kMaxItemCount + 1 <= FixedArray::kMaxLength);
classes_handle_ = isolate_->factory()->NewFixedArray(class_count_);
classes_handle_ = factory()->NewFixedArray(class_count_);
classes_ = *classes_handle_;
// Grow the array for SharedFunctionInfos.
@ -1719,7 +1774,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
objects_handle_ = isolate_->factory()->NewFixedArray(object_count_);
objects_handle_ = factory()->NewFixedArray(object_count_);
objects_ = *objects_handle_;
for (; current_object_count_ < object_count_; ++current_object_count_) {
uint32_t map_id;
@ -1734,7 +1789,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
// TODO(v8:11525): In-object properties.
Handle<Map> map(raw_map, isolate_);
Handle<PropertyArray> property_array =
isolate_->factory()->NewPropertyArray(no_properties);
factory()->NewPropertyArray(no_properties);
for (int i = 0; i < no_properties; ++i) {
Object value = ReadValue(property_array, i);
DisallowGarbageCollection no_gc;
@ -1754,7 +1809,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
}
property_array->set(i, value);
}
Handle<JSObject> object = isolate_->factory()->NewJSObjectFromMap(map);
Handle<JSObject> object = factory()->NewJSObjectFromMap(map);
object->set_raw_properties_or_hash(*property_array, kRelaxedStore);
objects_.set(static_cast<int>(current_object_count_), *object);
}
@ -1768,7 +1823,7 @@ void WebSnapshotDeserializer::DeserializeArrays() {
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
arrays_handle_ = isolate_->factory()->NewFixedArray(array_count_);
arrays_handle_ = factory()->NewFixedArray(array_count_);
arrays_ = *arrays_handle_;
for (; current_array_count_ < array_count_; ++current_array_count_) {
uint32_t length;
@ -1776,7 +1831,7 @@ void WebSnapshotDeserializer::DeserializeArrays() {
Throw("Malformed array");
return;
}
Handle<FixedArray> elements = isolate_->factory()->NewFixedArray(length);
Handle<FixedArray> elements = factory()->NewFixedArray(length);
ElementsKind elements_kind = PACKED_SMI_ELEMENTS;
for (uint32_t i = 0; i < length; ++i) {
Object value = ReadValue(elements, i);
@ -1786,8 +1841,8 @@ void WebSnapshotDeserializer::DeserializeArrays() {
}
elements->set(static_cast<int>(i), value);
}
Handle<JSArray> array = isolate_->factory()->NewJSArrayWithElements(
elements, elements_kind, length);
Handle<JSArray> array =
factory()->NewJSArrayWithElements(elements, elements_kind, length);
arrays_.set(static_cast<int>(current_array_count_), *array);
}
}
@ -1808,6 +1863,7 @@ void WebSnapshotDeserializer::DeserializeExports() {
dictionary = GlobalDictionary::EnsureCapacity(
isolate_, dictionary, dictionary->NumberOfElements() + count,
AllocationType::kYoung);
bool has_exported_values = false;
// TODO(v8:11525): The code below skips checks, in particular
// LookupIterator::UpdateProtectors and
@ -1818,6 +1874,15 @@ void WebSnapshotDeserializer::DeserializeExports() {
// No deferred references should occur at this point, since all objects have
// been deserialized.
Object export_value = ReadValue();
if (export_name->length() == 0 && i == 0) {
// Hack: treat the first empty-string-named export value as a return value
// from the deserializer.
CHECK_EQ(i, 0);
return_value_ = handle(export_value, isolate_);
continue;
}
DisallowGarbageCollection no_gc;
// Check for the correctness of the snapshot (thus far) before producing
// something observable. TODO(v8:11525): Strictly speaking, we should
@ -1830,22 +1895,23 @@ void WebSnapshotDeserializer::DeserializeExports() {
PropertyCell::InitialType(isolate_, export_value));
Handle<Object> export_value_handle(export_value, isolate_);
AllowGarbageCollection allow_gc;
Handle<PropertyCell> transition_cell = isolate_->factory()->NewPropertyCell(
Handle<PropertyCell> transition_cell = factory()->NewPropertyCell(
export_name, property_details, export_value_handle);
dictionary =
GlobalDictionary::Add(isolate_, dictionary, export_name,
transition_cell, property_details, &entry);
has_exported_values = true;
}
if (!has_exported_values) return;
global->set_global_dictionary(*dictionary, kReleaseStore);
JSObject::InvalidatePrototypeChains(global->map(isolate_));
}
Object WebSnapshotDeserializer::ReadValue(
Handle<HeapObject> object_for_deferred_reference,
uint32_t index_for_deferred_reference) {
Object WebSnapshotDeserializer::ReadValue(Handle<HeapObject> container,
uint32_t container_index) {
uint32_t value_type;
Factory* factory = isolate_->factory();
// TODO(v8:11525): Consider adding a ReadByte.
if (!deserializer_.ReadUint32(&value_type)) {
Throw("Malformed variable");
@ -1854,110 +1920,32 @@ Object WebSnapshotDeserializer::ReadValue(
return Smi::zero();
}
switch (value_type) {
case ValueType::FALSE_CONSTANT: {
return ReadOnlyRoots(isolate_).false_value();
}
case ValueType::TRUE_CONSTANT: {
return ReadOnlyRoots(isolate_).true_value();
}
case ValueType::NULL_CONSTANT: {
return ReadOnlyRoots(isolate_).null_value();
}
case ValueType::UNDEFINED_CONSTANT: {
return ReadOnlyRoots(isolate_).undefined_value();
}
case ValueType::INTEGER: {
Maybe<int32_t> number = deserializer_.ReadZigZag<int32_t>();
if (number.IsNothing()) {
Throw("Malformed integer");
return Smi::zero();
}
return *factory->NewNumberFromInt(number.FromJust());
}
case ValueType::DOUBLE: {
double number;
if (!deserializer_.ReadDouble(&number)) {
Throw("Malformed double");
return Smi::zero();
}
return *factory->NewNumber(number);
}
case ValueType::STRING_ID: {
case ValueType::FALSE_CONSTANT:
return roots_.false_value();
case ValueType::TRUE_CONSTANT:
return roots_.true_value();
case ValueType::NULL_CONSTANT:
return roots_.null_value();
case ValueType::UNDEFINED_CONSTANT:
return roots_.undefined_value();
case ValueType::INTEGER:
return ReadInteger();
case ValueType::DOUBLE:
return ReadNumber();
case ValueType::STRING_ID:
return ReadString(false);
}
case ValueType::ARRAY_ID:
uint32_t array_id;
if (!deserializer_.ReadUint32(&array_id) || array_id >= kMaxItemCount) {
Throw("Malformed variable");
return Smi::zero();
}
if (array_id < current_array_count_) {
return arrays_.get(array_id);
}
// The array hasn't been deserialized yet.
return AddDeferredReference(object_for_deferred_reference,
index_for_deferred_reference, ARRAY_ID,
array_id);
return ReadArray(container, container_index);
case ValueType::OBJECT_ID:
uint32_t object_id;
if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount) {
Throw("Malformed variable");
return Smi::zero();
}
if (object_id < current_object_count_) {
return objects_.get(object_id);
}
// The object hasn't been deserialized yet.
return AddDeferredReference(object_for_deferred_reference,
index_for_deferred_reference, OBJECT_ID,
object_id);
case ValueType::FUNCTION_ID: {
uint32_t function_id;
if (!deserializer_.ReadUint32(&function_id) ||
function_id >= function_count_) {
Throw("Malformed object property");
return Smi::zero();
}
if (function_id < current_function_count_) {
return functions_.get(function_id);
}
// The function hasn't been deserialized yet.
return AddDeferredReference(object_for_deferred_reference,
index_for_deferred_reference, FUNCTION_ID,
function_id);
}
case ValueType::CLASS_ID: {
uint32_t class_id;
if (!deserializer_.ReadUint32(&class_id) || class_id >= kMaxItemCount) {
Throw("Malformed object property");
return Smi::zero();
}
if (class_id < current_class_count_) {
return classes_.get(class_id);
}
// The class hasn't been deserialized yet.
return AddDeferredReference(object_for_deferred_reference,
index_for_deferred_reference, CLASS_ID,
class_id);
}
case ValueType::REGEXP: {
Handle<String> pattern(ReadString(false), isolate_);
Handle<String> flags_string(ReadString(false), isolate_);
base::Optional<JSRegExp::Flags> flags =
JSRegExp::FlagsFromString(isolate_, flags_string);
if (!flags.has_value()) {
Throw("Malformed flags in regular expression");
return Smi::zero();
}
MaybeHandle<JSRegExp> maybe_regexp =
JSRegExp::New(isolate_, pattern, flags.value());
Handle<JSRegExp> regexp;
if (!maybe_regexp.ToHandle(&regexp)) {
Throw("Malformed RegExp");
return Smi::zero();
}
return *regexp;
}
return ReadObject(container, container_index);
case ValueType::FUNCTION_ID:
return ReadFunction(container, container_index);
case ValueType::CLASS_ID:
return ReadClass(container, container_index);
case ValueType::REGEXP:
return ReadRegexp();
case ValueType::EXTERNAL_ID:
return ReadExternalReference();
default:
// TODO(v8:11525): Handle other value types.
Throw("Unsupported value type");
@ -1965,6 +1953,110 @@ Object WebSnapshotDeserializer::ReadValue(
}
}
Object WebSnapshotDeserializer::ReadInteger() {
Maybe<int32_t> number = deserializer_.ReadZigZag<int32_t>();
if (number.IsNothing()) {
Throw("Malformed integer");
return Smi::zero();
}
return *factory()->NewNumberFromInt(number.FromJust());
}
Object WebSnapshotDeserializer::ReadNumber() {
double number;
if (!deserializer_.ReadDouble(&number)) {
Throw("Malformed double");
return Smi::zero();
}
return *factory()->NewNumber(number);
}
Object WebSnapshotDeserializer::ReadArray(Handle<HeapObject> container,
uint32_t index) {
uint32_t array_id;
if (!deserializer_.ReadUint32(&array_id) || array_id >= kMaxItemCount) {
Throw("Malformed variable");
return Smi::zero();
}
if (array_id < current_array_count_) {
return arrays_.get(array_id);
}
// The array hasn't been deserialized yet.
return AddDeferredReference(container, index, ARRAY_ID, array_id);
}
Object WebSnapshotDeserializer::ReadObject(Handle<HeapObject> container,
uint32_t index) {
uint32_t object_id;
if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount) {
Throw("Malformed variable");
return Smi::zero();
}
if (object_id < current_object_count_) {
return objects_.get(object_id);
}
// The object hasn't been deserialized yet.
return AddDeferredReference(container, index, OBJECT_ID, object_id);
}
Object WebSnapshotDeserializer::ReadFunction(Handle<HeapObject> container,
uint32_t index) {
uint32_t function_id;
if (!deserializer_.ReadUint32(&function_id) ||
function_id >= function_count_) {
Throw("Malformed object property");
return Smi::zero();
}
if (function_id < current_function_count_) {
return functions_.get(function_id);
}
// The function hasn't been deserialized yet.
return AddDeferredReference(container, index, FUNCTION_ID, function_id);
}
Object WebSnapshotDeserializer::ReadClass(Handle<HeapObject> container,
uint32_t index) {
uint32_t class_id;
if (!deserializer_.ReadUint32(&class_id) || class_id >= kMaxItemCount) {
Throw("Malformed object property");
return Smi::zero();
}
if (class_id < current_class_count_) {
return classes_.get(class_id);
}
// The class hasn't been deserialized yet.
return AddDeferredReference(container, index, CLASS_ID, class_id);
}
Object WebSnapshotDeserializer::ReadRegexp() {
Handle<String> pattern(ReadString(false), isolate_);
Handle<String> flags_string(ReadString(false), isolate_);
base::Optional<JSRegExp::Flags> flags =
JSRegExp::FlagsFromString(isolate_, flags_string);
if (!flags.has_value()) {
Throw("Malformed flags in regular expression");
return Smi::zero();
}
MaybeHandle<JSRegExp> maybe_regexp =
JSRegExp::New(isolate_, pattern, flags.value());
Handle<JSRegExp> regexp;
if (!maybe_regexp.ToHandle(&regexp)) {
Throw("Malformed RegExp");
return Smi::zero();
}
return *regexp;
}
Object WebSnapshotDeserializer::ReadExternalReference() {
uint32_t ref_id;
if (!deserializer_.ReadUint32(&ref_id) ||
ref_id > static_cast<uint32_t>(external_references_.length())) {
Throw("Malformed class / function");
return Smi::zero();
}
return external_references_.get(ref_id);
}
void WebSnapshotDeserializer::ReadFunctionPrototype(
Handle<JSFunction> function) {
uint32_t object_id;
@ -2026,7 +2118,7 @@ HeapObject WebSnapshotDeserializer::AddDeferredReference(
break;
}
Throw(message);
return ReadOnlyRoots(isolate_).undefined_value();
return roots_.undefined_value();
}
DCHECK(container->IsPropertyArray() || container->IsContext() ||
container->IsFixedArray() || container->IsJSFunction() ||
@ -2035,15 +2127,13 @@ HeapObject WebSnapshotDeserializer::AddDeferredReference(
isolate_, deferred_references_, container, Smi::FromInt(index),
Smi::FromInt(target_type), Smi::FromInt(target_index));
// Use HeapObject as placeholder since this might break elements kinds.
return ReadOnlyRoots(isolate_).undefined_value();
return roots_.undefined_value();
}
void WebSnapshotDeserializer::ProcessDeferredReferences() {
// Check for error now, since the FixedArrays below might not have been
// created if there was an error.
if (has_error()) {
return;
}
if (has_error()) return;
DisallowGarbageCollection no_gc;
ArrayList raw_deferred_references = *deferred_references_;

View File

@ -52,7 +52,8 @@ class WebSnapshotSerializerDeserializer {
OBJECT_ID,
FUNCTION_ID,
CLASS_ID,
REGEXP
REGEXP,
EXTERNAL_ID
};
static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'};
@ -82,6 +83,9 @@ class WebSnapshotSerializerDeserializer {
: isolate_(isolate) {}
// Not virtual, on purpose (because it doesn't need to be).
void Throw(const char* message);
inline Factory* factory() const { return isolate_->factory(); }
Isolate* isolate_;
const char* error_message_ = nullptr;
@ -111,11 +115,15 @@ class V8_EXPORT WebSnapshotSerializer
: public WebSnapshotSerializerDeserializer {
public:
explicit WebSnapshotSerializer(v8::Isolate* isolate);
explicit WebSnapshotSerializer(Isolate* isolate);
~WebSnapshotSerializer();
bool TakeSnapshot(v8::Local<v8::Context> context,
v8::Local<v8::PrimitiveArray> exports,
WebSnapshotData& data_out);
bool TakeSnapshot(Handle<Object> object, MaybeHandle<FixedArray> block_list,
WebSnapshotData& data_out);
// For inspecting the state after taking a snapshot.
uint32_t string_count() const {
@ -150,12 +158,15 @@ class V8_EXPORT WebSnapshotSerializer
void SerializePendingItems();
void WriteSnapshot(uint8_t*& buffer, size_t& buffer_size);
void WriteObjects(ValueSerializer& destination, size_t count,
ValueSerializer& source, const char* name);
// Returns true if the object was already in the map, false if it was added.
bool InsertIntoIndexMap(ObjectCacheIndexMap& map, Handle<HeapObject> object,
bool InsertIntoIndexMap(ObjectCacheIndexMap& map, HeapObject heap_object,
uint32_t& id);
void Discovery(Handle<Object> object);
void ShallowDiscoverExternals(FixedArray externals);
void Discover(Handle<HeapObject> object);
void DiscoverFunction(Handle<JSFunction> function);
void DiscoverClass(Handle<JSFunction> function);
void DiscoverContextAndPrototype(Handle<JSFunction> function);
@ -177,7 +188,7 @@ class V8_EXPORT WebSnapshotSerializer
void SerializeArray(Handle<JSArray> array);
void SerializeObject(Handle<JSObject> object);
void SerializeExport(Handle<JSObject> object, Handle<String> export_name);
void SerializeExport(Handle<Object> object, Handle<String> export_name);
void WriteValue(Handle<Object> object, ValueSerializer& serializer);
uint32_t GetFunctionId(JSFunction function);
@ -185,6 +196,7 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t GetContextId(Context context);
uint32_t GetArrayId(JSArray array);
uint32_t GetObjectId(JSObject object);
uint32_t GetExternalId(HeapObject object);
ValueSerializer string_serializer_;
ValueSerializer map_serializer_;
@ -202,6 +214,10 @@ class V8_EXPORT WebSnapshotSerializer
Handle<ArrayList> arrays_;
Handle<ArrayList> objects_;
// IndexMap to keep track of explicitly blocked external objects and
// non-serializable/not-supporte objects (e.g. API Objects).
ObjectCacheIndexMap external_objects_ids_;
// ObjectCacheIndexMap implements fast lookup item -> id.
ObjectCacheIndexMap string_ids_;
ObjectCacheIndexMap map_ids_;
@ -212,7 +228,7 @@ class V8_EXPORT WebSnapshotSerializer
ObjectCacheIndexMap object_ids_;
uint32_t export_count_ = 0;
std::queue<Handle<Object>> discovery_queue_;
std::queue<Handle<HeapObject>> discovery_queue_;
// For constructing the minimal, "compacted", source string to cover all
// function bodies.
@ -232,7 +248,7 @@ class V8_EXPORT WebSnapshotDeserializer
size_t buffer_size);
WebSnapshotDeserializer(Isolate* isolate, Handle<Script> snapshot_as_script);
~WebSnapshotDeserializer();
bool Deserialize();
bool Deserialize(MaybeHandle<FixedArray> external_references = {});
// For inspecting the state after deserializing a snapshot.
uint32_t string_count() const { return string_count_; }
@ -251,6 +267,8 @@ class V8_EXPORT WebSnapshotDeserializer
void UpdatePointers();
MaybeHandle<Object> value() const { return return_value_; }
private:
WebSnapshotDeserializer(Isolate* isolate, Handle<Object> script_name,
base::Vector<const uint8_t> buffer);
@ -263,7 +281,6 @@ class V8_EXPORT WebSnapshotDeserializer
WebSnapshotDeserializer& operator=(const WebSnapshotDeserializer&) = delete;
void DeserializeStrings();
String ReadString(bool internalize = false);
void DeserializeMaps();
void DeserializeContexts();
Handle<ScopeInfo> CreateScopeInfo(uint32_t variable_count, bool has_parent,
@ -277,9 +294,21 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeArrays();
void DeserializeObjects();
void DeserializeExports();
Object ReadValue(
Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(),
uint32_t index_for_deferred_reference = 0);
Object ReadInteger();
Object ReadNumber();
String ReadString(bool internalize = false);
Object ReadArray(Handle<HeapObject> container, uint32_t container_index);
Object ReadObject(Handle<HeapObject> container, uint32_t container_index);
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index);
Object ReadClass(Handle<HeapObject> container, uint32_t container_index);
Object ReadRegexp();
Object ReadExternalReference();
void ReadFunctionPrototype(Handle<JSFunction> function);
bool SetFunctionPrototype(JSFunction function, JSReceiver prototype);
@ -311,6 +340,9 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> objects_handle_;
FixedArray objects_;
Handle<FixedArray> external_references_handle_;
FixedArray external_references_;
Handle<ArrayList> deferred_references_;
Handle<WeakFixedArray> shared_function_infos_handle_;
@ -321,6 +353,8 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<Script> script_;
Handle<Object> script_name_;
Handle<Object> return_value_;
uint32_t string_count_ = 0;
uint32_t map_count_ = 0;
uint32_t context_count_ = 0;
@ -334,6 +368,7 @@ class V8_EXPORT WebSnapshotDeserializer
uint32_t current_object_count_ = 0;
ValueDeserializer deserializer_;
ReadOnlyRoots roots_;
bool deserialized_ = false;
};

View File

@ -0,0 +1,57 @@
// 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
const external_1 = {external: 1};
const external_2 = {external: 2};
const object = {
a: [1,2],
b: external_1,
c: [external_1, external_2],
d: { d_a: external_2 }
};
(function testNoExternals() {
const snapshot = %WebSnapshotSerialize(object);
const deserialized = %WebSnapshotDeserialize(snapshot);
%HeapObjectVerify(deserialized);
assertEquals(deserialized, object);
assertEquals(deserialized.b, external_1);
assertNotSame(deserialized.b, external_1);
assertEquals(deserialized.d.d_a, external_2);
assertNotSame(deserialized.d.d_a, external_2);
})();
(function testOneExternals() {
const externals = [ external_1];
const snapshot = %WebSnapshotSerialize(object, externals);
const replaced_externals = [{replacement:1}]
const deserialized = %WebSnapshotDeserialize(snapshot, replaced_externals);
%HeapObjectVerify(deserialized);
assertEquals(deserialized.a, object.a);
assertSame(deserialized.b, replaced_externals[0]);
assertArrayEquals(deserialized.c, [replaced_externals[0], external_2]);
assertSame(deserialized.c[0], replaced_externals[0]);
assertNotSame(deserialized.c[1], external_2);
assertEquals(deserialized.d.d_a, external_2);
assertNotSame(deserialized.d.d_a, external_2);
})();
(function testTwoExternals() {
const externals = [external_1, external_2];
const snapshot = %WebSnapshotSerialize(object, externals);
const replaced_externals = [{replacement:1}, {replacement:2}]
const deserialized = %WebSnapshotDeserialize(snapshot, replaced_externals);
%HeapObjectVerify(deserialized);
assertEquals(deserialized.a, object.a);
assertSame(deserialized.b, replaced_externals[0]);
assertArrayEquals(deserialized.c, replaced_externals);
assertSame(deserialized.c[0], replaced_externals[0]);
assertSame(deserialized.c[1], replaced_externals[1]);
assertSame(deserialized.d.d_a, replaced_externals[1]);
})();