mirror of
https://github.com/tiennm99/FBcount.git
synced 2026-05-18 19:25:28 +00:00
485 lines
19 KiB
Diff
485 lines
19 KiB
Diff
commit a49ac519c8df83c52ed67e3860c51ccad80e037a
|
|
Author: yangguo@chromium.org <yangguo@chromium.org>
|
|
Date: Wed Sep 17 12:50:17 2014 +0000
|
|
|
|
Serialize code stubs using stub key.
|
|
|
|
Also add some tracing to the code serializer.
|
|
|
|
R=mvstanton@chromium.org
|
|
|
|
Review URL: https://codereview.chromium.org/556243005
|
|
|
|
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24002 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
|
|
|
|
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
|
|
index 4208e62..792fda9 100644
|
|
--- node/deps/v8/src/flag-definitions.h
|
|
+++ node/deps/v8/src/flag-definitions.h
|
|
@@ -428,6 +428,7 @@ DEFINE_BOOL(trace_stub_failures, false,
|
|
"trace deoptimization of generated code stubs")
|
|
|
|
DEFINE_BOOL(serialize_toplevel, false, "enable caching of toplevel scripts")
|
|
+DEFINE_BOOL(trace_code_serializer, false, "trace code serializer")
|
|
|
|
// compiler.cc
|
|
DEFINE_INT(min_preparse_length, 1024,
|
|
diff --git a/src/serialize.cc b/src/serialize.cc
|
|
index 1a19ab0..9447fc8 100644
|
|
--- node/deps/v8/src/serialize.cc
|
|
+++ node/deps/v8/src/serialize.cc
|
|
@@ -8,6 +8,7 @@
|
|
#include "src/api.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/bootstrapper.h"
|
|
+#include "src/code-stubs.h"
|
|
#include "src/deoptimizer.h"
|
|
#include "src/execution.h"
|
|
#include "src/global-handles.h"
|
|
@@ -872,7 +873,7 @@ void Deserializer::ReadChunk(Object** current,
|
|
} else if (where == kAttachedReference) { \
|
|
DCHECK(deserializing_user_code()); \
|
|
int index = source_->GetInt(); \
|
|
- new_object = attached_objects_->at(index); \
|
|
+ new_object = *attached_objects_->at(index); \
|
|
emit_write_barrier = isolate->heap()->InNewSpace(new_object); \
|
|
} else { \
|
|
DCHECK(where == kBackrefWithSkip); \
|
|
@@ -1125,6 +1126,10 @@ void Deserializer::ReadChunk(Object** current,
|
|
// the current object.
|
|
CASE_STATEMENT(kAttachedReference, kPlain, kStartOfObject, 0)
|
|
CASE_BODY(kAttachedReference, kPlain, kStartOfObject, 0)
|
|
+ CASE_STATEMENT(kAttachedReference, kPlain, kInnerPointer, 0)
|
|
+ CASE_BODY(kAttachedReference, kPlain, kInnerPointer, 0)
|
|
+ CASE_STATEMENT(kAttachedReference, kFromCode, kInnerPointer, 0)
|
|
+ CASE_BODY(kAttachedReference, kFromCode, kInnerPointer, 0)
|
|
|
|
#undef CASE_STATEMENT
|
|
#undef CASE_BODY
|
|
@@ -1311,12 +1316,12 @@ int Serializer::RootIndex(HeapObject* heap_object, HowToCode from) {
|
|
// location into a later object. We can encode the location as an offset from
|
|
// the start of the deserialized objects or as an offset backwards from the
|
|
// current allocation pointer.
|
|
-void Serializer::SerializeReferenceToPreviousObject(
|
|
- int space,
|
|
- int address,
|
|
- HowToCode how_to_code,
|
|
- WhereToPoint where_to_point,
|
|
- int skip) {
|
|
+void Serializer::SerializeReferenceToPreviousObject(HeapObject* heap_object,
|
|
+ HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point,
|
|
+ int skip) {
|
|
+ int space = SpaceOfObject(heap_object);
|
|
+ int address = address_mapper_.MappedTo(heap_object);
|
|
int offset = CurrentAllocationAddress(space) - address;
|
|
// Shift out the bits that are always 0.
|
|
offset >>= kObjectAlignmentBits;
|
|
@@ -1347,12 +1352,7 @@ void StartupSerializer::SerializeObject(
|
|
}
|
|
|
|
if (address_mapper_.IsMapped(heap_object)) {
|
|
- int space = SpaceOfObject(heap_object);
|
|
- int address = address_mapper_.MappedTo(heap_object);
|
|
- SerializeReferenceToPreviousObject(space,
|
|
- address,
|
|
- how_to_code,
|
|
- where_to_point,
|
|
+ SerializeReferenceToPreviousObject(heap_object, how_to_code, where_to_point,
|
|
skip);
|
|
} else {
|
|
if (skip != 0) {
|
|
@@ -1455,12 +1455,7 @@ void PartialSerializer::SerializeObject(
|
|
DCHECK(!heap_object->IsInternalizedString());
|
|
|
|
if (address_mapper_.IsMapped(heap_object)) {
|
|
- int space = SpaceOfObject(heap_object);
|
|
- int address = address_mapper_.MappedTo(heap_object);
|
|
- SerializeReferenceToPreviousObject(space,
|
|
- address,
|
|
- how_to_code,
|
|
- where_to_point,
|
|
+ SerializeReferenceToPreviousObject(heap_object, how_to_code, where_to_point,
|
|
skip);
|
|
} else {
|
|
if (skip != 0) {
|
|
@@ -1782,17 +1777,32 @@ void Serializer::InitializeCodeAddressMap() {
|
|
ScriptData* CodeSerializer::Serialize(Isolate* isolate,
|
|
Handle<SharedFunctionInfo> info,
|
|
Handle<String> source) {
|
|
+ base::ElapsedTimer timer;
|
|
+ if (FLAG_profile_deserialization) timer.Start();
|
|
+
|
|
// Serialize code object.
|
|
List<byte> payload;
|
|
ListSnapshotSink list_sink(&payload);
|
|
- CodeSerializer cs(isolate, &list_sink, *source);
|
|
+ DebugSnapshotSink debug_sink(&list_sink);
|
|
+ SnapshotByteSink* sink = FLAG_trace_code_serializer
|
|
+ ? static_cast<SnapshotByteSink*>(&debug_sink)
|
|
+ : static_cast<SnapshotByteSink*>(&list_sink);
|
|
+ CodeSerializer cs(isolate, sink, *source);
|
|
DisallowHeapAllocation no_gc;
|
|
Object** location = Handle<Object>::cast(info).location();
|
|
cs.VisitPointer(location);
|
|
cs.Pad();
|
|
|
|
SerializedCodeData data(&payload, &cs);
|
|
- return data.GetScriptData();
|
|
+ ScriptData* script_data = data.GetScriptData();
|
|
+
|
|
+ if (FLAG_profile_deserialization) {
|
|
+ double ms = timer.Elapsed().InMillisecondsF();
|
|
+ int length = script_data->length();
|
|
+ PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
|
|
+ }
|
|
+
|
|
+ return script_data;
|
|
}
|
|
|
|
|
|
@@ -1813,16 +1823,13 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
|
|
return;
|
|
}
|
|
|
|
- // TODO(yangguo) wire up stubs from stub cache.
|
|
// TODO(yangguo) wire up global object.
|
|
// TODO(yangguo) We cannot deal with different hash seeds yet.
|
|
DCHECK(!heap_object->IsHashTable());
|
|
|
|
if (address_mapper_.IsMapped(heap_object)) {
|
|
- int space = SpaceOfObject(heap_object);
|
|
- int address = address_mapper_.MappedTo(heap_object);
|
|
- SerializeReferenceToPreviousObject(space, address, how_to_code,
|
|
- where_to_point, skip);
|
|
+ SerializeReferenceToPreviousObject(heap_object, how_to_code, where_to_point,
|
|
+ skip);
|
|
return;
|
|
}
|
|
|
|
@@ -1832,7 +1839,11 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
|
|
SerializeBuiltin(code_object, how_to_code, where_to_point, skip);
|
|
return;
|
|
}
|
|
- // TODO(yangguo) figure out whether other code kinds can be handled smarter.
|
|
+ if (code_object->IsCodeStubOrIC()) {
|
|
+ SerializeCodeStub(code_object, how_to_code, where_to_point, skip);
|
|
+ return;
|
|
+ }
|
|
+ code_object->ClearInlineCaches();
|
|
}
|
|
|
|
if (heap_object == source_) {
|
|
@@ -1840,6 +1851,14 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
|
|
return;
|
|
}
|
|
|
|
+ SerializeHeapObject(heap_object, how_to_code, where_to_point, skip);
|
|
+}
|
|
+
|
|
+
|
|
+void CodeSerializer::SerializeHeapObject(HeapObject* heap_object,
|
|
+ HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point,
|
|
+ int skip) {
|
|
if (heap_object->IsScript()) {
|
|
// The wrapper cache uses a Foreign object to point to a global handle.
|
|
// However, the object visitor expects foreign objects to point to external
|
|
@@ -1851,6 +1870,13 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
|
|
sink_->Put(kSkip, "SkipFromSerializeObject");
|
|
sink_->PutInt(skip, "SkipDistanceFromSerializeObject");
|
|
}
|
|
+
|
|
+ if (FLAG_trace_code_serializer) {
|
|
+ PrintF("Encoding heap object: ");
|
|
+ heap_object->ShortPrint();
|
|
+ PrintF("\n");
|
|
+ }
|
|
+
|
|
// Object has not yet been serialized. Serialize it here.
|
|
ObjectSerializer serializer(this, heap_object, sink_, how_to_code,
|
|
where_to_point);
|
|
@@ -1870,11 +1896,62 @@ void CodeSerializer::SerializeBuiltin(Code* builtin, HowToCode how_to_code,
|
|
int builtin_index = builtin->builtin_index();
|
|
DCHECK_LT(builtin_index, Builtins::builtin_count);
|
|
DCHECK_LE(0, builtin_index);
|
|
+
|
|
+ if (FLAG_trace_code_serializer) {
|
|
+ PrintF("Encoding builtin: %s\n",
|
|
+ isolate()->builtins()->name(builtin_index));
|
|
+ }
|
|
+
|
|
sink_->Put(kBuiltin + how_to_code + where_to_point, "Builtin");
|
|
sink_->PutInt(builtin_index, "builtin_index");
|
|
}
|
|
|
|
|
|
+void CodeSerializer::SerializeCodeStub(Code* code, HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point, int skip) {
|
|
+ DCHECK((how_to_code == kPlain && where_to_point == kStartOfObject) ||
|
|
+ (how_to_code == kPlain && where_to_point == kInnerPointer) ||
|
|
+ (how_to_code == kFromCode && where_to_point == kInnerPointer));
|
|
+ uint32_t stub_key = code->stub_key();
|
|
+
|
|
+ if (CodeStub::MajorKeyFromKey(stub_key) == CodeStub::NoCacheKey()) {
|
|
+ if (FLAG_trace_code_serializer) {
|
|
+ PrintF("Encoding uncacheable code stub as heap object\n");
|
|
+ }
|
|
+ SerializeHeapObject(code, how_to_code, where_to_point, skip);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (skip != 0) {
|
|
+ sink_->Put(kSkip, "SkipFromSerializeCodeStub");
|
|
+ sink_->PutInt(skip, "SkipDistanceFromSerializeCodeStub");
|
|
+ }
|
|
+
|
|
+ int index = AddCodeStubKey(stub_key) + kCodeStubsBaseIndex;
|
|
+
|
|
+ if (FLAG_trace_code_serializer) {
|
|
+ PrintF("Encoding code stub %s as %d\n",
|
|
+ CodeStub::MajorName(CodeStub::MajorKeyFromKey(stub_key), false),
|
|
+ index);
|
|
+ }
|
|
+
|
|
+ sink_->Put(kAttachedReference + how_to_code + where_to_point, "CodeStub");
|
|
+ sink_->PutInt(index, "CodeStub key");
|
|
+}
|
|
+
|
|
+
|
|
+int CodeSerializer::AddCodeStubKey(uint32_t stub_key) {
|
|
+ // TODO(yangguo) Maybe we need a hash table for a faster lookup than O(n^2).
|
|
+ int index = 0;
|
|
+ while (index < stub_keys_.length()) {
|
|
+ if (stub_keys_[index] == stub_key) return index;
|
|
+ index++;
|
|
+ }
|
|
+ stub_keys_.Add(stub_key);
|
|
+ return index;
|
|
+}
|
|
+
|
|
+
|
|
void CodeSerializer::SerializeSourceObject(HowToCode how_to_code,
|
|
WhereToPoint where_to_point,
|
|
int skip) {
|
|
@@ -1883,6 +1960,10 @@ void CodeSerializer::SerializeSourceObject(HowToCode how_to_code,
|
|
sink_->PutInt(skip, "SkipDistanceFromSerializeSourceObject");
|
|
}
|
|
|
|
+ if (FLAG_trace_code_serializer) {
|
|
+ PrintF("Encoding source object\n");
|
|
+ }
|
|
+
|
|
DCHECK(how_to_code == kPlain && where_to_point == kStartOfObject);
|
|
sink_->Put(kAttachedReference + how_to_code + where_to_point, "Source");
|
|
sink_->PutInt(kSourceObjectIndex, "kSourceObjectIndex");
|
|
@@ -1894,22 +1975,36 @@ Handle<SharedFunctionInfo> CodeSerializer::Deserialize(Isolate* isolate,
|
|
Handle<String> source) {
|
|
base::ElapsedTimer timer;
|
|
if (FLAG_profile_deserialization) timer.Start();
|
|
- SerializedCodeData scd(data, *source);
|
|
- SnapshotByteSource payload(scd.Payload(), scd.PayloadLength());
|
|
- Deserializer deserializer(&payload);
|
|
- STATIC_ASSERT(NEW_SPACE == 0);
|
|
- for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
|
|
- deserializer.set_reservation(i, scd.GetReservation(i));
|
|
- }
|
|
-
|
|
- // Prepare and register list of attached objects.
|
|
- Vector<Object*> attached_objects = Vector<Object*>::New(1);
|
|
- attached_objects[kSourceObjectIndex] = *source;
|
|
- deserializer.SetAttachedObjects(&attached_objects);
|
|
|
|
Object* root;
|
|
- deserializer.DeserializePartial(isolate, &root);
|
|
- deserializer.FlushICacheForNewCodeObjects();
|
|
+
|
|
+ {
|
|
+ HandleScope scope(isolate);
|
|
+
|
|
+ SerializedCodeData scd(data, *source);
|
|
+ SnapshotByteSource payload(scd.Payload(), scd.PayloadLength());
|
|
+ Deserializer deserializer(&payload);
|
|
+ STATIC_ASSERT(NEW_SPACE == 0);
|
|
+ for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
|
|
+ deserializer.set_reservation(i, scd.GetReservation(i));
|
|
+ }
|
|
+
|
|
+ // Prepare and register list of attached objects.
|
|
+ Vector<const uint32_t> code_stub_keys = scd.CodeStubKeys();
|
|
+ Vector<Handle<Object> > attached_objects = Vector<Handle<Object> >::New(
|
|
+ code_stub_keys.length() + kCodeStubsBaseIndex);
|
|
+ attached_objects[kSourceObjectIndex] = source;
|
|
+ for (int i = 0; i < code_stub_keys.length(); i++) {
|
|
+ attached_objects[i + kCodeStubsBaseIndex] =
|
|
+ CodeStub::GetCode(isolate, code_stub_keys[i]).ToHandleChecked();
|
|
+ }
|
|
+ deserializer.SetAttachedObjects(&attached_objects);
|
|
+
|
|
+ // Deserialize.
|
|
+ deserializer.DeserializePartial(isolate, &root);
|
|
+ deserializer.FlushICacheForNewCodeObjects();
|
|
+ }
|
|
+
|
|
if (FLAG_profile_deserialization) {
|
|
double ms = timer.Elapsed().InMillisecondsF();
|
|
int length = data->length();
|
|
@@ -1922,18 +2017,35 @@ Handle<SharedFunctionInfo> CodeSerializer::Deserialize(Isolate* isolate,
|
|
SerializedCodeData::SerializedCodeData(List<byte>* payload, CodeSerializer* cs)
|
|
: owns_script_data_(true) {
|
|
DisallowHeapAllocation no_gc;
|
|
- int data_length = payload->length() + kHeaderEntries * kIntSize;
|
|
+ List<uint32_t>* stub_keys = cs->stub_keys();
|
|
+
|
|
+ // Calculate sizes.
|
|
+ int num_stub_keys = stub_keys->length();
|
|
+ int stub_keys_size = stub_keys->length() * kInt32Size;
|
|
+ int data_length = kHeaderSize + stub_keys_size + payload->length();
|
|
+
|
|
+ // Allocate backing store and create result data.
|
|
byte* data = NewArray<byte>(data_length);
|
|
DCHECK(IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment));
|
|
- CopyBytes(data + kHeaderEntries * kIntSize, payload->begin(),
|
|
- static_cast<size_t>(payload->length()));
|
|
script_data_ = new ScriptData(data, data_length);
|
|
script_data_->AcquireDataOwnership();
|
|
+
|
|
+ // Set header values.
|
|
SetHeaderValue(kCheckSumOffset, CheckSum(cs->source()));
|
|
+ SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
|
|
+ SetHeaderValue(kPayloadLengthOffset, payload->length());
|
|
STATIC_ASSERT(NEW_SPACE == 0);
|
|
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
|
|
SetHeaderValue(kReservationsOffset + i, cs->CurrentAllocationAddress(i));
|
|
}
|
|
+
|
|
+ // Copy code stub keys.
|
|
+ CopyBytes(data + kHeaderSize, reinterpret_cast<byte*>(stub_keys->begin()),
|
|
+ stub_keys_size);
|
|
+
|
|
+ // Copy serialized data.
|
|
+ CopyBytes(data + kHeaderSize + stub_keys_size, payload->begin(),
|
|
+ static_cast<size_t>(payload->length()));
|
|
}
|
|
|
|
|
|
diff --git a/src/serialize.h b/src/serialize.h
|
|
index 066d75f..71b274b 100644
|
|
--- node/deps/v8/src/serialize.h
|
|
+++ node/deps/v8/src/serialize.h
|
|
@@ -257,7 +257,7 @@ class Deserializer: public SerializerDeserializer {
|
|
|
|
// Serialized user code reference certain objects that are provided in a list
|
|
// By calling this method, we assume that we are deserializing user code.
|
|
- void SetAttachedObjects(Vector<Object*>* attached_objects) {
|
|
+ void SetAttachedObjects(Vector<Handle<Object> >* attached_objects) {
|
|
attached_objects_ = attached_objects;
|
|
}
|
|
|
|
@@ -308,7 +308,7 @@ class Deserializer: public SerializerDeserializer {
|
|
Isolate* isolate_;
|
|
|
|
// Objects from the attached object descriptions in the serialized user code.
|
|
- Vector<Object*>* attached_objects_;
|
|
+ Vector<Handle<Object> >* attached_objects_;
|
|
|
|
SnapshotByteSource* source_;
|
|
// This is the address of the next object that will be allocated in each
|
|
@@ -459,12 +459,10 @@ class Serializer : public SerializerDeserializer {
|
|
HowToCode how_to_code,
|
|
WhereToPoint where_to_point,
|
|
int skip) = 0;
|
|
- void SerializeReferenceToPreviousObject(
|
|
- int space,
|
|
- int address,
|
|
- HowToCode how_to_code,
|
|
- WhereToPoint where_to_point,
|
|
- int skip);
|
|
+ void SerializeReferenceToPreviousObject(HeapObject* heap_object,
|
|
+ HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point,
|
|
+ int skip);
|
|
void InitializeAllocators();
|
|
// This will return the space for an object.
|
|
static int SpaceOfObject(HeapObject* object);
|
|
@@ -594,20 +592,29 @@ class CodeSerializer : public Serializer {
|
|
Handle<String> source);
|
|
|
|
static const int kSourceObjectIndex = 0;
|
|
+ static const int kCodeStubsBaseIndex = 1;
|
|
|
|
String* source() {
|
|
DCHECK(!AllowHeapAllocation::IsAllowed());
|
|
return source_;
|
|
}
|
|
|
|
+ List<uint32_t>* stub_keys() { return &stub_keys_; }
|
|
+
|
|
private:
|
|
void SerializeBuiltin(Code* builtin, HowToCode how_to_code,
|
|
WhereToPoint where_to_point, int skip);
|
|
+ void SerializeCodeStub(Code* code, HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point, int skip);
|
|
void SerializeSourceObject(HowToCode how_to_code, WhereToPoint where_to_point,
|
|
int skip);
|
|
+ void SerializeHeapObject(HeapObject* heap_object, HowToCode how_to_code,
|
|
+ WhereToPoint where_to_point, int skip);
|
|
+ int AddCodeStubKey(uint32_t stub_key);
|
|
|
|
DisallowHeapAllocation no_gc_;
|
|
String* source_;
|
|
+ List<uint32_t> stub_keys_;
|
|
DISALLOW_COPY_AND_ASSIGN(CodeSerializer);
|
|
};
|
|
|
|
@@ -638,12 +645,22 @@ class SerializedCodeData {
|
|
return result;
|
|
}
|
|
|
|
+ Vector<const uint32_t> CodeStubKeys() const {
|
|
+ return Vector<const uint32_t>(
|
|
+ reinterpret_cast<const uint32_t*>(script_data_->data() + kHeaderSize),
|
|
+ GetHeaderValue(kNumCodeStubKeysOffset));
|
|
+ }
|
|
+
|
|
const byte* Payload() const {
|
|
- return script_data_->data() + kHeaderEntries * kIntSize;
|
|
+ int code_stubs_size = GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size;
|
|
+ return script_data_->data() + kHeaderSize + code_stubs_size;
|
|
}
|
|
|
|
int PayloadLength() const {
|
|
- return script_data_->length() - kHeaderEntries * kIntSize;
|
|
+ int payload_length = GetHeaderValue(kPayloadLengthOffset);
|
|
+ DCHECK_EQ(script_data_->data() + script_data_->length(),
|
|
+ Payload() + payload_length);
|
|
+ return payload_length;
|
|
}
|
|
|
|
int GetReservation(int space) const {
|
|
@@ -666,10 +683,21 @@ class SerializedCodeData {
|
|
|
|
// The data header consists of int-sized entries:
|
|
// [0] version hash
|
|
- // [1..7] reservation sizes for spaces from NEW_SPACE to PROPERTY_CELL_SPACE.
|
|
+ // [1] number of code stub keys
|
|
+ // [2] payload length
|
|
+ // [3..9] reservation sizes for spaces from NEW_SPACE to PROPERTY_CELL_SPACE.
|
|
static const int kCheckSumOffset = 0;
|
|
- static const int kReservationsOffset = 1;
|
|
- static const int kHeaderEntries = 8;
|
|
+ static const int kNumCodeStubKeysOffset = 1;
|
|
+ static const int kPayloadLengthOffset = 2;
|
|
+ static const int kReservationsOffset = 3;
|
|
+
|
|
+ static const int kNumSpaces = PROPERTY_CELL_SPACE - NEW_SPACE + 1;
|
|
+ static const int kHeaderEntries = kReservationsOffset + kNumSpaces;
|
|
+ static const int kHeaderSize = kHeaderEntries * kIntSize;
|
|
+
|
|
+ // Following the header, we store, in sequential order
|
|
+ // - code stub keys
|
|
+ // - serialization payload
|
|
|
|
ScriptData* script_data_;
|
|
bool owns_script_data_;
|