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

[web snap] Support inheriting from builtins

Side product: enable null as __proto__.

Bug: v8:11525,v8:12820
Change-Id: I2b9508d0f3563d9000ddede24e7684aab18c2b5e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3637791Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80474}
parent 6b4a541c
...@@ -55,7 +55,11 @@ void WebSnapshotSerializerDeserializer::IterateBuiltinObjects( ...@@ -55,7 +55,11 @@ void WebSnapshotSerializerDeserializer::IterateBuiltinObjects(
func(roots.Error_string(), isolate_->context().error_function()); func(roots.Error_string(), isolate_->context().error_function());
func(*factory()->NewStringFromAsciiChecked("Error.prototype"), func(*factory()->NewStringFromAsciiChecked("Error.prototype"),
isolate_->context().error_function().instance_prototype()); isolate_->context().error_function().instance_prototype());
STATIC_ASSERT(kBuiltinObjectCount == 2); func(roots.Object_string(), isolate_->context().object_function());
func(*factory()->NewStringFromAsciiChecked("Object.prototype"),
isolate_->context().initial_object_prototype());
STATIC_ASSERT(kBuiltinObjectCount == 4);
} }
uint32_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags( uint32_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags(
...@@ -429,7 +433,7 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer, ...@@ -429,7 +433,7 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
context_serializer_.buffer_size_ + function_serializer_.buffer_size_ + context_serializer_.buffer_size_ + function_serializer_.buffer_size_ +
class_serializer_.buffer_size_ + array_serializer_.buffer_size_ + class_serializer_.buffer_size_ + array_serializer_.buffer_size_ +
object_serializer_.buffer_size_ + export_serializer_.buffer_size_ + object_serializer_.buffer_size_ + export_serializer_.buffer_size_ +
8 * sizeof(uint32_t); 10 * sizeof(uint32_t);
if (total_serializer.ExpandBuffer(needed_size).IsNothing()) { if (total_serializer.ExpandBuffer(needed_size).IsNothing()) {
Throw("Out of memory"); Throw("Out of memory");
return; return;
...@@ -520,31 +524,14 @@ void WebSnapshotSerializer::SerializeSymbol(Handle<Symbol> symbol) { ...@@ -520,31 +524,14 @@ void WebSnapshotSerializer::SerializeSymbol(Handle<Symbol> symbol) {
symbol_serializer_); symbol_serializer_);
} }
} }
void WebSnapshotSerializer::SerializeObjectPrototype(
Handle<Map> map, ValueSerializer& serializer) {
if (map->prototype() ==
isolate_->native_context()->initial_object_prototype()) {
serializer.WriteUint32(0);
} else {
// TODO(v8:11525): Support non-JSObject prototypes, at least null. Recognize
// well-known objects to that we don't end up encoding them in the snapshot.
if (!map->prototype().IsJSObject()) {
Throw("Non-JSObject __proto__s not supported");
return;
}
uint32_t prototype_id = GetObjectId(JSObject::cast(map->prototype()));
serializer.WriteUint32(prototype_id + 1);
}
}
// Format (serialized shape): // Format (serialized shape):
// - PropertyAttributesType // - PropertyAttributesType
// - 0 if the __proto__ is Object.prototype, 1 + object id for the __proto__
// otherwise
// - Property count // - Property count
// - For each property // - For each property
// - Name: STRING_ID + String id or SYMBOL_ID + Symbol id or in-place string // - Name: STRING_ID + String id or SYMBOL_ID + Symbol id or in-place string
// - If the PropertyAttributesType is CUSTOM: attributes // - If the PropertyAttributesType is CUSTOM: attributes
// - __proto__: Serialized value
void WebSnapshotSerializer::SerializeMap(Handle<Map> map) { void WebSnapshotSerializer::SerializeMap(Handle<Map> map) {
DCHECK(!map->is_dictionary_map()); DCHECK(!map->is_dictionary_map());
int first_custom_index = -1; int first_custom_index = -1;
...@@ -574,7 +561,7 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map) { ...@@ -574,7 +561,7 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map) {
map_serializer_.WriteUint32(first_custom_index == -1 map_serializer_.WriteUint32(first_custom_index == -1
? PropertyAttributesType::DEFAULT ? PropertyAttributesType::DEFAULT
: PropertyAttributesType::CUSTOM); : PropertyAttributesType::CUSTOM);
SerializeObjectPrototype(map, map_serializer_);
map_serializer_.WriteUint32(static_cast<uint32_t>(keys.size())); map_serializer_.WriteUint32(static_cast<uint32_t>(keys.size()));
uint32_t default_flags = GetDefaultAttributeFlags(); uint32_t default_flags = GetDefaultAttributeFlags();
...@@ -596,6 +583,8 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map) { ...@@ -596,6 +583,8 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map) {
} }
} }
} }
WriteValue(handle(map->prototype(), isolate_), map_serializer_);
} }
void WebSnapshotSerializer::SerializeBuiltinObject(uint32_t name_id) { void WebSnapshotSerializer::SerializeBuiltinObject(uint32_t name_id) {
...@@ -743,6 +732,7 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) { ...@@ -743,6 +732,7 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
DiscoverClass(Handle<JSFunction>::cast(object)); DiscoverClass(Handle<JSFunction>::cast(object));
break; break;
case JS_OBJECT_TYPE: case JS_OBJECT_TYPE:
case JS_OBJECT_PROTOTYPE_TYPE:
DiscoverObject(Handle<JSObject>::cast(object)); DiscoverObject(Handle<JSObject>::cast(object));
break; break;
case JS_ARRAY_TYPE: case JS_ARRAY_TYPE:
...@@ -1039,10 +1029,7 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) { ...@@ -1039,10 +1029,7 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
DiscoverMap(map); DiscoverMap(map);
// Discover __proto__. // Discover __proto__.
if (map->prototype() !=
isolate_->native_context()->initial_object_prototype()) {
discovery_queue_.push(handle(map->prototype(), isolate_)); discovery_queue_.push(handle(map->prototype(), isolate_));
}
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
// Discover property values. // Discover property values.
...@@ -1079,6 +1066,9 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) { ...@@ -1079,6 +1066,9 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
} }
bool WebSnapshotSerializer::DiscoverIfBuiltinObject(Handle<HeapObject> object) { bool WebSnapshotSerializer::DiscoverIfBuiltinObject(Handle<HeapObject> object) {
// TODO(v8:11525): Consider speccing a set of fixed builtins (such as
// Object.prototype) for objects which are almost always included in the
// snapshot.
uint32_t name_index; uint32_t name_index;
if (!GetBuiltinObjectNameIndex(*object, name_index)) { if (!GetBuiltinObjectNameIndex(*object, name_index)) {
return false; return false;
...@@ -1244,15 +1234,13 @@ void WebSnapshotSerializer::SerializeObjectPropertiesWithDictionaryMap(T dict) { ...@@ -1244,15 +1234,13 @@ void WebSnapshotSerializer::SerializeObjectPropertiesWithDictionaryMap(T dict) {
// - For each property: // - For each property:
// - Serialized value // - Serialized value
// Else (dictionary map) // Else (dictionary map)
// - 0 if the __proto__ is Object.prototype, 1 + object id for the __proto__
// otherwise
// - PropertyAttributesType // - PropertyAttributesType
// - Property count // - Property count
// - For each property // - For each property
// - Name: STRING_ID + String id or SYMBOL_ID + Symbol id or in-place string // - Name: STRING_ID + String id or SYMBOL_ID + Symbol id or in-place string
// - Serialized value // - Serialized value
// - If the PropertyAttributesType is CUSTOM: attributes // - If the PropertyAttributesType is CUSTOM: attributes
// - __proto__: serialized value
// - Max element index + 1 (or 0 if there are no elements) // - Max element index + 1 (or 0 if there are no elements)
// - For each element: // - For each element:
// - Index // - Index
...@@ -1278,7 +1266,6 @@ void WebSnapshotSerializer::SerializeObject(Handle<JSObject> object) { ...@@ -1278,7 +1266,6 @@ void WebSnapshotSerializer::SerializeObject(Handle<JSObject> object) {
WriteValue(value, object_serializer_); WriteValue(value, object_serializer_);
} }
} else { } else {
SerializeObjectPrototype(map, object_serializer_);
if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Handle<SwissNameDictionary> swiss_dictionary = Handle<SwissNameDictionary> swiss_dictionary =
handle(object->property_dictionary_swiss(), isolate_); handle(object->property_dictionary_swiss(), isolate_);
...@@ -1288,6 +1275,7 @@ void WebSnapshotSerializer::SerializeObject(Handle<JSObject> object) { ...@@ -1288,6 +1275,7 @@ void WebSnapshotSerializer::SerializeObject(Handle<JSObject> object) {
handle(object->property_dictionary(), isolate_); handle(object->property_dictionary(), isolate_);
SerializeObjectPropertiesWithDictionaryMap(dictionary); SerializeObjectPropertiesWithDictionaryMap(dictionary);
} }
WriteValue(handle(map->prototype(), isolate_), object_serializer_);
} }
// Elements. // Elements.
...@@ -1964,13 +1952,6 @@ void WebSnapshotDeserializer::DeserializeMaps() { ...@@ -1964,13 +1952,6 @@ void WebSnapshotDeserializer::DeserializeMaps() {
for (uint32_t i = 0; i < map_count_; ++i) { for (uint32_t i = 0; i < map_count_; ++i) {
bool has_custom_property_attributes = ReadMapType(); bool has_custom_property_attributes = ReadMapType();
uint32_t prototype_id;
if (!deserializer_.ReadUint32(&prototype_id) ||
prototype_id > kMaxItemCount) {
Throw("Malformed shape");
return;
}
uint32_t property_count; uint32_t property_count;
if (!deserializer_.ReadUint32(&property_count)) { if (!deserializer_.ReadUint32(&property_count)) {
Throw("Malformed shape"); Throw("Malformed shape");
...@@ -1986,17 +1967,8 @@ void WebSnapshotDeserializer::DeserializeMaps() { ...@@ -1986,17 +1967,8 @@ void WebSnapshotDeserializer::DeserializeMaps() {
} }
if (property_count == 0) { if (property_count == 0) {
if (prototype_id == 0) { Handle<Map> map = DeserializeObjectPrototypeAndCreateEmptyMap();
DisallowGarbageCollection no_gc;
Map empty_map =
isolate_->native_context()->object_function().initial_map();
maps_.set(i, empty_map);
} else {
Handle<Map> map = factory()->NewMap(
JS_OBJECT_TYPE, JSObject::kHeaderSize, HOLEY_ELEMENTS, 0);
DeserializeObjectPrototype(map, prototype_id);
maps_.set(i, *map); maps_.set(i, *map);
}
continue; continue;
} }
...@@ -2005,10 +1977,10 @@ void WebSnapshotDeserializer::DeserializeMaps() { ...@@ -2005,10 +1977,10 @@ void WebSnapshotDeserializer::DeserializeMaps() {
for (InternalIndex i : InternalIndex::Range(property_count)) { for (InternalIndex i : InternalIndex::Range(property_count)) {
// No deferred references here, since strings and symbols have already // No deferred references here, since strings and symbols have already
// been deserialized. // been deserialized.
Handle<Object> key = Object key = std::get<0>(
handle(ReadValue(Handle<HeapObject>(), 0, InternalizeStrings::kYes), ReadValue(Handle<HeapObject>(), 0, InternalizeStrings::kYes));
isolate_); DisallowGarbageCollection no_gc;
if (!key->IsName()) { if (!key.IsName()) {
Throw("Invalid map key"); Throw("Invalid map key");
return; return;
} }
...@@ -2024,8 +1996,8 @@ void WebSnapshotDeserializer::DeserializeMaps() { ...@@ -2024,8 +1996,8 @@ void WebSnapshotDeserializer::DeserializeMaps() {
// Use the "none" representation until we see the first object having this // Use the "none" representation until we see the first object having this
// map. At that point, modify the representation. // map. At that point, modify the representation.
Descriptor desc = Descriptor desc =
Descriptor::DataField(isolate_, Handle<Name>::cast(key), i.as_int(), Descriptor::DataField(isolate_, handle(Name::cast(key), isolate_),
attributes, Representation::None()); i.as_int(), attributes, Representation::None());
descriptors->Set(i, &desc); descriptors->Set(i, &desc);
} }
DCHECK_EQ(descriptors->number_of_descriptors(), property_count); DCHECK_EQ(descriptors->number_of_descriptors(), property_count);
...@@ -2035,7 +2007,7 @@ void WebSnapshotDeserializer::DeserializeMaps() { ...@@ -2035,7 +2007,7 @@ void WebSnapshotDeserializer::DeserializeMaps() {
HOLEY_ELEMENTS, 0); HOLEY_ELEMENTS, 0);
map->InitializeDescriptors(isolate_, *descriptors); map->InitializeDescriptors(isolate_, *descriptors);
// TODO(v8:11525): Set 'constructor'. // TODO(v8:11525): Set 'constructor'.
DeserializeObjectPrototype(map, prototype_id); DeserializeObjectPrototype(map);
maps_.set(i, *map); maps_.set(i, *map);
} }
} }
...@@ -2161,7 +2133,7 @@ void WebSnapshotDeserializer::DeserializeContexts() { ...@@ -2161,7 +2133,7 @@ void WebSnapshotDeserializer::DeserializeContexts() {
for (int variable_index = 0; for (int variable_index = 0;
variable_index < static_cast<int>(variable_count); ++variable_index) { variable_index < static_cast<int>(variable_count); ++variable_index) {
int context_index = scope_info->ContextHeaderLength() + variable_index; int context_index = scope_info->ContextHeaderLength() + variable_index;
Object value = ReadValue(context, context_index); Object value = std::get<0>(ReadValue(context, context_index));
context->set(context_index, value); context->set(context_index, value);
} }
contexts_.set(i, *context); contexts_.set(i, *context);
...@@ -2421,25 +2393,47 @@ void WebSnapshotDeserializer::DeserializeClasses() { ...@@ -2421,25 +2393,47 @@ void WebSnapshotDeserializer::DeserializeClasses() {
} }
} }
void WebSnapshotDeserializer::DeserializeObjectPrototype( void WebSnapshotDeserializer::DeserializeObjectPrototype(Handle<Map> map) {
Handle<Map> map, uint32_t prototype_id) { auto result = ReadValue(map, 0, InternalizeStrings::kNo);
if (prototype_id == 0) { Object prototype = std::get<0>(result);
// Use Object.prototype as the prototype. bool was_deferred = std::get<1>(result);
Map::SetPrototype( if (!was_deferred) {
isolate_, map, SetPrototype(map, handle(prototype, isolate_));
handle(isolate_->native_context()->initial_object_prototype(),
isolate_));
} else {
// TODO(v8::11525): Implement stricter checks, e.g., disallow cycles.
--prototype_id;
if (prototype_id < current_object_count_) {
HeapObject prototype = HeapObject::cast(objects_.get(prototype_id));
prototype.map().set_is_prototype_map(true);
Map::SetPrototype(isolate_, map, handle(prototype, isolate_));
} else {
// The object hasn't been deserialized yet.
AddDeferredReference(map, 0, OBJECT_ID, prototype_id);
} }
}
Handle<Map>
WebSnapshotDeserializer::DeserializeObjectPrototypeAndCreateEmptyMap() {
Handle<Map> map = factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize,
HOLEY_ELEMENTS, 0);
auto result = ReadValue(map, 0, InternalizeStrings::kNo);
Object prototype = std::get<0>(result);
bool was_deferred = std::get<1>(result);
// If we got a deferred reference, the prototype cannot be a builtin; those
// references aren't deferred.
// TODO(v8:11525): if the object order is relaxed, it's possible to have a
// deferred reference to Object.prototype, and we'll need to recognize and
// handle that case.
if (prototype == isolate_->context().initial_object_prototype()) {
// TODO(v8:11525): Avoid map creation (above) in this case.
return handle(isolate_->native_context()->object_function().initial_map(),
isolate_);
}
if (!was_deferred) {
SetPrototype(map, handle(prototype, isolate_));
}
return map;
}
void WebSnapshotDeserializer::SetPrototype(Handle<Map> map,
Handle<Object> prototype) {
if (prototype->IsJSObject()) {
HeapObject::cast(*prototype).map().set_is_prototype_map(true);
Map::SetPrototype(isolate_, map, Handle<JSObject>::cast(prototype));
} else if (prototype->IsNull(isolate_)) {
map->set_prototype(HeapObject::cast(*prototype));
} else {
Throw("Invalid prototype");
} }
} }
...@@ -2447,13 +2441,14 @@ template <typename T> ...@@ -2447,13 +2441,14 @@ template <typename T>
void WebSnapshotDeserializer::DeserializeObjectPropertiesWithDictionaryMap( void WebSnapshotDeserializer::DeserializeObjectPropertiesWithDictionaryMap(
T dict, uint32_t property_count, bool has_custom_property_attributes) { T dict, uint32_t property_count, bool has_custom_property_attributes) {
for (uint32_t i = 0; i < property_count; i++) { for (uint32_t i = 0; i < property_count; i++) {
Handle<Object> key( Handle<Object> key(std::get<0>(ReadValue(Handle<HeapObject>(), 0,
ReadValue(Handle<HeapObject>(), 0, InternalizeStrings::kYes), isolate_); InternalizeStrings::kYes)),
isolate_);
if (!key->IsName()) { if (!key->IsName()) {
Throw("Invalid map key"); Throw("Invalid map key");
return; return;
} }
Handle<Object> value(ReadValue(), isolate_); Handle<Object> value(std::get<0>(ReadValue()), isolate_);
PropertyAttributes attributes = PropertyAttributes::NONE; PropertyAttributes attributes = PropertyAttributes::NONE;
if (has_custom_property_attributes) { if (has_custom_property_attributes) {
uint32_t flags; uint32_t flags;
...@@ -2521,7 +2516,7 @@ void WebSnapshotDeserializer::DeserializeObjects() { ...@@ -2521,7 +2516,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
Handle<PropertyArray> property_array = Handle<PropertyArray> property_array =
factory()->NewPropertyArray(no_properties); factory()->NewPropertyArray(no_properties);
for (int i = 0; i < no_properties; ++i) { for (int i = 0; i < no_properties; ++i) {
Object value = ReadValue(property_array, i); Object value = std::get<0>(ReadValue(property_array, i));
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
// Read the representation from the map. // Read the representation from the map.
DescriptorArray raw_descriptors = *descriptors; DescriptorArray raw_descriptors = *descriptors;
...@@ -2546,12 +2541,6 @@ void WebSnapshotDeserializer::DeserializeObjects() { ...@@ -2546,12 +2541,6 @@ void WebSnapshotDeserializer::DeserializeObjects() {
HOLEY_ELEMENTS, 0); HOLEY_ELEMENTS, 0);
map->set_may_have_interesting_symbols(true); map->set_may_have_interesting_symbols(true);
map->set_is_dictionary_map(true); map->set_is_dictionary_map(true);
uint32_t prototype_id;
if (!deserializer_.ReadUint32(&prototype_id)) {
Throw("Malformed prototype id");
return;
}
DeserializeObjectPrototype(map, prototype_id);
bool has_custom_property_attributes = ReadMapType(); bool has_custom_property_attributes = ReadMapType();
...@@ -2582,6 +2571,7 @@ void WebSnapshotDeserializer::DeserializeObjects() { ...@@ -2582,6 +2571,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
object = factory()->NewJSObjectFromMap(map); object = factory()->NewJSObjectFromMap(map);
object->SetProperties(*dictionary); object->SetProperties(*dictionary);
} }
DeserializeObjectPrototype(map);
} }
DCHECK(!object->is_null()); DCHECK(!object->is_null());
uint32_t max_element_index = 0; uint32_t max_element_index = 0;
...@@ -2602,7 +2592,7 @@ void WebSnapshotDeserializer::DeserializeObjects() { ...@@ -2602,7 +2592,7 @@ void WebSnapshotDeserializer::DeserializeObjects() {
Throw("Malformed object"); Throw("Malformed object");
return; return;
} }
Object value = ReadValue(elements, index); Object value = std::get<0>(ReadValue(elements, index));
elements->set(index, value); elements->set(index, value);
if (index == max_element_index) { if (index == max_element_index) {
break; break;
...@@ -2637,7 +2627,7 @@ Handle<JSArray> WebSnapshotDeserializer::ReadDenseArrayElements( ...@@ -2637,7 +2627,7 @@ Handle<JSArray> WebSnapshotDeserializer::ReadDenseArrayElements(
ElementsKind elements_kind = PACKED_SMI_ELEMENTS; ElementsKind elements_kind = PACKED_SMI_ELEMENTS;
bool has_hole = false; bool has_hole = false;
for (uint32_t i = 0; i < length; ++i) { for (uint32_t i = 0; i < length; ++i) {
Object value = ReadValue(elements, i); Object value = std::get<0>(ReadValue(elements, i));
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
if (!value.IsSmi()) { if (!value.IsSmi()) {
elements_kind = PACKED_ELEMENTS; elements_kind = PACKED_ELEMENTS;
...@@ -2664,7 +2654,7 @@ Handle<JSArray> WebSnapshotDeserializer::ReadSparseArrayElements( ...@@ -2664,7 +2654,7 @@ Handle<JSArray> WebSnapshotDeserializer::ReadSparseArrayElements(
Throw("Malformed element index in sparse array"); Throw("Malformed element index in sparse array");
return isolate_->factory()->NewJSArray(0); return isolate_->factory()->NewJSArray(0);
} }
Object value = ReadValue(dict, element_index); Object value = std::get<0>(ReadValue(dict, element_index));
Handle<NumberDictionary> new_dict = Handle<NumberDictionary> new_dict =
dict->Set(isolate_, dict, element_index, handle(value, isolate_)); dict->Set(isolate_, dict, element_index, handle(value, isolate_));
// The number dictionary didn't grow, since it was preallocated to be // The number dictionary didn't grow, since it was preallocated to be
...@@ -2736,7 +2726,7 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) { ...@@ -2736,7 +2726,7 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) {
isolate_); isolate_);
// No deferred references should occur at this point, since all objects // No deferred references should occur at this point, since all objects
// have been deserialized. // have been deserialized.
Object export_value = ReadValue(); Object export_value = std::get<0>(ReadValue());
USE(export_name); USE(export_name);
USE(export_value); USE(export_value);
} }
...@@ -2762,7 +2752,7 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) { ...@@ -2762,7 +2752,7 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) {
Handle<String> export_name(ReadString(InternalizeStrings::kYes), isolate_); Handle<String> export_name(ReadString(InternalizeStrings::kYes), isolate_);
// No deferred references should occur at this point, since all objects have // No deferred references should occur at this point, since all objects have
// been deserialized. // been deserialized.
Object export_value = ReadValue(); Object export_value = std::get<0>(ReadValue());
if (export_name->length() == 0 && i == 0) { if (export_name->length() == 0 && i == 0) {
// Hack: treat the first empty-string-named export value as a return value // Hack: treat the first empty-string-named export value as a return value
...@@ -2798,34 +2788,34 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) { ...@@ -2798,34 +2788,34 @@ void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) {
JSObject::InvalidatePrototypeChains(global->map(isolate_)); JSObject::InvalidatePrototypeChains(global->map(isolate_));
} }
Object WebSnapshotDeserializer::ReadValue( std::tuple<Object, bool> WebSnapshotDeserializer::ReadValue(
Handle<HeapObject> container, uint32_t container_index, Handle<HeapObject> container, uint32_t container_index,
InternalizeStrings internalize_strings) { InternalizeStrings internalize_strings) {
uint32_t value_type; uint32_t value_type;
// TODO(v8:11525): Consider adding a ReadByte. // TODO(v8:11525): Consider adding a ReadByte.
if (!deserializer_.ReadUint32(&value_type)) { if (!deserializer_.ReadUint32(&value_type)) {
Throw("Malformed variable"); Throw("Malformed variable");
// Set "value" here so that the "keep on trucking" error handling won't fail // Return a placeholder "value" so that the "keep on trucking" error
// when dereferencing the handle. // handling won't fail.
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
switch (value_type) { switch (value_type) {
case ValueType::FALSE_CONSTANT: case ValueType::FALSE_CONSTANT:
return roots_.false_value(); return std::make_tuple(roots_.false_value(), false);
case ValueType::TRUE_CONSTANT: case ValueType::TRUE_CONSTANT:
return roots_.true_value(); return std::make_tuple(roots_.true_value(), false);
case ValueType::NULL_CONSTANT: case ValueType::NULL_CONSTANT:
return roots_.null_value(); return std::make_tuple(roots_.null_value(), false);
case ValueType::UNDEFINED_CONSTANT: case ValueType::UNDEFINED_CONSTANT:
return roots_.undefined_value(); return std::make_tuple(roots_.undefined_value(), false);
case ValueType::NO_ELEMENT_CONSTANT: case ValueType::NO_ELEMENT_CONSTANT:
return roots_.the_hole_value(); return std::make_tuple(roots_.the_hole_value(), false);
case ValueType::INTEGER: case ValueType::INTEGER:
return ReadInteger(); return std::make_tuple(ReadInteger(), false);
case ValueType::DOUBLE: case ValueType::DOUBLE:
return ReadNumber(); return std::make_tuple(ReadNumber(), false);
case ValueType::STRING_ID: case ValueType::STRING_ID:
return ReadString(internalize_strings); return std::make_tuple(ReadString(internalize_strings), false);
case ValueType::ARRAY_ID: case ValueType::ARRAY_ID:
return ReadArray(container, container_index); return ReadArray(container, container_index);
case ValueType::OBJECT_ID: case ValueType::OBJECT_ID:
...@@ -2835,19 +2825,19 @@ Object WebSnapshotDeserializer::ReadValue( ...@@ -2835,19 +2825,19 @@ Object WebSnapshotDeserializer::ReadValue(
case ValueType::CLASS_ID: case ValueType::CLASS_ID:
return ReadClass(container, container_index); return ReadClass(container, container_index);
case ValueType::REGEXP: case ValueType::REGEXP:
return ReadRegexp(); return std::make_tuple(ReadRegexp(), false);
case ValueType::SYMBOL_ID: case ValueType::SYMBOL_ID:
return ReadSymbol(); return std::make_tuple(ReadSymbol(), false);
case ValueType::EXTERNAL_ID: case ValueType::EXTERNAL_ID:
return ReadExternalReference(); return std::make_tuple(ReadExternalReference(), false);
case ValueType::BUILTIN_OBJECT_ID: case ValueType::BUILTIN_OBJECT_ID:
return ReadBuiltinObjectReference(); return std::make_tuple(ReadBuiltinObjectReference(), false);
case ValueType::IN_PLACE_STRING_ID: case ValueType::IN_PLACE_STRING_ID:
return ReadInPlaceString(internalize_strings); return std::make_tuple(ReadInPlaceString(internalize_strings), false);
default: default:
// TODO(v8:11525): Handle other value types. // TODO(v8:11525): Handle other value types.
Throw("Unsupported value type"); Throw("Unsupported value type");
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
} }
...@@ -2869,61 +2859,65 @@ Object WebSnapshotDeserializer::ReadNumber() { ...@@ -2869,61 +2859,65 @@ Object WebSnapshotDeserializer::ReadNumber() {
return *factory()->NewNumber(number); return *factory()->NewNumber(number);
} }
Object WebSnapshotDeserializer::ReadArray(Handle<HeapObject> container, std::tuple<Object, bool> WebSnapshotDeserializer::ReadArray(
uint32_t index) { Handle<HeapObject> container, uint32_t index) {
uint32_t array_id; uint32_t array_id;
if (!deserializer_.ReadUint32(&array_id) || array_id >= kMaxItemCount) { if (!deserializer_.ReadUint32(&array_id) || array_id >= kMaxItemCount) {
Throw("Malformed variable"); Throw("Malformed variable");
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
if (array_id < current_array_count_) { if (array_id < current_array_count_) {
return arrays_.get(array_id); return std::make_tuple(arrays_.get(array_id), false);
} }
// The array hasn't been deserialized yet. // The array hasn't been deserialized yet.
return AddDeferredReference(container, index, ARRAY_ID, array_id); return std::make_tuple(
AddDeferredReference(container, index, ARRAY_ID, array_id), true);
} }
Object WebSnapshotDeserializer::ReadObject(Handle<HeapObject> container, std::tuple<Object, bool> WebSnapshotDeserializer::ReadObject(
uint32_t index) { Handle<HeapObject> container, uint32_t index) {
uint32_t object_id; uint32_t object_id;
if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount) { if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount) {
Throw("Malformed variable"); Throw("Malformed variable");
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
if (object_id < current_object_count_) { if (object_id < current_object_count_) {
return objects_.get(object_id); return std::make_tuple(objects_.get(object_id), false);
} }
// The object hasn't been deserialized yet. // The object hasn't been deserialized yet.
return AddDeferredReference(container, index, OBJECT_ID, object_id); return std::make_tuple(
AddDeferredReference(container, index, OBJECT_ID, object_id), true);
} }
Object WebSnapshotDeserializer::ReadFunction(Handle<HeapObject> container, std::tuple<Object, bool> WebSnapshotDeserializer::ReadFunction(
uint32_t index) { Handle<HeapObject> container, uint32_t index) {
uint32_t function_id; uint32_t function_id;
if (!deserializer_.ReadUint32(&function_id) || if (!deserializer_.ReadUint32(&function_id) ||
function_id >= function_count_) { function_id >= function_count_) {
Throw("Malformed object property"); Throw("Malformed object property");
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
if (function_id < current_function_count_) { if (function_id < current_function_count_) {
return functions_.get(function_id); return std::make_tuple(functions_.get(function_id), false);
} }
// The function hasn't been deserialized yet. // The function hasn't been deserialized yet.
return AddDeferredReference(container, index, FUNCTION_ID, function_id); return std::make_tuple(
AddDeferredReference(container, index, FUNCTION_ID, function_id), true);
} }
Object WebSnapshotDeserializer::ReadClass(Handle<HeapObject> container, std::tuple<Object, bool> WebSnapshotDeserializer::ReadClass(
uint32_t index) { Handle<HeapObject> container, uint32_t index) {
uint32_t class_id; uint32_t class_id;
if (!deserializer_.ReadUint32(&class_id) || class_id >= kMaxItemCount) { if (!deserializer_.ReadUint32(&class_id) || class_id >= kMaxItemCount) {
Throw("Malformed object property"); Throw("Malformed object property");
return Smi::zero(); return std::make_tuple(Smi::zero(), false);
} }
if (class_id < current_class_count_) { if (class_id < current_class_count_) {
return classes_.get(class_id); return std::make_tuple(classes_.get(class_id), false);
} }
// The class hasn't been deserialized yet. // The class hasn't been deserialized yet.
return AddDeferredReference(container, index, CLASS_ID, class_id); return std::make_tuple(
AddDeferredReference(container, index, CLASS_ID, class_id), true);
} }
Object WebSnapshotDeserializer::ReadRegexp() { Object WebSnapshotDeserializer::ReadRegexp() {
...@@ -3128,13 +3122,9 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() { ...@@ -3128,13 +3122,9 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() {
// The only deferred reference allowed for a Map is the __proto__. // The only deferred reference allowed for a Map is the __proto__.
DCHECK_EQ(index, 0); DCHECK_EQ(index, 0);
DCHECK(target.IsJSReceiver()); DCHECK(target.IsJSReceiver());
HeapObject prototype = HeapObject::cast(target);
prototype.map().set_is_prototype_map(true);
{
AllowGarbageCollection allow_gc; AllowGarbageCollection allow_gc;
Map::SetPrototype(isolate_, handle(Map::cast(container), isolate_), SetPrototype(handle(Map::cast(container), isolate_),
handle(prototype, isolate_)); handle(target, isolate_));
}
raw_deferred_references = *deferred_references_; raw_deferred_references = *deferred_references_;
} else { } else {
UNREACHABLE(); UNREACHABLE();
......
...@@ -99,7 +99,7 @@ class WebSnapshotSerializerDeserializer { ...@@ -99,7 +99,7 @@ class WebSnapshotSerializerDeserializer {
void IterateBuiltinObjects(std::function<void(String, HeapObject)> func); void IterateBuiltinObjects(std::function<void(String, HeapObject)> func);
static constexpr int kBuiltinObjectCount = 2; static constexpr int kBuiltinObjectCount = 4;
inline Factory* factory() const { return isolate_->factory(); } inline Factory* factory() const { return isolate_->factory(); }
...@@ -397,13 +397,16 @@ class V8_EXPORT WebSnapshotDeserializer ...@@ -397,13 +397,16 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeArrays(); void DeserializeArrays();
void DeserializeObjects(); void DeserializeObjects();
void DeserializeExports(bool skip_exports); void DeserializeExports(bool skip_exports);
void DeserializeObjectPrototype(Handle<Map> map, uint32_t prototype_id); void DeserializeObjectPrototype(Handle<Map> map);
Handle<Map> DeserializeObjectPrototypeAndCreateEmptyMap();
void SetPrototype(Handle<Map> map, Handle<Object> prototype);
template <typename T> template <typename T>
void DeserializeObjectPropertiesWithDictionaryMap( void DeserializeObjectPropertiesWithDictionaryMap(
T dict, uint32_t property_count, bool has_custom_property_attributes); T dict, uint32_t property_count, bool has_custom_property_attributes);
Object ReadValue( // Return value: (object, was_deferred)
std::tuple<Object, bool> ReadValue(
Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(), Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(),
uint32_t index_for_deferred_reference = 0, uint32_t index_for_deferred_reference = 0,
InternalizeStrings internalize_strings = InternalizeStrings::kNo); InternalizeStrings internalize_strings = InternalizeStrings::kNo);
...@@ -415,10 +418,14 @@ class V8_EXPORT WebSnapshotDeserializer ...@@ -415,10 +418,14 @@ class V8_EXPORT WebSnapshotDeserializer
String ReadInPlaceString( String ReadInPlaceString(
InternalizeStrings internalize_strings = InternalizeStrings::kNo); InternalizeStrings internalize_strings = InternalizeStrings::kNo);
Object ReadSymbol(); Object ReadSymbol();
Object ReadArray(Handle<HeapObject> container, uint32_t container_index); std::tuple<Object, bool> ReadArray(Handle<HeapObject> container,
Object ReadObject(Handle<HeapObject> container, uint32_t container_index); uint32_t container_index);
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index); std::tuple<Object, bool> ReadObject(Handle<HeapObject> container,
Object ReadClass(Handle<HeapObject> container, uint32_t container_index); uint32_t container_index);
std::tuple<Object, bool> ReadFunction(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadClass(Handle<HeapObject> container,
uint32_t container_index);
Object ReadRegexp(); Object ReadRegexp();
Object ReadBuiltinObjectReference(); Object ReadBuiltinObjectReference();
Object ReadExternalReference(); Object ReadExternalReference();
......
...@@ -90,9 +90,9 @@ TEST(Minimal) { ...@@ -90,9 +90,9 @@ TEST(Minimal) {
const char* snapshot_source = "var foo = {'key': 'lol'};"; const char* snapshot_source = "var foo = {'key': 'lol'};";
const char* test_source = "foo.key"; const char* test_source = "foo.key";
const char* expected_result = "lol"; const char* expected_result = "lol";
uint32_t kStringCount = 1; // 'foo'; 'key' is in-place. uint32_t kStringCount = 2; // 'foo', 'Object.prototype'; 'key' is in-place.
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -106,9 +106,9 @@ TEST(Minimal) { ...@@ -106,9 +106,9 @@ TEST(Minimal) {
TEST(EmptyObject) { TEST(EmptyObject) {
const char* snapshot_source = "var foo = {}"; const char* snapshot_source = "var foo = {}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo' uint32_t kStringCount = 2; // 'foo', 'Object.prototype'
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -138,9 +138,10 @@ TEST(Numbers) { ...@@ -138,9 +138,10 @@ TEST(Numbers) {
" 'f': Number.NEGATIVE_INFINITY,\n" " 'f': Number.NEGATIVE_INFINITY,\n"
"}"; "}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'f' are in-place. uint32_t kStringCount =
2; // 'foo', 'Object.prototype'; 'a'...'f' are in-place.
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -195,9 +196,10 @@ TEST(Oddballs) { ...@@ -195,9 +196,10 @@ TEST(Oddballs) {
" 'd': undefined,\n" " 'd': undefined,\n"
"}"; "}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'d' are in-place. // 'foo', 'Object.prototype'; 'a'...'d' are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -226,9 +228,10 @@ TEST(Function) { ...@@ -226,9 +228,10 @@ TEST(Function) {
"var foo = {'key': function() { return '11525'; }};"; "var foo = {'key': function() { return '11525'; }};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; const char* expected_result = "11525";
uint32_t kStringCount = 2; // 'foo', function source code. 'key' is in-place. // 'foo', 'Object.prototype', function source code. 'key' is in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -248,10 +251,11 @@ TEST(InnerFunctionWithContext) { ...@@ -248,10 +251,11 @@ TEST(InnerFunctionWithContext) {
" })()};"; " })()};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; const char* expected_result = "11525";
// Strings: 'foo', 'result', function source code (inner). 'key' is in-place. // Strings: 'foo', 'result', 'Object.prototype'. function source code (inner).
uint32_t kStringCount = 3; // 'key' is in-place.
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 1; uint32_t kContextCount = 1;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -277,10 +281,11 @@ TEST(InnerFunctionWithContextAndParentContext) { ...@@ -277,10 +281,11 @@ TEST(InnerFunctionWithContextAndParentContext) {
" })()};"; " })()};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; const char* expected_result = "11525";
// Strings: 'foo', function source code (innerinner), 'part1', 'part2'. // Strings: 'foo', 'Object.prototype', function source code (innerinner),
uint32_t kStringCount = 4; // 'part1', 'part2'.
uint32_t kStringCount = 5;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 2; uint32_t kContextCount = 2;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -294,9 +299,10 @@ TEST(InnerFunctionWithContextAndParentContext) { ...@@ -294,9 +299,10 @@ TEST(InnerFunctionWithContextAndParentContext) {
TEST(RegExp) { TEST(RegExp) {
const char* snapshot_source = "var foo = {'re': /ab+c/gi}"; const char* snapshot_source = "var foo = {'re': /ab+c/gi}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags // 'foo', 'Object.prototype', RegExp pattern, RegExp flags
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -327,9 +333,10 @@ TEST(RegExp) { ...@@ -327,9 +333,10 @@ TEST(RegExp) {
TEST(RegExpNoFlags) { TEST(RegExpNoFlags) {
const char* snapshot_source = "var foo = {'re': /ab+c/}"; const char* snapshot_source = "var foo = {'re': /ab+c/}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags // 'foo', , 'Object.prototype RegExp pattern, RegExp flags
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -719,9 +726,10 @@ TEST(FunctionKinds) { ...@@ -719,9 +726,10 @@ TEST(FunctionKinds) {
" f: async function*() {}\n" " f: async function*() {}\n"
"}"; "}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 2; // 'foo', source code. 'a'...'f' in-place. // 'foo', 'Object.prototype', source code. 'a'...'f' in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 6; uint32_t kFunctionCount = 6;
...@@ -931,10 +939,10 @@ TEST(InPlaceStringsInObjects) { ...@@ -931,10 +939,10 @@ TEST(InPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'three'};"; const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'three'};";
const char* test_source = "foo.a + foo.b + foo.c;"; const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwothree"; const char* expected_result = "onetwothree";
// 'foo'. Other strings are in-place. // 'foo', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 1; uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -949,10 +957,10 @@ TEST(RepeatedInPlaceStringsInObjects) { ...@@ -949,10 +957,10 @@ TEST(RepeatedInPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'one'};"; const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'one'};";
const char* test_source = "foo.a + foo.b + foo.c;"; const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwoone"; const char* expected_result = "onetwoone";
// 'foo', 'one'. Other strings are in-place. // 'foo', 'one', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -963,14 +971,14 @@ TEST(RepeatedInPlaceStringsInObjects) { ...@@ -963,14 +971,14 @@ TEST(RepeatedInPlaceStringsInObjects) {
kFunctionCount, kObjectCount, kArrayCount); kFunctionCount, kObjectCount, kArrayCount);
} }
TEST(builtin_objects) { TEST(BuiltinObjects) {
const char* snapshot_source = "var foo = {a: Error.prototype};"; const char* snapshot_source = "var foo = {a: Error.prototype};";
const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\""; const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass"; const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place. // 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1; uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -981,15 +989,15 @@ TEST(builtin_objects) { ...@@ -981,15 +989,15 @@ TEST(builtin_objects) {
kFunctionCount, kObjectCount, kArrayCount); kFunctionCount, kObjectCount, kArrayCount);
} }
TEST(builtin_objectsDeduplicated) { TEST(BuiltinObjectsDeduplicated) {
const char* snapshot_source = const char* snapshot_source =
"var foo = {a: Error.prototype, b: Error.prototype}"; "var foo = {a: Error.prototype, b: Error.prototype}";
const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\""; const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass"; const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place. // 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1; uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
......
...@@ -41,3 +41,49 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js'); ...@@ -41,3 +41,49 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
assertEquals(1, obj.__proto__.x); assertEquals(1, obj.__proto__.x);
assertSame(Realm.eval(realm, 'Object.prototype'), obj.__proto__.__proto__); assertSame(Realm.eval(realm, 'Object.prototype'), obj.__proto__.__proto__);
})(); })();
(function TestDictionaryObjectPrototype() {
function createObjects() {
const obj = {};
// Create an object with dictionary map.
for (let i = 0; i < 2000; i++){
obj[`key${i}`] = `value${i}`;
}
obj.__proto__ = {x: 1};
globalThis.foo = obj;
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(2000, Object.keys(foo).length);
assertEquals(2000, Object.values(foo).length);
for (let i = 0; i < 2000; i++){
assertEquals(`value${i}`, foo[`key${i}`]);
}
assertEquals(1, foo.x);
assertEquals(1, foo.__proto__.x);
})();
(function TestNullPrototype() {
function createObjects() {
globalThis.foo = Object.create(null);
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(null, Object.getPrototypeOf(foo));
})();
(function TestInheritFromBuiltin() {
function createObjects() {
function inherit(subclass, superclass) {
function middle() {}
middle.prototype = superclass.prototype;
subclass.prototype = new middle();
subclass.prototype.constructor = subclass;
};
function MyError() {}
inherit(MyError, Error);
globalThis.MyError = MyError;
}
const realm = Realm.create();
const {MyError} = takeAndUseWebSnapshot(createObjects, ['MyError'], realm);
const obj = new MyError();
assertTrue(obj.__proto__.__proto__ === Realm.eval(realm, "Error.prototype"));
})();
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