Commit 39ed137b authored by yangguo's avatar yangguo Committed by Commit bot

[serializer] introduce API to serialize internal fields

See https://goo.gl/C9U1dL

BUG=chromium:617892

Review-Url: https://codereview.chromium.org/2452333002
Cr-Commit-Position: refs/heads/master@{#40623}
parent b4f76a8e
...@@ -6240,6 +6240,19 @@ class V8_EXPORT EmbedderHeapTracer { ...@@ -6240,6 +6240,19 @@ class V8_EXPORT EmbedderHeapTracer {
virtual ~EmbedderHeapTracer() = default; virtual ~EmbedderHeapTracer() = default;
}; };
/**
* Callback to the embedder used in SnapshotCreator to handle internal fields.
*/
typedef StartupData (*SerializeInternalFieldsCallback)(Local<Object> holder,
int index);
/**
* Callback to the embedder used to deserialize internal fields.
*/
typedef void (*DeserializeInternalFieldsCallback)(Local<Object> holder,
int index,
StartupData payload);
/** /**
* Isolate represents an isolated instance of the V8 engine. V8 isolates have * Isolate represents an isolated instance of the V8 engine. V8 isolates have
* completely separate states. Objects from one isolate must not be used in * completely separate states. Objects from one isolate must not be used in
...@@ -6262,7 +6275,8 @@ class V8_EXPORT Isolate { ...@@ -6262,7 +6275,8 @@ class V8_EXPORT Isolate {
create_histogram_callback(nullptr), create_histogram_callback(nullptr),
add_histogram_sample_callback(nullptr), add_histogram_sample_callback(nullptr),
array_buffer_allocator(nullptr), array_buffer_allocator(nullptr),
external_references(nullptr) {} external_references(nullptr),
deserialize_internal_fields_callback(nullptr) {}
/** /**
* The optional entry_hook allows the host application to provide the * The optional entry_hook allows the host application to provide the
...@@ -6318,6 +6332,12 @@ class V8_EXPORT Isolate { ...@@ -6318,6 +6332,12 @@ class V8_EXPORT Isolate {
* entire lifetime of the isolate. * entire lifetime of the isolate.
*/ */
intptr_t* external_references; intptr_t* external_references;
/**
* Specifies an optional callback to deserialize internal fields. It
* should match the SerializeInternalFieldCallback used to serialize.
*/
DeserializeInternalFieldsCallback deserialize_internal_fields_callback;
}; };
...@@ -7583,10 +7603,12 @@ class SnapshotCreator { ...@@ -7583,10 +7603,12 @@ class SnapshotCreator {
* This must not be called from within a handle scope. * This must not be called from within a handle scope.
* \param function_code_handling whether to include compiled function code * \param function_code_handling whether to include compiled function code
* in the snapshot. * in the snapshot.
* \param callback to serialize embedder-set internal fields.
* \returns { nullptr, 0 } on failure, and a startup snapshot on success. The * \returns { nullptr, 0 } on failure, and a startup snapshot on success. The
* caller acquires ownership of the data array in the return value. * caller acquires ownership of the data array in the return value.
*/ */
StartupData CreateBlob(FunctionCodeHandling function_code_handling); StartupData CreateBlob(FunctionCodeHandling function_code_handling,
SerializeInternalFieldsCallback callback = nullptr);
// Disallow copying and assigning. // Disallow copying and assigning.
SnapshotCreator(const SnapshotCreator&) = delete; SnapshotCreator(const SnapshotCreator&) = delete;
......
...@@ -523,7 +523,8 @@ size_t SnapshotCreator::AddTemplate(Local<Template> template_obj) { ...@@ -523,7 +523,8 @@ size_t SnapshotCreator::AddTemplate(Local<Template> template_obj) {
} }
StartupData SnapshotCreator::CreateBlob( StartupData SnapshotCreator::CreateBlob(
SnapshotCreator::FunctionCodeHandling function_code_handling) { SnapshotCreator::FunctionCodeHandling function_code_handling,
SerializeInternalFieldsCallback callback) {
SnapshotCreatorData* data = SnapshotCreatorData::cast(data_); SnapshotCreatorData* data = SnapshotCreatorData::cast(data_);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(data->isolate_); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(data->isolate_);
DCHECK(!data->created_); DCHECK(!data->created_);
...@@ -564,7 +565,8 @@ StartupData SnapshotCreator::CreateBlob( ...@@ -564,7 +565,8 @@ StartupData SnapshotCreator::CreateBlob(
// Serialize each context with a new partial serializer. // Serialize each context with a new partial serializer.
i::List<i::SnapshotData*> context_snapshots(num_contexts); i::List<i::SnapshotData*> context_snapshots(num_contexts);
for (int i = 0; i < num_contexts; i++) { for (int i = 0; i < num_contexts; i++) {
i::PartialSerializer partial_serializer(isolate, &startup_serializer); i::PartialSerializer partial_serializer(isolate, &startup_serializer,
callback);
partial_serializer.Serialize(&contexts[i]); partial_serializer.Serialize(&contexts[i]);
context_snapshots.Add(new i::SnapshotData(&partial_serializer)); context_snapshots.Add(new i::SnapshotData(&partial_serializer));
} }
...@@ -7889,6 +7891,8 @@ Isolate* Isolate::New(const Isolate::CreateParams& params) { ...@@ -7889,6 +7891,8 @@ Isolate* Isolate::New(const Isolate::CreateParams& params) {
} }
isolate->set_api_external_references(params.external_references); isolate->set_api_external_references(params.external_references);
isolate->set_deserialize_internal_fields_callback(
params.deserialize_internal_fields_callback);
SetResourceConstraints(isolate, params.constraints); SetResourceConstraints(isolate, params.constraints);
// TODO(jochen): Once we got rid of Isolate::Current(), we can remove this. // TODO(jochen): Once we got rid of Isolate::Current(), we can remove this.
Isolate::Scope isolate_scope(v8_isolate); Isolate::Scope isolate_scope(v8_isolate);
......
...@@ -402,6 +402,8 @@ typedef List<HeapObject*> DebugObjectCache; ...@@ -402,6 +402,8 @@ typedef List<HeapObject*> DebugObjectCache;
V(intptr_t*, api_external_references, nullptr) \ V(intptr_t*, api_external_references, nullptr) \
V(base::HashMap*, external_reference_map, nullptr) \ V(base::HashMap*, external_reference_map, nullptr) \
V(base::HashMap*, root_index_map, nullptr) \ V(base::HashMap*, root_index_map, nullptr) \
V(v8::DeserializeInternalFieldsCallback, \
deserialize_internal_fields_callback, nullptr) \
V(int, pending_microtask_count, 0) \ V(int, pending_microtask_count, 0) \
V(int, debug_microtask_count, 0) \ V(int, debug_microtask_count, 0) \
V(HStatistics*, hstatistics, nullptr) \ V(HStatistics*, hstatistics, nullptr) \
......
...@@ -128,6 +128,7 @@ MaybeHandle<Object> Deserializer::DeserializePartial( ...@@ -128,6 +128,7 @@ MaybeHandle<Object> Deserializer::DeserializePartial(
Object* root; Object* root;
VisitPointer(&root); VisitPointer(&root);
DeserializeDeferredObjects(); DeserializeDeferredObjects();
DeserializeInternalFields();
isolate->heap()->RegisterReservationsForBlackAllocation(reservations_); isolate->heap()->RegisterReservationsForBlackAllocation(reservations_);
...@@ -212,6 +213,31 @@ void Deserializer::DeserializeDeferredObjects() { ...@@ -212,6 +213,31 @@ void Deserializer::DeserializeDeferredObjects() {
} }
} }
void Deserializer::DeserializeInternalFields() {
if (!source_.HasMore() || source_.Get() != kInternalFieldsData) return;
DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate_);
DisallowCompilation no_compile(isolate_);
v8::DeserializeInternalFieldsCallback callback =
isolate_->deserialize_internal_fields_callback();
DCHECK_NOT_NULL(callback);
for (int code = source_.Get(); code != kSynchronize; code = source_.Get()) {
HandleScope scope(isolate_);
int space = code & kSpaceMask;
DCHECK(space <= kNumberOfSpaces);
DCHECK(code - space == kNewObject);
Handle<JSObject> obj(JSObject::cast(GetBackReferencedObject(space)),
isolate_);
int index = source_.GetInt();
int size = source_.GetInt();
byte* data = new byte[size];
source_.CopyRaw(data, size);
callback(v8::Utils::ToLocal(obj), index,
{reinterpret_cast<char*>(data), size});
delete[] data;
}
}
// Used to insert a deserialized internalized string into the string table. // Used to insert a deserialized internalized string into the string table.
class StringTableInsertionKey : public HashTableKey { class StringTableInsertionKey : public HashTableKey {
public: public:
......
...@@ -88,6 +88,7 @@ class Deserializer : public SerializerDeserializer { ...@@ -88,6 +88,7 @@ class Deserializer : public SerializerDeserializer {
} }
void DeserializeDeferredObjects(); void DeserializeDeferredObjects();
void DeserializeInternalFields();
void FlushICacheForNewIsolate(); void FlushICacheForNewIsolate();
void FlushICacheForNewCodeObjectsAndRecordEmbeddedObjects(); void FlushICacheForNewCodeObjectsAndRecordEmbeddedObjects();
......
...@@ -10,9 +10,12 @@ ...@@ -10,9 +10,12 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
PartialSerializer::PartialSerializer(Isolate* isolate, PartialSerializer::PartialSerializer(
StartupSerializer* startup_serializer) Isolate* isolate, StartupSerializer* startup_serializer,
: Serializer(isolate), startup_serializer_(startup_serializer) { v8::SerializeInternalFieldsCallback callback)
: Serializer(isolate),
startup_serializer_(startup_serializer),
serialize_internal_fields_(callback) {
InitializeCodeAddressMap(); InitializeCodeAddressMap();
} }
...@@ -40,6 +43,7 @@ void PartialSerializer::Serialize(Object** o) { ...@@ -40,6 +43,7 @@ void PartialSerializer::Serialize(Object** o) {
} }
VisitPointer(o); VisitPointer(o);
SerializeDeferredObjects(); SerializeDeferredObjects();
SerializeInternalFields();
Pad(); Pad();
} }
...@@ -96,6 +100,11 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, ...@@ -96,6 +100,11 @@ void PartialSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
function->ClearTypeFeedbackInfo(); function->ClearTypeFeedbackInfo();
} }
if (obj->IsJSObject()) {
JSObject* jsobj = JSObject::cast(obj);
if (jsobj->GetInternalFieldCount() > 0) internal_field_holders_.Add(jsobj);
}
// Object has not yet been serialized. Serialize it here. // Object has not yet been serialized. Serialize it here.
ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point); ObjectSerializer serializer(this, obj, &sink_, how_to_code, where_to_point);
serializer.Serialize(); serializer.Serialize();
...@@ -113,5 +122,34 @@ bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject* o) { ...@@ -113,5 +122,34 @@ bool PartialSerializer::ShouldBeInThePartialSnapshotCache(HeapObject* o) {
startup_serializer_->isolate()->heap()->fixed_cow_array_map(); startup_serializer_->isolate()->heap()->fixed_cow_array_map();
} }
void PartialSerializer::SerializeInternalFields() {
int count = internal_field_holders_.length();
if (count == 0) return;
DisallowHeapAllocation no_gc;
DisallowJavascriptExecution no_js(isolate());
DisallowCompilation no_compile(isolate());
DCHECK_NOT_NULL(serialize_internal_fields_);
sink_.Put(kInternalFieldsData, "internal fields data");
while (internal_field_holders_.length() > 0) {
HandleScope scope(isolate());
Handle<JSObject> obj(internal_field_holders_.RemoveLast(), isolate());
SerializerReference reference = reference_map_.Lookup(*obj);
DCHECK(reference.is_back_reference());
int internal_fields_count = obj->GetInternalFieldCount();
for (int i = 0; i < internal_fields_count; i++) {
if (obj->GetInternalField(i)->IsHeapObject()) continue;
StartupData data = serialize_internal_fields_(v8::Utils::ToLocal(obj), i);
sink_.Put(kNewObject + reference.space(), "internal field holder");
PutBackReference(*obj, reference);
sink_.PutInt(i, "internal field index");
sink_.PutInt(data.raw_size, "internal fields data size");
sink_.PutRaw(reinterpret_cast<const byte*>(data.data), data.raw_size,
"internal fields data");
delete[] data.data;
}
}
sink_.Put(kSynchronize, "Finished with internal fields data");
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -15,7 +15,8 @@ class StartupSerializer; ...@@ -15,7 +15,8 @@ class StartupSerializer;
class PartialSerializer : public Serializer { class PartialSerializer : public Serializer {
public: public:
PartialSerializer(Isolate* isolate, StartupSerializer* startup_serializer); PartialSerializer(Isolate* isolate, StartupSerializer* startup_serializer,
v8::SerializeInternalFieldsCallback callback);
~PartialSerializer() override; ~PartialSerializer() override;
...@@ -28,7 +29,11 @@ class PartialSerializer : public Serializer { ...@@ -28,7 +29,11 @@ class PartialSerializer : public Serializer {
bool ShouldBeInThePartialSnapshotCache(HeapObject* o); bool ShouldBeInThePartialSnapshotCache(HeapObject* o);
void SerializeInternalFields();
StartupSerializer* startup_serializer_; StartupSerializer* startup_serializer_;
List<JSObject*> internal_field_holders_;
v8::SerializeInternalFieldsCallback serialize_internal_fields_;
DISALLOW_COPY_AND_ASSIGN(PartialSerializer); DISALLOW_COPY_AND_ASSIGN(PartialSerializer);
}; };
......
...@@ -172,6 +172,8 @@ class SerializerDeserializer : public ObjectVisitor { ...@@ -172,6 +172,8 @@ class SerializerDeserializer : public ObjectVisitor {
// Used for the source code for compiled stubs, which is in the executable, // Used for the source code for compiled stubs, which is in the executable,
// but is referred to from external strings in the snapshot. // but is referred to from external strings in the snapshot.
static const int kExtraNativesStringResource = 0x1e; static const int kExtraNativesStringResource = 0x1e;
// Used for embedder-provided serialization data for internal fields.
static const int kInternalFieldsData = 0x1f;
// 8 hot (recently seen or back-referenced) objects with optional skip. // 8 hot (recently seen or back-referenced) objects with optional skip.
static const int kNumberOfHotObjects = 8; static const int kNumberOfHotObjects = 8;
...@@ -182,7 +184,7 @@ class SerializerDeserializer : public ObjectVisitor { ...@@ -182,7 +184,7 @@ class SerializerDeserializer : public ObjectVisitor {
static const int kHotObjectWithSkip = 0x58; static const int kHotObjectWithSkip = 0x58;
static const int kHotObjectMask = 0x07; static const int kHotObjectMask = 0x07;
// 0x1f, 0x35..0x37, 0x55..0x57, 0x75..0x7f unused. // 0x35..0x37, 0x55..0x57, 0x75..0x7f unused.
// ---------- byte code range 0x80..0xff ---------- // ---------- byte code range 0x80..0xff ----------
// First 32 root array items. // First 32 root array items.
......
...@@ -287,7 +287,7 @@ static void PartiallySerializeObject(Vector<const byte>* startup_blob_out, ...@@ -287,7 +287,7 @@ static void PartiallySerializeObject(Vector<const byte>* startup_blob_out,
isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear); isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear);
startup_serializer.SerializeStrongReferences(); startup_serializer.SerializeStrongReferences();
PartialSerializer partial_serializer(isolate, &startup_serializer); PartialSerializer partial_serializer(isolate, &startup_serializer, nullptr);
partial_serializer.Serialize(&raw_foo); partial_serializer.Serialize(&raw_foo);
startup_serializer.SerializeWeakReferencesAndDeferred(); startup_serializer.SerializeWeakReferencesAndDeferred();
...@@ -387,7 +387,7 @@ static void PartiallySerializeContext(Vector<const byte>* startup_blob_out, ...@@ -387,7 +387,7 @@ static void PartiallySerializeContext(Vector<const byte>* startup_blob_out,
startup_serializer.SerializeStrongReferences(); startup_serializer.SerializeStrongReferences();
SnapshotByteSink partial_sink; SnapshotByteSink partial_sink;
PartialSerializer partial_serializer(isolate, &startup_serializer); PartialSerializer partial_serializer(isolate, &startup_serializer, nullptr);
partial_serializer.Serialize(&raw_context); partial_serializer.Serialize(&raw_context);
startup_serializer.SerializeWeakReferencesAndDeferred(); startup_serializer.SerializeWeakReferencesAndDeferred();
...@@ -506,7 +506,7 @@ static void PartiallySerializeCustomContext( ...@@ -506,7 +506,7 @@ static void PartiallySerializeCustomContext(
startup_serializer.SerializeStrongReferences(); startup_serializer.SerializeStrongReferences();
SnapshotByteSink partial_sink; SnapshotByteSink partial_sink;
PartialSerializer partial_serializer(isolate, &startup_serializer); PartialSerializer partial_serializer(isolate, &startup_serializer, nullptr);
partial_serializer.Serialize(&raw_context); partial_serializer.Serialize(&raw_context);
startup_serializer.SerializeWeakReferencesAndDeferred(); startup_serializer.SerializeWeakReferencesAndDeferred();
...@@ -2109,10 +2109,38 @@ TEST(SnapshotCreatorUnknownExternalReferences) { ...@@ -2109,10 +2109,38 @@ TEST(SnapshotCreatorUnknownExternalReferences) {
delete[] blob.data; delete[] blob.data;
} }
struct InternalFieldData {
uint32_t data;
};
v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder,
int index) {
InternalFieldData* data = static_cast<InternalFieldData*>(
holder->GetAlignedPointerFromInternalField(index));
int size = sizeof(*data);
char* payload = new char[size];
// We simply use memcpy to serialize the content.
memcpy(payload, data, size);
return {payload, size};
}
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
v8::StartupData payload) {
InternalFieldData* data = new InternalFieldData{0};
memcpy(data, payload.data, payload.raw_size);
holder->SetAlignedPointerInInternalField(index, data);
}
TEST(SnapshotCreatorTemplates) { TEST(SnapshotCreatorTemplates) {
DisableTurbofan(); DisableTurbofan();
v8::StartupData blob; v8::StartupData blob;
{ {
InternalFieldData* a1 = new InternalFieldData{11};
InternalFieldData* b0 = new InternalFieldData{20};
InternalFieldData* c0 = new InternalFieldData{30};
InternalFieldData* c1 = new InternalFieldData{31};
v8::SnapshotCreator creator(original_external_references); v8::SnapshotCreator creator(original_external_references);
v8::Isolate* isolate = creator.GetIsolate(); v8::Isolate* isolate = creator.GetIsolate();
{ {
...@@ -2125,14 +2153,38 @@ TEST(SnapshotCreatorTemplates) { ...@@ -2125,14 +2153,38 @@ TEST(SnapshotCreatorTemplates) {
global_template->Set(v8_str("f"), callback); global_template->Set(v8_str("f"), callback);
v8::Local<v8::Context> context = v8::Local<v8::Context> context =
v8::Context::New(isolate, no_extension, global_template); v8::Context::New(isolate, no_extension, global_template);
v8::Local<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetInternalFieldCount(2);
v8::Context::Scope context_scope(context); v8::Context::Scope context_scope(context);
ExpectInt32("f()", 42); ExpectInt32("f()", 42);
v8::Local<v8::Object> a =
object_template->NewInstance(context).ToLocalChecked();
v8::Local<v8::Object> b =
object_template->NewInstance(context).ToLocalChecked();
v8::Local<v8::Object> c =
object_template->NewInstance(context).ToLocalChecked();
a->SetInternalField(0, b);
a->SetAlignedPointerInInternalField(1, a1);
b->SetAlignedPointerInInternalField(0, b0);
b->SetInternalField(1, c);
c->SetAlignedPointerInInternalField(0, c0);
c->SetAlignedPointerInInternalField(1, c1);
CHECK(context->Global()->Set(context, v8_str("a"), a).FromJust());
CHECK_EQ(0, creator.AddContext(context)); CHECK_EQ(0, creator.AddContext(context));
CHECK_EQ(0, creator.AddTemplate(callback)); CHECK_EQ(0, creator.AddTemplate(callback));
CHECK_EQ(1, creator.AddTemplate(global_template)); CHECK_EQ(1, creator.AddTemplate(global_template));
} }
blob = blob = creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear,
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); SerializeInternalFields);
delete a1;
delete b0;
delete c1;
delete c0;
} }
{ {
...@@ -2140,6 +2192,7 @@ TEST(SnapshotCreatorTemplates) { ...@@ -2140,6 +2192,7 @@ TEST(SnapshotCreatorTemplates) {
params.snapshot_blob = &blob; params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator(); params.array_buffer_allocator = CcTest::array_buffer_allocator();
params.external_references = original_external_references; params.external_references = original_external_references;
params.deserialize_internal_fields_callback = DeserializeInternalFields;
v8::Isolate* isolate = v8::Isolate::New(params); v8::Isolate* isolate = v8::Isolate::New(params);
{ {
v8::Isolate::Scope isolate_scope(isolate); v8::Isolate::Scope isolate_scope(isolate);
...@@ -2173,10 +2226,39 @@ TEST(SnapshotCreatorTemplates) { ...@@ -2173,10 +2226,39 @@ TEST(SnapshotCreatorTemplates) {
// Check that it instantiates to the same prototype. // Check that it instantiates to the same prototype.
ExpectTrue("g.prototype === f.prototype"); ExpectTrue("g.prototype === f.prototype");
// Retrieve internal fields.
v8::Local<v8::Object> a = context->Global()
->Get(context, v8_str("a"))
.ToLocalChecked()
->ToObject(context)
.ToLocalChecked();
v8::Local<v8::Object> b =
a->GetInternalField(0)->ToObject(context).ToLocalChecked();
InternalFieldData* a1 = reinterpret_cast<InternalFieldData*>(
a->GetAlignedPointerFromInternalField(1));
InternalFieldData* b0 = reinterpret_cast<InternalFieldData*>(
b->GetAlignedPointerFromInternalField(0));
v8::Local<v8::Object> c =
b->GetInternalField(1)->ToObject(context).ToLocalChecked();
InternalFieldData* c0 = reinterpret_cast<InternalFieldData*>(
c->GetAlignedPointerFromInternalField(0));
InternalFieldData* c1 = reinterpret_cast<InternalFieldData*>(
c->GetAlignedPointerFromInternalField(1));
CHECK_EQ(11, a1->data);
CHECK_EQ(20, b0->data);
CHECK_EQ(30, c0->data);
CHECK_EQ(31, c1->data);
// Accessing out of bound returns empty MaybeHandle. // Accessing out of bound returns empty MaybeHandle.
CHECK(v8::ObjectTemplate::FromSnapshot(isolate, 2).IsEmpty()); CHECK(v8::ObjectTemplate::FromSnapshot(isolate, 2).IsEmpty());
CHECK(v8::FunctionTemplate::FromSnapshot(isolate, 2).IsEmpty()); CHECK(v8::FunctionTemplate::FromSnapshot(isolate, 2).IsEmpty());
CHECK(v8::Context::FromSnapshot(isolate, 2).IsEmpty()); CHECK(v8::Context::FromSnapshot(isolate, 2).IsEmpty());
delete a1;
delete b0;
delete c1;
delete c0;
} }
{ {
......
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