Commit 54bf4d20 authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

[snapshot] support serializing external strings with known resource.

TBR=ulan@chromium.org
R=jgruber@chromium.org


Bug: v8:7240
Change-Id: I4273105b496da16a5e00f53a6df3112efddedc91
Reviewed-on: https://chromium-review.googlesource.com/842882
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50307}
parent 4c490296
......@@ -629,7 +629,7 @@ enum VisitMode {
VISIT_ALL_IN_SCAVENGE,
VISIT_ALL_IN_SWEEP_NEWSPACE,
VISIT_ONLY_STRONG,
VISIT_ONLY_STRONG_FOR_SERIALIZATION,
VISIT_FOR_SERIALIZATION,
};
// Flag indicating whether code is built into the VM (one of the natives files).
......
......@@ -4877,8 +4877,11 @@ void Heap::IterateWeakRoots(RootVisitor* v, VisitMode mode) {
v->VisitRootPointer(Root::kStringTable, reinterpret_cast<Object**>(
&roots_[kStringTableRootIndex]));
v->Synchronize(VisitorSynchronization::kStringTable);
if (!isMinorGC && mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
if (!isMinorGC && mode != VISIT_ALL_IN_SWEEP_NEWSPACE &&
mode != VISIT_FOR_SERIALIZATION) {
// Scavenge collections have special processing for this.
// Do not visit for serialization, since the external string table will
// be populated from scratch upon deserialization.
external_string_table_.IterateAll(v);
}
v->Synchronize(VisitorSynchronization::kExternalStringsTable);
......@@ -4980,7 +4983,7 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
// Iterate over global handles.
switch (mode) {
case VISIT_ONLY_STRONG_FOR_SERIALIZATION:
case VISIT_FOR_SERIALIZATION:
case VISIT_ONLY_STRONG:
isolate_->global_handles()->IterateStrongRoots(v);
break;
......@@ -5019,7 +5022,7 @@ void Heap::IterateStrongRoots(RootVisitor* v, VisitMode mode) {
v->Synchronize(VisitorSynchronization::kStrongRoots);
// Iterate over the partial snapshot cache unless serializing.
if (mode != VISIT_ONLY_STRONG_FOR_SERIALIZATION) {
if (mode != VISIT_FOR_SERIALIZATION) {
SerializerDeserializer::Iterate(isolate_, v);
}
// We don't do a v->Synchronize call here, because in debug mode that will
......
......@@ -534,6 +534,33 @@ bool ExternalString::is_short() {
return (type & kShortExternalStringMask) == kShortExternalStringTag;
}
Address ExternalString::resource_as_address() {
return *reinterpret_cast<Address*>(FIELD_ADDR(this, kResourceOffset));
}
void ExternalString::set_address_as_resource(Address address) {
DCHECK(IsAligned(reinterpret_cast<intptr_t>(address), kPointerSize));
*reinterpret_cast<Address*>(FIELD_ADDR(this, kResourceOffset)) = address;
if (IsExternalOneByteString()) {
ExternalOneByteString::cast(this)->update_data_cache();
} else {
ExternalTwoByteString::cast(this)->update_data_cache();
}
}
uint32_t ExternalString::resource_as_uint32() {
return static_cast<uint32_t>(
*reinterpret_cast<uintptr_t*>(FIELD_ADDR(this, kResourceOffset)));
}
void ExternalString::set_uint32_as_resource(uint32_t value) {
*reinterpret_cast<uintptr_t*>(FIELD_ADDR(this, kResourceOffset)) = value;
if (is_short()) return;
const char** data_field =
reinterpret_cast<const char**>(FIELD_ADDR(this, kResourceDataOffset));
*data_field = nullptr;
}
const ExternalOneByteString::Resource* ExternalOneByteString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset));
}
......
......@@ -719,6 +719,12 @@ class ExternalString : public String {
// Return whether external string is short (data pointer is not cached).
inline bool is_short();
// Used in the serializer/deserializer.
inline Address resource_as_address();
inline void set_address_as_resource(Address address);
inline uint32_t resource_as_uint32();
inline void set_uint32_as_resource(uint32_t value);
STATIC_ASSERT(kResourceOffset == Internals::kStringResourceOffset);
private:
......
......@@ -192,14 +192,21 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
if (isolate_->external_reference_redirector()) {
call_handler_infos_.push_back(CallHandlerInfo::cast(obj));
}
} else if (obj->IsExternalOneByteString()) {
DCHECK(obj->map() == isolate_->heap()->native_source_string_map());
ExternalOneByteString* string = ExternalOneByteString::cast(obj);
DCHECK(string->is_short());
string->set_resource(
NativesExternalStringResource::DecodeForDeserialization(
string->resource()));
isolate_->heap()->RegisterExternalString(string);
} else if (obj->IsExternalString()) {
if (obj->map() == isolate_->heap()->native_source_string_map()) {
ExternalOneByteString* string = ExternalOneByteString::cast(obj);
DCHECK(string->is_short());
string->set_resource(
NativesExternalStringResource::DecodeForDeserialization(
string->resource()));
} else {
ExternalString* string = ExternalString::cast(obj);
uint32_t index = string->resource_as_uint32();
Address address =
reinterpret_cast<Address>(isolate_->api_external_references()[index]);
string->set_address_as_resource(address);
}
isolate_->heap()->RegisterExternalString(String::cast(obj));
} else if (obj->IsJSTypedArray()) {
JSTypedArray* typed_array = JSTypedArray::cast(obj);
CHECK(typed_array->byte_offset()->IsSmi());
......
......@@ -55,6 +55,17 @@ ExternalReferenceEncoder::~ExternalReferenceEncoder() {
#endif // DEBUG
}
Maybe<ExternalReferenceEncoder::Value> ExternalReferenceEncoder::TryEncode(
Address address) {
Maybe<uint32_t> maybe_index = map_->Get(address);
if (maybe_index.IsNothing()) return Nothing<Value>();
Value result(maybe_index.FromJust());
#ifdef DEBUG
if (result.is_from_api()) count_[result.index()]++;
#endif // DEBUG
return Just<Value>(result);
}
ExternalReferenceEncoder::Value ExternalReferenceEncoder::Encode(
Address address) {
Maybe<uint32_t> maybe_index = map_->Get(address);
......
......@@ -22,6 +22,7 @@ class ExternalReferenceEncoder {
class Value {
public:
explicit Value(uint32_t raw) : value_(raw) {}
Value() : value_(0) {}
static uint32_t Encode(uint32_t index, bool is_from_api) {
return Index::encode(index) | IsFromAPI::encode(is_from_api);
}
......@@ -40,6 +41,7 @@ class ExternalReferenceEncoder {
~ExternalReferenceEncoder();
Value Encode(Address key);
Maybe<Value> TryEncode(Address key);
const char* NameOfAddress(Isolate* isolate, Address address) const;
......
......@@ -454,13 +454,24 @@ void Serializer<AllocatorT>::ObjectSerializer::SerializeJSArrayBuffer() {
template <class AllocatorT>
void Serializer<AllocatorT>::ObjectSerializer::SerializeExternalString() {
Heap* heap = serializer_->isolate()->heap();
// For external strings with known resources, we replace the resource field
// with the encoded external reference, which we restore upon deserialize.
// for native native source code strings, we replace the resource field
// with the native source id.
// For the rest we serialize them to look like ordinary sequential strings.
if (object_->map() != heap->native_source_string_map()) {
// Usually we cannot recreate resources for external strings. To work
// around this, external strings are serialized to look like ordinary
// sequential strings.
// The exception are native source code strings, since we can recreate
// their resources.
SerializeExternalStringAsSequentialString();
ExternalString* string = ExternalString::cast(object_);
Address resource = string->resource_as_address();
ExternalReferenceEncoder::Value reference;
if (serializer_->external_reference_encoder_.TryEncode(resource).To(
&reference)) {
DCHECK(reference.is_from_api());
string->set_uint32_as_resource(reference.index());
SerializeObject();
string->set_address_as_resource(resource);
} else {
SerializeExternalStringAsSequentialString();
}
} else {
ExternalOneByteString* string = ExternalOneByteString::cast(object_);
DCHECK(string->is_short());
......
......@@ -37,7 +37,7 @@ void StartupDeserializer::DeserializeInto(Isolate* isolate) {
isolate->heap()->IterateSmiRoots(this);
isolate->heap()->IterateStrongRoots(this, VISIT_ONLY_STRONG);
isolate->heap()->RepairFreeListsAfterDeserialization();
isolate->heap()->IterateWeakRoots(this, VISIT_ALL);
isolate->heap()->IterateWeakRoots(this, VISIT_FOR_SERIALIZATION);
DeserializeDeferredObjects();
RestoreExternalReferenceRedirectors(accessor_infos());
RestoreExternalReferenceRedirectors(call_handler_infos());
......
......@@ -95,7 +95,7 @@ void StartupSerializer::SerializeWeakReferencesAndDeferred() {
// one entry with 'undefined' to terminate the partial snapshot cache.
Object* undefined = isolate()->heap()->undefined_value();
VisitRootPointer(Root::kPartialSnapshotCache, &undefined);
isolate()->heap()->IterateWeakRoots(this, VISIT_ALL);
isolate()->heap()->IterateWeakRoots(this, VISIT_FOR_SERIALIZATION);
SerializeDeferredObjects();
Pad();
}
......@@ -131,8 +131,7 @@ void StartupSerializer::SerializeStrongReferences() {
isolate->heap()->IterateSmiRoots(this);
isolate->heap()->SetStackLimits();
// First visit immortal immovables to make sure they end up in the first page.
isolate->heap()->IterateStrongRoots(this,
VISIT_ONLY_STRONG_FOR_SERIALIZATION);
isolate->heap()->IterateStrongRoots(this, VISIT_FOR_SERIALIZATION);
}
void StartupSerializer::VisitRootPointers(Root root, Object** start,
......
......@@ -1640,28 +1640,34 @@ class SerializerOneByteResource
: public v8::String::ExternalOneByteStringResource {
public:
SerializerOneByteResource(const char* data, size_t length)
: data_(data), length_(length) {}
: data_(data), length_(length), dispose_count_(0) {}
virtual const char* data() const { return data_; }
virtual size_t length() const { return length_; }
virtual void Dispose() { dispose_count_++; }
int dispose_count() { return dispose_count_; }
private:
const char* data_;
size_t length_;
int dispose_count_;
};
class SerializerTwoByteResource : public v8::String::ExternalStringResource {
public:
SerializerTwoByteResource(const char* data, size_t length)
: data_(AsciiToTwoByteString(data)), length_(length) {}
: data_(AsciiToTwoByteString(data)), length_(length), dispose_count_(0) {}
~SerializerTwoByteResource() { DeleteArray<const uint16_t>(data_); }
virtual const uint16_t* data() const { return data_; }
virtual size_t length() const { return length_; }
virtual void Dispose() { dispose_count_++; }
int dispose_count() { return dispose_count_; }
private:
const uint16_t* data_;
size_t length_;
int dispose_count_;
};
TEST(CodeSerializerExternalString) {
......@@ -2323,6 +2329,9 @@ class SerializedExtension : public v8::Extension {
}
};
static SerializerOneByteResource serializable_one_byte_resource("one_byte", 8);
static SerializerTwoByteResource serializable_two_byte_resource("two_byte", 8);
intptr_t original_external_references[] = {
reinterpret_cast<intptr_t>(SerializedCallback),
reinterpret_cast<intptr_t>(&serialized_static_field),
......@@ -2330,6 +2339,8 @@ intptr_t original_external_references[] = {
reinterpret_cast<intptr_t>(&AccessorForSerialization),
reinterpret_cast<intptr_t>(&SerializedExtension::FunctionCallback),
reinterpret_cast<intptr_t>(&serialized_static_field), // duplicate entry
reinterpret_cast<intptr_t>(&serializable_one_byte_resource),
reinterpret_cast<intptr_t>(&serializable_two_byte_resource),
0};
intptr_t replaced_external_references[] = {
......@@ -2339,6 +2350,8 @@ intptr_t replaced_external_references[] = {
reinterpret_cast<intptr_t>(&AccessorForSerialization),
reinterpret_cast<intptr_t>(&SerializedExtension::FunctionCallback),
reinterpret_cast<intptr_t>(&serialized_static_field),
reinterpret_cast<intptr_t>(&serializable_one_byte_resource),
reinterpret_cast<intptr_t>(&serializable_two_byte_resource),
0};
intptr_t short_external_references[] = {
......@@ -2359,13 +2372,32 @@ TEST(SnapshotCreatorExternalReferences) {
v8::Local<v8::Value> function =
callback->GetFunction(context).ToLocalChecked();
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
CHECK(context->Global()
->Set(context, v8_str("one_byte"),
v8::String::NewExternalOneByte(
isolate, &serializable_one_byte_resource)
.ToLocalChecked())
.FromJust());
CHECK(context->Global()
->Set(context, v8_str("two_byte"),
v8::String::NewExternalTwoByte(
isolate, &serializable_two_byte_resource)
.ToLocalChecked())
.FromJust());
ExpectInt32("f()", 42);
ExpectString("one_byte", "one_byte");
ExpectString("two_byte", "two_byte");
creator.SetDefaultContext(context);
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
CHECK_EQ(1, serializable_one_byte_resource.dispose_count());
CHECK_EQ(1, serializable_two_byte_resource.dispose_count());
// Deserialize with the original external reference.
{
v8::Isolate::CreateParams params;
......@@ -2380,10 +2412,17 @@ TEST(SnapshotCreatorExternalReferences) {
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
ExpectInt32("f()", 42);
ExpectString("one_byte", "one_byte");
ExpectString("two_byte", "two_byte");
CHECK(CompileRun("one_byte").As<v8::String>()->IsExternalOneByte());
CHECK(CompileRun("two_byte").As<v8::String>()->IsExternal());
}
isolate->Dispose();
}
CHECK_EQ(2, serializable_one_byte_resource.dispose_count());
CHECK_EQ(2, serializable_two_byte_resource.dispose_count());
// Deserialize with some other external reference.
{
v8::Isolate::CreateParams params;
......@@ -2401,6 +2440,10 @@ TEST(SnapshotCreatorExternalReferences) {
}
isolate->Dispose();
}
CHECK_EQ(3, serializable_one_byte_resource.dispose_count());
CHECK_EQ(3, serializable_two_byte_resource.dispose_count());
delete[] blob.data;
}
......
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