[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: Yang Guo <yangguo@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#50307}
This commit is contained in:
parent
4c490296df
commit
54bf4d20de
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user