Commit 9be698ff authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[web-snapshot] Collect unsupported objects in the externals JSArray

With this change we can easily track and filter unsupported objects
for full-page snapshots.

Bug: v8:11525
Change-Id: Id75b6f4edf68b47d6dfbe79aed2b686aeec61068
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3484320Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79252}
parent 88ddce15
......@@ -1559,23 +1559,35 @@ RUNTIME_FUNCTION(Runtime_WebSnapshotSerialize) {
}
Handle<Object> object = args.at<Object>(0);
Handle<FixedArray> block_list = isolate->factory()->empty_fixed_array();
Handle<JSArray> block_list_js_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);
block_list_js_array = args.at<JSArray>(1);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, block_list,
JSReceiver::GetOwnValues(raw_blocklist,
JSReceiver::GetOwnValues(block_list_js_array,
PropertyFilter::ENUMERABLE_STRINGS));
}
auto snapshot_data = std::make_shared<WebSnapshotData>();
WebSnapshotSerializer serializer(isolate);
if (!serializer.TakeSnapshot(object, block_list, *snapshot_data)) {
DCHECK(isolate->has_pending_exception());
return ReadOnlyRoots(isolate).exception();
}
if (!block_list_js_array.is_null() &&
static_cast<uint32_t>(block_list->length()) <
serializer.external_objects_count()) {
Handle<FixedArray> externals = serializer.GetExternals();
Handle<Map> map = JSObject::GetElementsTransitionMap(block_list_js_array,
PACKED_ELEMENTS);
block_list_js_array->set_elements(*externals);
block_list_js_array->set_length(Smi::FromInt(externals->length()));
block_list_js_array->set_map(*map);
}
i::Handle<i::Object> managed_object = Managed<WebSnapshotData>::FromSharedPtr(
isolate, snapshot_data->buffer_size, snapshot_data);
return *managed_object;
......@@ -1613,8 +1625,8 @@ RUNTIME_FUNCTION(Runtime_WebSnapshotDeserialize) {
WebSnapshotDeserializer deserializer(v8_isolate, data->buffer,
data->buffer_size);
if (!deserializer.Deserialize(injected_references)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kWebSnapshotError));
DCHECK(isolate->has_pending_exception());
return ReadOnlyRoots(isolate).exception();
}
Handle<Object> object;
if (!deserializer.value().ToHandle(&object)) {
......
......@@ -142,8 +142,6 @@ 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) \
......@@ -481,11 +479,11 @@ namespace internal {
F(DebugTrace, 0, 1) \
F(DebugTrackRetainingPath, -1, 1) \
F(DeoptimizeFunction, 1, 1) \
F(DisableOptimizationFinalization, 0, 1) \
F(DisallowCodegenFromStrings, 1, 1) \
F(DisassembleFunction, 1, 1) \
F(EnableCodeLoggingForTesting, 0, 1) \
F(EnsureFeedbackVectorForFunction, 1, 1) \
F(DisableOptimizationFinalization, 0, 1) \
F(FinalizeOptimization, 0, 1) \
F(GetCallable, 0, 1) \
F(GetInitializerFunction, 1, 1) \
......@@ -539,10 +537,10 @@ namespace internal {
F(PretenureAllocationSite, 1, 1) \
F(PrintWithNameForAssert, 2, 1) \
F(PromiseSpeciesProtector, 0, 1) \
F(RegExpSpeciesProtector, 0, 1) \
F(RegexpHasBytecode, 2, 1) \
F(RegexpHasNativeCode, 2, 1) \
F(RegexpIsUnmodified, 1, 1) \
F(RegExpSpeciesProtector, 0, 1) \
F(RegexpTypeTag, 1, 1) \
F(RunningInSimulator, 0, 1) \
F(RuntimeEvaluateREPL, 1, 1) \
......@@ -561,6 +559,8 @@ namespace internal {
F(TurbofanStaticAssert, 1, 1) \
F(TypedArraySpeciesProtector, 0, 1) \
F(WaitForBackgroundOptimization, 0, 1) \
F(WebSnapshotDeserialize, -1, 1) \
F(WebSnapshotSerialize, -1, 1) \
I(DeoptimizeNow, 0, 1)
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F, I) \
......
......@@ -1255,5 +1255,21 @@ Serializer::HotObjectsList::~HotObjectsList() {
heap_->UnregisterStrongRoots(strong_roots_entry_);
}
Handle<FixedArray> ObjectCacheIndexMap::Values(Isolate* isolate) {
if (size() == 0) {
return isolate->factory()->empty_fixed_array();
}
Handle<FixedArray> externals = isolate->factory()->NewFixedArray(size());
DisallowGarbageCollection no_gc;
FixedArray raw = *externals;
IdentityMap<int, base::DefaultAllocationPolicy>::IteratableScope it_scope(
&map_);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
raw.set(*it.entry(), it.key());
}
return externals;
}
} // namespace internal
} // namespace v8
......@@ -162,6 +162,8 @@ class ObjectCacheIndexMap {
return true;
}
Handle<FixedArray> Values(Isolate* isolate);
int size() const { return next_index_; }
private:
......
......@@ -653,6 +653,9 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
if (object->IsString()) {
// Can't contain references to other objects.
break;
} else if (external_objects_ids_.size() > 0) {
int unused_id;
external_objects_ids_.LookupOrInsert(*object, &unused_id);
} else {
Throw("Unsupported object");
}
......@@ -963,9 +966,10 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
}
DCHECK(object->IsHeapObject());
switch (HeapObject::cast(*object).map().instance_type()) {
Handle<HeapObject> heap_object = Handle<HeapObject>::cast(object);
switch ((*heap_object).map().instance_type()) {
case ODDBALL_TYPE:
switch (Oddball::cast(*object).kind()) {
switch (Oddball::cast(*heap_object).kind()) {
case Oddball::kFalse:
serializer.WriteUint32(ValueType::FALSE_CONSTANT);
return;
......@@ -984,26 +988,26 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
case HEAP_NUMBER_TYPE:
// TODO(v8:11525): Handle possible endianness mismatch.
serializer.WriteUint32(ValueType::DOUBLE);
serializer.WriteDouble(HeapNumber::cast(*object).value());
serializer.WriteDouble(HeapNumber::cast(*heap_object).value());
break;
case JS_FUNCTION_TYPE:
serializer.WriteUint32(ValueType::FUNCTION_ID);
serializer.WriteUint32(GetFunctionId(JSFunction::cast(*object)));
serializer.WriteUint32(GetFunctionId(JSFunction::cast(*heap_object)));
break;
case JS_CLASS_CONSTRUCTOR_TYPE:
serializer.WriteUint32(ValueType::CLASS_ID);
serializer.WriteUint32(GetClassId(JSFunction::cast(*object)));
serializer.WriteUint32(GetClassId(JSFunction::cast(*heap_object)));
break;
case JS_OBJECT_TYPE:
serializer.WriteUint32(ValueType::OBJECT_ID);
serializer.WriteUint32(GetObjectId(JSObject::cast(*object)));
serializer.WriteUint32(GetObjectId(JSObject::cast(*heap_object)));
break;
case JS_ARRAY_TYPE:
serializer.WriteUint32(ValueType::ARRAY_ID);
serializer.WriteUint32(GetArrayId(JSArray::cast(*object)));
serializer.WriteUint32(GetArrayId(JSArray::cast(*heap_object)));
break;
case JS_REG_EXP_TYPE: {
Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object);
Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(heap_object);
if (regexp->map() != isolate_->regexp_function()->initial_map()) {
Throw("Unsupported RegExp map");
return;
......@@ -1020,8 +1024,8 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
break;
}
default:
if (object->IsString()) {
SerializeString(Handle<String>::cast(object), id);
if (heap_object->IsString()) {
SerializeString(Handle<String>::cast(heap_object), id);
serializer.WriteUint32(ValueType::STRING_ID);
serializer.WriteUint32(id);
} else {
......@@ -1079,6 +1083,10 @@ uint32_t WebSnapshotSerializer::GetExternalId(HeapObject object) {
return static_cast<uint32_t>(id);
}
Handle<FixedArray> WebSnapshotSerializer::GetExternals() {
return external_objects_ids_.Values(isolate_);
}
WebSnapshotDeserializer::WebSnapshotDeserializer(v8::Isolate* isolate,
const uint8_t* data,
size_t buffer_size)
......@@ -1215,7 +1223,6 @@ bool WebSnapshotDeserializer::Deserialize(
timer.Start();
}
if (!DeserializeSnapshot()) {
isolate_->ReportPendingMessages();
return false;
}
if (!DeserializeScript()) {
......@@ -2051,8 +2058,8 @@ Object WebSnapshotDeserializer::ReadRegexp() {
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");
ref_id >= static_cast<uint32_t>(external_references_.length())) {
Throw("Invalid external reference");
return Smi::zero();
}
return external_references_.get(ref_id);
......
......@@ -152,6 +152,12 @@ 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());
}
Handle<FixedArray> GetExternals();
private:
WebSnapshotSerializer(const WebSnapshotSerializer&) = delete;
WebSnapshotSerializer& operator=(const WebSnapshotSerializer&) = delete;
......
......@@ -15,7 +15,6 @@ const object = {
d: { d_a: external_2 }
};
(function testNoExternals() {
const snapshot = %WebSnapshotSerialize(object);
const deserialized = %WebSnapshotDeserialize(snapshot);
......@@ -55,3 +54,30 @@ const object = {
assertSame(deserialized.c[1], replaced_externals[1]);
assertSame(deserialized.d.d_a, replaced_externals[1]);
})();
(function testApiObject() {
const api_object = new d8.dom.Div();
const source_1 = [{}, api_object];
assertThrows(() => %WebSnapshotSerialize(source_1));
let externals = [external_1]
const source_2 = [{}, external_1, api_object, api_object];
const snapshot_2 = %WebSnapshotSerialize(source_2, externals);
%HeapObjectVerify(externals);
// Check that the unhandled api object is added to the externals.
assertArrayEquals(externals, [external_1, api_object]);
assertThrows(() => %WebSnapshotDeserialize(snapshot_2));
assertThrows(() => %WebSnapshotDeserialize(snapshot_2, []));
assertThrows(() => %WebSnapshotDeserialize(snapshot_2, [external_1]));
const result_2 = %WebSnapshotDeserialize(snapshot_2, [external_1, api_object]);
%HeapObjectVerify(externals);
%HeapObjectVerify(result_2);
assertArrayEquals(result_2, source_2);
assertNotSame(result_2[0], source_2[0]);
assertSame(result_2[1], external_1);
assertSame(result_2[2], api_object);
assertSame(result_2[3], api_object);
})();
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