Commit ce488c01 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

Reland: [web snapshot] Recognize builtins

Builtins are not snapshotted, but instead we insert "builtin wrappers"
into the snapshot, and create references to the corresponding builtin
when deserializing.

Subclassing builtins will be implemented in a follow-up CL.

First version: https://chromium-review.googlesource.com/c/v8/v8/+/3630080
Fix: initialize builtin_objects_handle_

Bug: v8:11525,v8:12820
Change-Id: Ia2b5d41af5d7f577f1b02356b22a8760963009e4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3635718Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80430}
parent fee23365
......@@ -42,7 +42,7 @@ BUILTIN(WebSnapshotSerialize) {
}
if (!block_list_js_array.is_null() &&
static_cast<uint32_t>(block_list->length()) <
serializer.external_objects_count()) {
serializer.external_object_count()) {
Handle<FixedArray> externals = serializer.GetExternals();
Handle<Map> map = JSObject::GetElementsTransitionMap(block_list_js_array,
PACKED_ELEMENTS);
......
......@@ -484,6 +484,7 @@ class RuntimeCallTimer final {
V(UpdateProtector) \
V(WebSnapshotDeserialize) \
V(WebSnapshotDeserialize_Arrays) \
V(WebSnapshotDeserialize_BuiltinObjects) \
V(WebSnapshotDeserialize_Classes) \
V(WebSnapshotDeserialize_Contexts) \
V(WebSnapshotDeserialize_Exports) \
......
......@@ -23,6 +23,7 @@ namespace v8 {
namespace internal {
constexpr uint8_t WebSnapshotSerializerDeserializer::kMagicNumber[4];
constexpr int WebSnapshotSerializerDeserializer::kBuiltinObjectCount;
// When encountering an error during deserializing, we note down the error but
// don't bail out from processing the snapshot further. This is to speed up
......@@ -47,6 +48,16 @@ void WebSnapshotSerializerDeserializer::Throw(const char* message) {
}
}
void WebSnapshotSerializerDeserializer::IterateBuiltinObjects(
std::function<void(String, HeapObject)> func) {
// TODO(v8:11525): Add more builtins.
auto roots = ReadOnlyRoots(isolate_);
func(roots.Error_string(), isolate_->context().error_function());
func(*factory()->NewStringFromAsciiChecked("Error.prototype"),
isolate_->context().error_function().instance_prototype());
STATIC_ASSERT(kBuiltinObjectCount == 2);
}
uint32_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags(
FunctionKind kind) {
// TODO(v8:11525): Support more function kinds.
......@@ -199,13 +210,14 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
string_serializer_(isolate_, nullptr),
symbol_serializer_(isolate_, nullptr),
map_serializer_(isolate_, nullptr),
builtin_object_serializer_(isolate_, nullptr),
context_serializer_(isolate_, nullptr),
function_serializer_(isolate_, nullptr),
class_serializer_(isolate_, nullptr),
array_serializer_(isolate_, nullptr),
object_serializer_(isolate_, nullptr),
export_serializer_(isolate_, nullptr),
external_objects_ids_(isolate_->heap()),
external_object_ids_(isolate_->heap()),
string_ids_(isolate_->heap()),
symbol_ids_(isolate_->heap()),
map_ids_(isolate_->heap()),
......@@ -214,6 +226,8 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
class_ids_(isolate_->heap()),
array_ids_(isolate_->heap()),
object_ids_(isolate_->heap()),
builtin_object_to_name_(isolate_->heap()),
builtin_object_ids_(isolate_->heap()),
all_strings_(isolate_->heap()) {
auto empty_array_list = factory()->empty_array_list();
strings_ = empty_array_list;
......@@ -239,6 +253,10 @@ bool WebSnapshotSerializer::TakeSnapshot(
ShallowDiscoverExternals(*maybe_externals.ToHandleChecked());
}
v8::Local<v8::Context> context =
reinterpret_cast<v8::Isolate*>(isolate_)->GetCurrentContext();
ShallowDiscoverBuiltinObjects(context);
if (object->IsHeapObject()) Discover(Handle<HeapObject>::cast(object));
ConstructSource();
......@@ -265,8 +283,10 @@ bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context,
}
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
std::unique_ptr<Handle<JSObject>[]> export_objects(
new Handle<JSObject>[exports->Length()]);
ShallowDiscoverBuiltinObjects(context);
Handle<FixedArray> export_objects =
isolate_->factory()->NewFixedArray(exports->Length());
for (int i = 0, length = exports->Length(); i < length; ++i) {
v8::Local<v8::String> str =
exports->Get(v8_isolate, i)->ToString(context).ToLocalChecked();
......@@ -285,8 +305,15 @@ 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));
Discover(export_objects[i]);
auto object = Handle<JSObject>::cast(Utils::OpenHandle(*v8_object));
export_objects->set(i, *object);
Discover(object);
// The error messages will be confusing if we continue running code when
// already in the error state.
if (has_error()) {
isolate_->ReportPendingMessages();
return false;
}
}
ConstructSource();
......@@ -297,7 +324,7 @@ bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context,
if (str->Length() == 0) {
continue;
}
SerializeExport(export_objects[i],
SerializeExport(handle(export_objects->get(i), isolate_),
Handle<String>::cast(Utils::OpenHandle(*str)));
}
......@@ -328,6 +355,9 @@ void WebSnapshotSerializer::SerializePendingItems() {
Handle<Map> map = handle(Map::cast(maps_->Get(i)), isolate_);
SerializeMap(map);
}
for (auto name_id : builtin_objects_) {
SerializeBuiltinObject(name_id);
}
// Serialize the items in the reverse order. The items at the end of the
// contexts_ etc get lower IDs and vice versa. IDs which items use for
......@@ -366,6 +396,9 @@ void WebSnapshotSerializer::SerializePendingItems() {
// - Symbol count
// - For each symbol:
// - Serialized symbol
// - Builtin object count
// - For each builtin object:
// - Id of the builtin object name string
// - Shape count
// - For each shape:
// - Serialized shape
......@@ -391,7 +424,8 @@ 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_ + map_serializer_.buffer_size_ +
symbol_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_ +
object_serializer_.buffer_size_ + export_serializer_.buffer_size_ +
......@@ -404,6 +438,8 @@ 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, builtin_object_count(),
builtin_object_serializer_, "builtin_objects");
WriteObjects(total_serializer, map_count(), map_serializer_, "maps");
WriteObjects(total_serializer, context_count(), context_serializer_,
"contexts");
......@@ -438,9 +474,6 @@ bool WebSnapshotSerializer::InsertIntoIndexMap(ObjectCacheIndexMap& map,
uint32_t& id) {
DisallowGarbageCollection no_gc;
int index_out;
if (external_objects_ids_.Lookup(heap_object, &index_out)) {
return true;
}
bool found = map.LookupOrInsert(heap_object, &index_out);
id = static_cast<uint32_t>(index_out);
return found;
......@@ -565,6 +598,10 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map) {
}
}
void WebSnapshotSerializer::SerializeBuiltinObject(uint32_t name_id) {
builtin_object_serializer_.WriteUint32(name_id);
}
// Construct the minimal source string to be included in the snapshot. Maintain
// the "inner function is textually inside its outer function" relationship.
// Example:
......@@ -662,11 +699,32 @@ void WebSnapshotSerializer::ShallowDiscoverExternals(FixedArray externals) {
Object object = externals.get(i);
if (!object.IsHeapObject()) continue;
uint32_t unused_id = 0;
InsertIntoIndexMap(external_objects_ids_, HeapObject::cast(object),
InsertIntoIndexMap(external_object_ids_, HeapObject::cast(object),
unused_id);
}
}
void WebSnapshotSerializer::ShallowDiscoverBuiltinObjects(
v8::Local<v8::Context> context) {
// Fill in builtin_object_to_name_. Don't discover them or their
// names, so that they won't be included in the snapshot unless needed.
builtin_object_name_strings_ =
isolate_->factory()->NewFixedArray(kBuiltinObjectCount);
int i = 0;
IterateBuiltinObjects([&](String name, HeapObject object) {
builtin_object_name_strings_->set(i, name);
uint32_t id;
bool already_exists =
InsertIntoIndexMap(builtin_object_to_name_, object, id);
CHECK(!already_exists);
CHECK_EQ(static_cast<int>(id), i);
++i;
});
DCHECK_EQ(i, kBuiltinObjectCount);
}
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
......@@ -721,9 +779,9 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
// strings.
DiscoverString(Handle<String>::cast(object), AllowInPlace::Yes);
break;
} else if (external_objects_ids_.size() > 0) {
} else if (external_object_ids_.size() > 0) {
int unused_id;
external_objects_ids_.LookupOrInsert(*object, &unused_id);
external_object_ids_.LookupOrInsert(*object, &unused_id);
} else {
Throw("Unsupported object");
}
......@@ -785,6 +843,10 @@ void WebSnapshotSerializer::DiscoverString(Handle<String> string,
}
void WebSnapshotSerializer::DiscoverFunction(Handle<JSFunction> function) {
if (DiscoverIfBuiltinObject(function)) {
return;
}
uint32_t id;
if (InsertIntoIndexMap(function_ids_, *function, id)) {
return;
......@@ -933,6 +995,13 @@ void WebSnapshotSerializer::DiscoverObjectPropertiesWithDictionaryMap(T dict) {
}
void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
if (GetExternalId(*object)) {
return;
}
if (DiscoverIfBuiltinObject(object)) {
return;
}
uint32_t id;
if (InsertIntoIndexMap(object_ids_, *object, id)) return;
......@@ -986,6 +1055,32 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
}
}
bool WebSnapshotSerializer::DiscoverIfBuiltinObject(Handle<HeapObject> object) {
uint32_t name_index;
if (!GetBuiltinObjectNameIndex(*object, name_index)) {
return false;
}
CHECK_LT(name_index, builtin_object_name_strings_->length());
Handle<String> name_string = handle(
String::cast(builtin_object_name_strings_->get(name_index)), isolate_);
DiscoverString(name_string, AllowInPlace::No);
// Ensure the builtin object reference gets included in the snapshot.
uint32_t id;
if (InsertIntoIndexMap(builtin_object_ids_, *object, id)) {
// The builtin object is already referred to by something else.
return true;
}
DCHECK_EQ(id, builtin_objects_.size());
bool in_place = false;
uint32_t name_id = GetStringId(name_string, in_place);
DCHECK(!in_place);
USE(in_place);
builtin_objects_.push_back(name_id);
return true;
}
void WebSnapshotSerializer::DiscoverSymbol(Handle<Symbol> symbol) {
if (symbol->is_well_known_symbol()) {
// TODO(v8:11525): Support well-known Symbols.
......@@ -1257,10 +1352,16 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
return;
}
int external_id;
if (external_objects_ids_.Lookup(HeapObject::cast(*object), &external_id)) {
uint32_t id;
if (GetExternalId(HeapObject::cast(*object), &id)) {
serializer.WriteUint32(ValueType::EXTERNAL_ID);
serializer.WriteUint32(static_cast<uint32_t>(external_id));
serializer.WriteUint32(id);
return;
}
if (GetBuiltinObjectId(HeapObject::cast(*object), id)) {
serializer.WriteUint32(ValueType::BUILTIN_OBJECT_ID);
serializer.WriteUint32(id);
return;
}
......@@ -1431,16 +1532,33 @@ uint32_t WebSnapshotSerializer::GetObjectId(JSObject object) {
return static_cast<uint32_t>(object_ids_.size() - 1 - 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);
bool WebSnapshotSerializer::GetExternalId(HeapObject object, uint32_t* id) {
int id_int;
bool return_value = external_object_ids_.Lookup(object, &id_int);
if (id != nullptr) {
*id = static_cast<uint32_t>(id_int);
}
return return_value;
}
bool WebSnapshotSerializer::GetBuiltinObjectNameIndex(HeapObject object,
uint32_t& index) {
int index_int = 0;
bool return_value = builtin_object_to_name_.Lookup(object, &index_int);
index = static_cast<uint32_t>(index_int);
return return_value;
}
bool WebSnapshotSerializer::GetBuiltinObjectId(HeapObject object,
uint32_t& id) {
int id_int;
bool return_value = builtin_object_ids_.Lookup(object, &id_int);
id = static_cast<uint32_t>(id_int);
return return_value;
}
Handle<FixedArray> WebSnapshotSerializer::GetExternals() {
return external_objects_ids_.Values(isolate_);
return external_object_ids_.Values(isolate_);
}
WebSnapshotDeserializer::WebSnapshotDeserializer(v8::Isolate* isolate,
......@@ -1465,6 +1583,7 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
Handle<FixedArray> empty_array = factory()->empty_fixed_array();
strings_handle_ = empty_array;
symbols_handle_ = empty_array;
builtin_objects_handle_ = empty_array;
maps_handle_ = empty_array;
contexts_handle_ = empty_array;
functions_handle_ = empty_array;
......@@ -1483,6 +1602,7 @@ WebSnapshotDeserializer::~WebSnapshotDeserializer() {
void WebSnapshotDeserializer::UpdatePointers() {
strings_ = *strings_handle_;
symbols_ = *symbols_handle_;
builtin_objects_ = *builtin_objects_handle_;
maps_ = *maps_handle_;
contexts_ = *contexts_handle_;
functions_ = *functions_handle_;
......@@ -1549,6 +1669,7 @@ void WebSnapshotDeserializer::Throw(const char* message) {
string_count_ = 0;
symbol_count_ = 0;
map_count_ = 0;
builtin_object_count_ = 0;
context_count_ = 0;
class_count_ = 0;
function_count_ = 0;
......@@ -1597,6 +1718,8 @@ bool WebSnapshotDeserializer::Deserialize(
}
bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
CollectBuiltinObjects();
deferred_references_ = ArrayList::New(isolate_, 30);
const void* magic_bytes;
......@@ -1608,6 +1731,7 @@ bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
DeserializeStrings();
DeserializeSymbols();
DeserializeBuiltinObjects();
DeserializeMaps();
DeserializeContexts();
DeserializeFunctions();
......@@ -1621,6 +1745,28 @@ bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
return !has_error();
}
void WebSnapshotDeserializer::CollectBuiltinObjects() {
// TODO(v8:11525): Look up the builtin objects from the global object.
builtin_object_name_to_object_ =
ObjectHashTable::New(isolate_, kBuiltinObjectCount);
#if DEBUG
int i = 0;
#endif
IterateBuiltinObjects([&](String name, HeapObject object) {
auto new_builtin_object_name_to_object =
ObjectHashTable::Put(builtin_object_name_to_object_,
handle(name, isolate_), handle(object, isolate_));
USE(new_builtin_object_name_to_object);
// We preallocated the correct size, so the hash table doesn't grow.
DCHECK_EQ(*new_builtin_object_name_to_object,
*builtin_object_name_to_object_);
#if DEBUG
++i;
#endif
});
DCHECK_EQ(i, kBuiltinObjectCount);
}
bool WebSnapshotDeserializer::DeserializeScript() {
// If there is more data, treat it as normal JavaScript.
DCHECK_LE(deserializer_.position_, deserializer_.end_);
......@@ -1840,6 +1986,24 @@ void WebSnapshotDeserializer::DeserializeMaps() {
}
}
void WebSnapshotDeserializer::DeserializeBuiltinObjects() {
RCS_SCOPE(isolate_,
RuntimeCallCounterId::kWebSnapshotDeserialize_BuiltinObjects);
if (!deserializer_.ReadUint32(&builtin_object_count_) ||
builtin_object_count_ > kMaxItemCount) {
Throw("Malformed builtin object table");
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
builtin_objects_handle_ = factory()->NewFixedArray(builtin_object_count_);
builtin_objects_ = *builtin_objects_handle_;
for (uint32_t i = 0; i < builtin_object_count_; ++i) {
Handle<String> name = handle(ReadString(), isolate_);
builtin_objects_.set(static_cast<int>(i),
builtin_object_name_to_object_->Lookup(name));
}
}
void WebSnapshotDeserializer::DeserializeContexts() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Contexts);
if (!deserializer_.ReadUint32(&context_count_) ||
......@@ -2558,6 +2722,8 @@ Object WebSnapshotDeserializer::ReadValue(
return ReadSymbol();
case ValueType::EXTERNAL_ID:
return ReadExternalReference();
case ValueType::BUILTIN_OBJECT_ID:
return ReadBuiltinObjectReference();
case ValueType::IN_PLACE_STRING_ID:
return ReadInPlaceString(internalize_strings);
default:
......@@ -2671,6 +2837,16 @@ Object WebSnapshotDeserializer::ReadExternalReference() {
return external_references_.get(ref_id);
}
Object WebSnapshotDeserializer::ReadBuiltinObjectReference() {
uint32_t ref_id;
if (!deserializer_.ReadUint32(&ref_id) ||
ref_id >= static_cast<uint32_t>(builtin_objects_.length())) {
Throw("Invalid builtin object reference");
return Smi::zero();
}
return builtin_objects_.get(ref_id);
}
void WebSnapshotDeserializer::ReadFunctionPrototype(
Handle<JSFunction> function) {
uint32_t object_id;
......
......@@ -57,6 +57,7 @@ class WebSnapshotSerializerDeserializer {
CLASS_ID,
SYMBOL_ID,
EXTERNAL_ID,
BUILTIN_OBJECT_ID,
IN_PLACE_STRING_ID
};
......@@ -94,6 +95,10 @@ class WebSnapshotSerializerDeserializer {
// Not virtual, on purpose (because it doesn't need to be).
void Throw(const char* message);
void IterateBuiltinObjects(std::function<void(String, HeapObject)> func);
static constexpr int kBuiltinObjectCount = 2;
inline Factory* factory() const { return isolate_->factory(); }
Isolate* isolate_;
......@@ -146,6 +151,10 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t map_count() const { return static_cast<uint32_t>(map_ids_.size()); }
uint32_t builtin_object_count() const {
return static_cast<uint32_t>(builtin_object_ids_.size());
}
uint32_t context_count() const {
return static_cast<uint32_t>(context_ids_.size());
}
......@@ -166,8 +175,8 @@ class V8_EXPORT WebSnapshotSerializer
return static_cast<uint32_t>(object_ids_.size());
}
uint32_t external_objects_count() const {
return static_cast<uint32_t>(external_objects_ids_.size());
uint32_t external_object_count() const {
return static_cast<uint32_t>(external_object_ids_.size());
}
Handle<FixedArray> GetExternals();
......@@ -191,6 +200,7 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t& id);
void ShallowDiscoverExternals(FixedArray externals);
void ShallowDiscoverBuiltinObjects(v8::Local<v8::Context> context);
void Discover(Handle<HeapObject> object);
void DiscoverString(Handle<String> string,
AllowInPlace can_be_in_place = AllowInPlace::No);
......@@ -202,6 +212,7 @@ class V8_EXPORT WebSnapshotSerializer
void DiscoverContext(Handle<Context> context);
void DiscoverArray(Handle<JSArray> array);
void DiscoverObject(Handle<JSObject> object);
bool DiscoverIfBuiltinObject(Handle<HeapObject> object);
void DiscoverSource(Handle<JSFunction> function);
template <typename T>
void DiscoverObjectPropertiesWithDictionaryMap(T dict);
......@@ -213,6 +224,7 @@ class V8_EXPORT WebSnapshotSerializer
void SerializeString(Handle<String> string, ValueSerializer& serializer);
void SerializeSymbol(Handle<Symbol> symbol);
void SerializeMap(Handle<Map> map);
void SerializeBuiltinObject(uint32_t name_id);
void SerializeObjectPrototype(Handle<Map> map, ValueSerializer& serializer);
template <typename T>
......@@ -237,11 +249,15 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t GetContextId(Context context);
uint32_t GetArrayId(JSArray array);
uint32_t GetObjectId(JSObject object);
uint32_t GetExternalId(HeapObject object);
bool GetExternalId(HeapObject object, uint32_t* id = nullptr);
// Returns index into builtin_object_name_strings_.
bool GetBuiltinObjectNameIndex(HeapObject object, uint32_t& index);
bool GetBuiltinObjectId(HeapObject object, uint32_t& id);
ValueSerializer string_serializer_;
ValueSerializer symbol_serializer_;
ValueSerializer map_serializer_;
ValueSerializer builtin_object_serializer_;
ValueSerializer context_serializer_;
ValueSerializer function_serializer_;
ValueSerializer class_serializer_;
......@@ -261,7 +277,7 @@ class V8_EXPORT WebSnapshotSerializer
// IndexMap to keep track of explicitly blocked external objects and
// non-serializable/not-supported objects (e.g. API Objects).
ObjectCacheIndexMap external_objects_ids_;
ObjectCacheIndexMap external_object_ids_;
// ObjectCacheIndexMap implements fast lookup item -> id. Some items (context,
// function, class, array, object) can point to other items and we serialize
......@@ -277,6 +293,24 @@ class V8_EXPORT WebSnapshotSerializer
ObjectCacheIndexMap object_ids_;
uint32_t export_count_ = 0;
// For handling references to builtin objects:
// --------------------------------
// String objects for the names of all known builtins.
Handle<FixedArray> builtin_object_name_strings_;
// Map object -> index in builtin_name_strings_ for all known builtins.
ObjectCacheIndexMap builtin_object_to_name_;
// Map object -> index in builtins_. Includes only builtins which will be
// incluced in the snapshot.
ObjectCacheIndexMap builtin_object_ids_;
// For creating the Builtin wrappers in the snapshot. Includes only builtins
// which will be incluced in the snapshot. Each element is the id of the
// builtin name string in the snapshot.
std::vector<uint32_t> builtin_objects_;
// --------------------------------
std::queue<Handle<HeapObject>> discovery_queue_;
// For keeping track of which strings have exactly one reference. Strings are
......@@ -310,6 +344,7 @@ class V8_EXPORT WebSnapshotDeserializer
uint32_t string_count() const { return string_count_; }
uint32_t symbol_count() const { return symbol_count_; }
uint32_t map_count() const { return map_count_; }
uint32_t builtin_object_count() const { return builtin_object_count_; }
uint32_t context_count() const { return context_count_; }
uint32_t function_count() const { return function_count_; }
uint32_t class_count() const { return class_count_; }
......@@ -337,6 +372,7 @@ class V8_EXPORT WebSnapshotDeserializer
base::Vector<const uint8_t> ExtractScriptBuffer(
Isolate* isolate, Handle<Script> snapshot_as_script);
bool DeserializeSnapshot(bool skip_exports);
void CollectBuiltinObjects();
bool DeserializeScript();
WebSnapshotDeserializer(const WebSnapshotDeserializer&) = delete;
......@@ -345,6 +381,7 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeStrings();
void DeserializeSymbols();
void DeserializeMaps();
void DeserializeBuiltinObjects();
void DeserializeContexts();
Handle<ScopeInfo> CreateScopeInfo(uint32_t variable_count, bool has_parent,
ContextType context_type,
......@@ -381,6 +418,7 @@ class V8_EXPORT WebSnapshotDeserializer
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index);
Object ReadClass(Handle<HeapObject> container, uint32_t container_index);
Object ReadRegexp();
Object ReadBuiltinObjectReference();
Object ReadExternalReference();
bool ReadMapType();
......@@ -400,6 +438,9 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> symbols_handle_;
FixedArray symbols_;
Handle<FixedArray> builtin_objects_handle_;
FixedArray builtin_objects_;
Handle<FixedArray> maps_handle_;
FixedArray maps_;
......@@ -421,6 +462,9 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> external_references_handle_;
FixedArray external_references_;
// Map: String -> builtin object.
Handle<ObjectHashTable> builtin_object_name_to_object_;
Handle<ArrayList> deferred_references_;
Handle<WeakFixedArray> shared_function_infos_handle_;
......@@ -436,6 +480,7 @@ class V8_EXPORT WebSnapshotDeserializer
uint32_t string_count_ = 0;
uint32_t symbol_count_ = 0;
uint32_t map_count_ = 0;
uint32_t builtin_object_count_ = 0;
uint32_t context_count_ = 0;
uint32_t function_count_ = 0;
uint32_t current_function_count_ = 0;
......
......@@ -16,9 +16,9 @@ namespace {
void TestWebSnapshotExtensive(
const char* snapshot_source, const char* test_source,
std::function<void(v8::Isolate*, v8::Local<v8::Context>)> tester,
uint32_t string_count, uint32_t symbol_count, uint32_t map_count,
uint32_t context_count, uint32_t function_count, uint32_t object_count,
uint32_t array_count) {
uint32_t string_count, uint32_t symbol_count, uint32_t builtin_object_count,
uint32_t map_count, uint32_t context_count, uint32_t function_count,
uint32_t object_count, uint32_t array_count) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......@@ -40,6 +40,7 @@ void TestWebSnapshotExtensive(
CHECK_EQ(string_count, serializer.string_count());
CHECK_EQ(symbol_count, serializer.symbol_count());
CHECK_EQ(map_count, serializer.map_count());
CHECK_EQ(builtin_object_count, serializer.builtin_object_count());
CHECK_EQ(context_count, serializer.context_count());
CHECK_EQ(function_count, serializer.function_count());
CHECK_EQ(object_count, serializer.object_count());
......@@ -58,6 +59,7 @@ void TestWebSnapshotExtensive(
CHECK_EQ(string_count, deserializer.string_count());
CHECK_EQ(symbol_count, deserializer.symbol_count());
CHECK_EQ(map_count, deserializer.map_count());
CHECK_EQ(builtin_object_count, deserializer.builtin_object_count());
CHECK_EQ(context_count, deserializer.context_count());
CHECK_EQ(function_count, deserializer.function_count());
CHECK_EQ(object_count, deserializer.object_count());
......@@ -68,8 +70,9 @@ void TestWebSnapshotExtensive(
void TestWebSnapshot(const char* snapshot_source, const char* test_source,
const char* expected_result, uint32_t string_count,
uint32_t symbol_count, uint32_t map_count,
uint32_t context_count, uint32_t function_count,
uint32_t object_count, uint32_t array_count) {
uint32_t builtin_object_count, uint32_t context_count,
uint32_t function_count, uint32_t object_count,
uint32_t array_count) {
TestWebSnapshotExtensive(
snapshot_source, test_source,
[test_source, expected_result](v8::Isolate* isolate,
......@@ -77,8 +80,8 @@ void TestWebSnapshot(const char* snapshot_source, const char* test_source,
v8::Local<v8::String> result = CompileRun(test_source).As<v8::String>();
CHECK(result->Equals(new_context, v8_str(expected_result)).FromJust());
},
string_count, symbol_count, map_count, context_count, function_count,
object_count, array_count);
string_count, symbol_count, map_count, builtin_object_count,
context_count, function_count, object_count, array_count);
}
} // namespace
......@@ -89,14 +92,15 @@ TEST(Minimal) {
const char* expected_result = "lol";
uint32_t kStringCount = 1; // 'foo'; 'key' is in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(EmptyObject) {
......@@ -104,6 +108,7 @@ TEST(EmptyObject) {
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -118,8 +123,9 @@ TEST(EmptyObject) {
i_isolate->native_context()->object_function().initial_map());
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
TEST(Numbers) {
......@@ -134,6 +140,7 @@ TEST(Numbers) {
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'f' are in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -175,8 +182,9 @@ TEST(Numbers) {
CHECK_EQ(f, -std::numeric_limits<double>::infinity());
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
TEST(Oddballs) {
......@@ -189,6 +197,7 @@ TEST(Oddballs) {
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'d' are in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -207,8 +216,9 @@ TEST(Oddballs) {
CHECK(d->IsUndefined());
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
TEST(Function) {
......@@ -218,14 +228,15 @@ TEST(Function) {
const char* expected_result = "11525";
uint32_t kStringCount = 2; // 'foo', function source code. 'key' is in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 1;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(InnerFunctionWithContext) {
......@@ -240,14 +251,15 @@ TEST(InnerFunctionWithContext) {
// Strings: 'foo', 'result', function source code (inner). 'key' is in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 1;
uint32_t kFunctionCount = 1;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(InnerFunctionWithContextAndParentContext) {
......@@ -268,14 +280,15 @@ TEST(InnerFunctionWithContextAndParentContext) {
// Strings: 'foo', function source code (innerinner), 'part1', 'part2'.
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 2;
uint32_t kFunctionCount = 1;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(RegExp) {
......@@ -283,6 +296,7 @@ TEST(RegExp) {
const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -305,8 +319,9 @@ TEST(RegExp) {
CHECK(no_match->IsNull());
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
TEST(RegExpNoFlags) {
......@@ -314,6 +329,7 @@ TEST(RegExpNoFlags) {
const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -336,8 +352,9 @@ TEST(RegExpNoFlags) {
CHECK(no_match->IsNull());
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
TEST(SFIDeduplication) {
......@@ -704,6 +721,7 @@ TEST(FunctionKinds) {
const char* test_source = "foo";
uint32_t kStringCount = 2; // 'foo', source code. 'a'...'f' in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 6;
......@@ -727,8 +745,9 @@ TEST(FunctionKinds) {
FunctionKind::kAsyncGeneratorFunction);
};
TestWebSnapshotExtensive(snapshot_source, test_source, tester, kStringCount,
kSymbolCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount,
kContextCount, kFunctionCount, kObjectCount,
kArrayCount);
}
// Test that concatenating JS code to the snapshot works.
......@@ -880,14 +899,15 @@ TEST(InPlaceStringsInArrays) {
const char* expected_result = "onetwothree";
uint32_t kStringCount = 1; // 'foo'; Other strings are in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 0;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 0;
uint32_t kArrayCount = 1;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(RepeatedInPlaceStringsInArrays) {
......@@ -896,14 +916,15 @@ TEST(RepeatedInPlaceStringsInArrays) {
const char* expected_result = "onetwoone";
uint32_t kStringCount = 2; // 'foo', 'one'; Other strings are in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 0;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 0;
uint32_t kArrayCount = 1;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(InPlaceStringsInObjects) {
......@@ -913,14 +934,15 @@ TEST(InPlaceStringsInObjects) {
// 'foo'. Other strings are in-place.
uint32_t kStringCount = 1;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(RepeatedInPlaceStringsInObjects) {
......@@ -930,14 +952,52 @@ TEST(RepeatedInPlaceStringsInObjects) {
// 'foo', 'one'. Other strings are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kMapCount, kContextCount, kFunctionCount,
kObjectCount, kArrayCount);
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(builtin_objects) {
const char* snapshot_source = "var foo = {a: Error.prototype};";
const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(builtin_objectsDeduplicated) {
const char* snapshot_source =
"var foo = {a: Error.prototype, b: Error.prototype}";
const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
uint32_t kObjectCount = 1;
uint32_t kArrayCount = 0;
TestWebSnapshot(snapshot_source, test_source, expected_result, kStringCount,
kSymbolCount, kBuiltinObjectCount, kMapCount, kContextCount,
kFunctionCount, kObjectCount, kArrayCount);
}
} // namespace internal
......
// 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 TestBuiltin() {
function createObjects() {
globalThis.obj1 = {'a': Error};
globalThis.obj2 = {'b': Error.prototype};
}
const realm = Realm.create();
const {obj1, obj2} = takeAndUseWebSnapshot(
createObjects, ['obj1', 'obj2'], realm);
assertTrue(obj1.a === Realm.eval(realm, "Error"));
assertTrue(obj2.b === Realm.eval(realm, "Error.prototype"));
})();
......@@ -8,13 +8,14 @@ function use(exports) {
return result;
}
function takeAndUseWebSnapshot(createObjects, exports) {
function takeAndUseWebSnapshot(createObjects, exports, realmForDeserializing) {
// Take a snapshot in Realm r1.
const r1 = Realm.create();
Realm.eval(r1, createObjects, { type: 'function' });
const snapshot = Realm.takeWebSnapshot(r1, exports);
// Use the snapshot in Realm r2.
const r2 = Realm.create();
const r2 = realmForDeserializing != undefined ?
realmForDeserializing : Realm.create();
const success = Realm.useWebSnapshot(r2, snapshot);
assertTrue(success);
const result =
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment