• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // The common functionality when building with or without snapshots.
6 
7 #include "src/snapshot/snapshot.h"
8 
9 #include "src/base/platform/platform.h"
10 #include "src/common/assert-scope.h"
11 #include "src/execution/isolate-inl.h"
12 #include "src/heap/safepoint.h"
13 #include "src/init/bootstrapper.h"
14 #include "src/logging/runtime-call-stats-scope.h"
15 #include "src/objects/code-kind.h"
16 #include "src/objects/js-regexp-inl.h"
17 #include "src/snapshot/context-deserializer.h"
18 #include "src/snapshot/context-serializer.h"
19 #include "src/snapshot/read-only-deserializer.h"
20 #include "src/snapshot/read-only-serializer.h"
21 #include "src/snapshot/shared-heap-deserializer.h"
22 #include "src/snapshot/shared-heap-serializer.h"
23 #include "src/snapshot/snapshot-utils.h"
24 #include "src/snapshot/startup-deserializer.h"
25 #include "src/snapshot/startup-serializer.h"
26 #include "src/utils/memcopy.h"
27 #include "src/utils/version.h"
28 
29 #ifdef V8_SNAPSHOT_COMPRESSION
30 #include "src/snapshot/snapshot-compression.h"
31 #endif
32 
33 namespace v8 {
34 namespace internal {
35 
36 namespace {
37 
38 class SnapshotImpl : public AllStatic {
39  public:
40   static v8::StartupData CreateSnapshotBlob(
41       const SnapshotData* startup_snapshot_in,
42       const SnapshotData* read_only_snapshot_in,
43       const SnapshotData* shared_heap_snapshot_in,
44       const std::vector<SnapshotData*>& context_snapshots_in,
45       bool can_be_rehashed);
46 
47   static uint32_t ExtractNumContexts(const v8::StartupData* data);
48   static uint32_t ExtractContextOffset(const v8::StartupData* data,
49                                        uint32_t index);
50   static base::Vector<const byte> ExtractStartupData(
51       const v8::StartupData* data);
52   static base::Vector<const byte> ExtractReadOnlyData(
53       const v8::StartupData* data);
54   static base::Vector<const byte> ExtractSharedHeapData(
55       const v8::StartupData* data);
56   static base::Vector<const byte> ExtractContextData(
57       const v8::StartupData* data, uint32_t index);
58 
GetHeaderValue(const v8::StartupData * data,uint32_t offset)59   static uint32_t GetHeaderValue(const v8::StartupData* data, uint32_t offset) {
60     return base::ReadLittleEndianValue<uint32_t>(
61         reinterpret_cast<Address>(data->data) + offset);
62   }
SetHeaderValue(char * data,uint32_t offset,uint32_t value)63   static void SetHeaderValue(char* data, uint32_t offset, uint32_t value) {
64     base::WriteLittleEndianValue(reinterpret_cast<Address>(data) + offset,
65                                  value);
66   }
67 
68   static void CheckVersion(const v8::StartupData* data);
69 
70   // Snapshot blob layout:
71   // [0] number of contexts N
72   // [1] rehashability
73   // [2] checksum
74   // [3] (64 bytes) version string
75   // [4] offset to readonly
76   // [5] offset to shared heap
77   // [6] offset to context 0
78   // [7] offset to context 1
79   // ...
80   // ... offset to context N - 1
81   // ... startup snapshot data
82   // ... read-only snapshot data
83   // ... shared heap snapshot data
84   // ... context 0 snapshot data
85   // ... context 1 snapshot data
86 
87   static const uint32_t kNumberOfContextsOffset = 0;
88   // TODO(yangguo): generalize rehashing, and remove this flag.
89   static const uint32_t kRehashabilityOffset =
90       kNumberOfContextsOffset + kUInt32Size;
91   static const uint32_t kChecksumOffset = kRehashabilityOffset + kUInt32Size;
92   static const uint32_t kVersionStringOffset = kChecksumOffset + kUInt32Size;
93   static const uint32_t kVersionStringLength = 64;
94   static const uint32_t kReadOnlyOffsetOffset =
95       kVersionStringOffset + kVersionStringLength;
96   static const uint32_t kSharedHeapOffsetOffset =
97       kReadOnlyOffsetOffset + kUInt32Size;
98   static const uint32_t kFirstContextOffsetOffset =
99       kSharedHeapOffsetOffset + kUInt32Size;
100 
ChecksummedContent(const v8::StartupData * data)101   static base::Vector<const byte> ChecksummedContent(
102       const v8::StartupData* data) {
103     STATIC_ASSERT(kVersionStringOffset == kChecksumOffset + kUInt32Size);
104     const uint32_t kChecksumStart = kVersionStringOffset;
105     return base::Vector<const byte>(
106         reinterpret_cast<const byte*>(data->data + kChecksumStart),
107         data->raw_size - kChecksumStart);
108   }
109 
StartupSnapshotOffset(int num_contexts)110   static uint32_t StartupSnapshotOffset(int num_contexts) {
111     return POINTER_SIZE_ALIGN(kFirstContextOffsetOffset +
112                               num_contexts * kInt32Size);
113   }
114 
ContextSnapshotOffsetOffset(int index)115   static uint32_t ContextSnapshotOffsetOffset(int index) {
116     return kFirstContextOffsetOffset + index * kInt32Size;
117   }
118 };
119 
120 }  // namespace
121 
MaybeDecompress(Isolate * isolate,const base::Vector<const byte> & snapshot_data)122 SnapshotData MaybeDecompress(Isolate* isolate,
123                              const base::Vector<const byte>& snapshot_data) {
124 #ifdef V8_SNAPSHOT_COMPRESSION
125   TRACE_EVENT0("v8", "V8.SnapshotDecompress");
126   RCS_SCOPE(isolate, RuntimeCallCounterId::kSnapshotDecompress);
127   return SnapshotCompression::Decompress(snapshot_data);
128 #else
129   return SnapshotData(snapshot_data);
130 #endif
131 }
132 
133 #ifdef DEBUG
SnapshotIsValid(const v8::StartupData * snapshot_blob)134 bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) {
135   return SnapshotImpl::ExtractNumContexts(snapshot_blob) > 0;
136 }
137 #endif  // DEBUG
138 
HasContextSnapshot(Isolate * isolate,size_t index)139 bool Snapshot::HasContextSnapshot(Isolate* isolate, size_t index) {
140   // Do not use snapshots if the isolate is used to create snapshots.
141   const v8::StartupData* blob = isolate->snapshot_blob();
142   if (blob == nullptr) return false;
143   if (blob->data == nullptr) return false;
144   size_t num_contexts =
145       static_cast<size_t>(SnapshotImpl::ExtractNumContexts(blob));
146   return index < num_contexts;
147 }
148 
VersionIsValid(const v8::StartupData * data)149 bool Snapshot::VersionIsValid(const v8::StartupData* data) {
150   char version[SnapshotImpl::kVersionStringLength];
151   memset(version, 0, SnapshotImpl::kVersionStringLength);
152   CHECK_LT(
153       SnapshotImpl::kVersionStringOffset + SnapshotImpl::kVersionStringLength,
154       static_cast<uint32_t>(data->raw_size));
155   Version::GetString(
156       base::Vector<char>(version, SnapshotImpl::kVersionStringLength));
157   return strncmp(version, data->data + SnapshotImpl::kVersionStringOffset,
158                  SnapshotImpl::kVersionStringLength) == 0;
159 }
160 
Initialize(Isolate * isolate)161 bool Snapshot::Initialize(Isolate* isolate) {
162   if (!isolate->snapshot_available()) return false;
163   TRACE_EVENT0("v8", "V8.DeserializeIsolate");
164   RCS_SCOPE(isolate, RuntimeCallCounterId::kDeserializeIsolate);
165   base::ElapsedTimer timer;
166   if (FLAG_profile_deserialization) timer.Start();
167 
168   const v8::StartupData* blob = isolate->snapshot_blob();
169   SnapshotImpl::CheckVersion(blob);
170   if (FLAG_verify_snapshot_checksum) CHECK(VerifyChecksum(blob));
171   base::Vector<const byte> startup_data =
172       SnapshotImpl::ExtractStartupData(blob);
173   base::Vector<const byte> read_only_data =
174       SnapshotImpl::ExtractReadOnlyData(blob);
175   base::Vector<const byte> shared_heap_data =
176       SnapshotImpl::ExtractSharedHeapData(blob);
177 
178   SnapshotData startup_snapshot_data(MaybeDecompress(isolate, startup_data));
179   SnapshotData read_only_snapshot_data(
180       MaybeDecompress(isolate, read_only_data));
181   SnapshotData shared_heap_snapshot_data(
182       MaybeDecompress(isolate, shared_heap_data));
183 
184   bool success = isolate->InitWithSnapshot(
185       &startup_snapshot_data, &read_only_snapshot_data,
186       &shared_heap_snapshot_data, ExtractRehashability(blob));
187   if (FLAG_profile_deserialization) {
188     double ms = timer.Elapsed().InMillisecondsF();
189     int bytes = startup_data.length();
190     PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
191   }
192   return success;
193 }
194 
NewContextFromSnapshot(Isolate * isolate,Handle<JSGlobalProxy> global_proxy,size_t context_index,v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer)195 MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
196     Isolate* isolate, Handle<JSGlobalProxy> global_proxy, size_t context_index,
197     v8::DeserializeEmbedderFieldsCallback embedder_fields_deserializer) {
198   if (!isolate->snapshot_available()) return Handle<Context>();
199   TRACE_EVENT0("v8", "V8.DeserializeContext");
200   RCS_SCOPE(isolate, RuntimeCallCounterId::kDeserializeContext);
201   base::ElapsedTimer timer;
202   if (FLAG_profile_deserialization) timer.Start();
203 
204   const v8::StartupData* blob = isolate->snapshot_blob();
205   bool can_rehash = ExtractRehashability(blob);
206   base::Vector<const byte> context_data = SnapshotImpl::ExtractContextData(
207       blob, static_cast<uint32_t>(context_index));
208   SnapshotData snapshot_data(MaybeDecompress(isolate, context_data));
209 
210   MaybeHandle<Context> maybe_result = ContextDeserializer::DeserializeContext(
211       isolate, &snapshot_data, can_rehash, global_proxy,
212       embedder_fields_deserializer);
213 
214   Handle<Context> result;
215   if (!maybe_result.ToHandle(&result)) return MaybeHandle<Context>();
216 
217   if (FLAG_profile_deserialization) {
218     double ms = timer.Elapsed().InMillisecondsF();
219     int bytes = context_data.length();
220     PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
221            context_index, bytes, ms);
222   }
223   return result;
224 }
225 
226 // static
ClearReconstructableDataForSerialization(Isolate * isolate,bool clear_recompilable_data)227 void Snapshot::ClearReconstructableDataForSerialization(
228     Isolate* isolate, bool clear_recompilable_data) {
229   // Clear SFIs and JSRegExps.
230   PtrComprCageBase cage_base(isolate);
231 
232   if (clear_recompilable_data) {
233     HandleScope scope(isolate);
234     std::vector<i::Handle<i::SharedFunctionInfo>> sfis_to_clear;
235     {
236       i::HeapObjectIterator it(isolate->heap());
237       for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
238         if (o.IsSharedFunctionInfo(cage_base)) {
239           i::SharedFunctionInfo shared = i::SharedFunctionInfo::cast(o);
240           if (shared.script(cage_base).IsScript(cage_base) &&
241               Script::cast(shared.script(cage_base)).type() ==
242                   Script::TYPE_EXTENSION) {
243             continue;  // Don't clear extensions, they cannot be recompiled.
244           }
245           if (shared.CanDiscardCompiled()) {
246             sfis_to_clear.emplace_back(shared, isolate);
247           }
248         } else if (o.IsJSRegExp(cage_base)) {
249           i::JSRegExp regexp = i::JSRegExp::cast(o);
250           if (regexp.HasCompiledCode()) {
251             regexp.DiscardCompiledCodeForSerialization();
252           }
253         }
254       }
255     }
256 
257     // Must happen after heap iteration since SFI::DiscardCompiled may allocate.
258     for (i::Handle<i::SharedFunctionInfo> shared : sfis_to_clear) {
259       if (shared->CanDiscardCompiled()) {
260         i::SharedFunctionInfo::DiscardCompiled(isolate, shared);
261       }
262     }
263   }
264 
265   // Clear JSFunctions.
266 
267   i::HeapObjectIterator it(isolate->heap());
268   for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
269     if (!o.IsJSFunction(cage_base)) continue;
270 
271     i::JSFunction fun = i::JSFunction::cast(o);
272     fun.CompleteInobjectSlackTrackingIfActive();
273 
274     i::SharedFunctionInfo shared = fun.shared();
275     if (shared.script(cage_base).IsScript(cage_base) &&
276         Script::cast(shared.script(cage_base)).type() ==
277             Script::TYPE_EXTENSION) {
278       continue;  // Don't clear extensions, they cannot be recompiled.
279     }
280 
281     // Also, clear out feedback vectors and recompilable code.
282     if (fun.CanDiscardCompiled()) {
283       fun.set_code(*BUILTIN_CODE(isolate, CompileLazy));
284     }
285     if (!fun.raw_feedback_cell(cage_base).value(cage_base).IsUndefined()) {
286       fun.raw_feedback_cell(cage_base).set_value(
287           i::ReadOnlyRoots(isolate).undefined_value());
288     }
289 #ifdef DEBUG
290     if (clear_recompilable_data) {
291 #if V8_ENABLE_WEBASSEMBLY
292       DCHECK(fun.shared().HasWasmExportedFunctionData() ||
293              fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
294              fun.shared().HasUncompiledDataWithoutPreparseData());
295 #else
296       DCHECK(fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
297              fun.shared().HasUncompiledDataWithoutPreparseData());
298 #endif  // V8_ENABLE_WEBASSEMBLY
299     }
300 #endif  // DEBUG
301   }
302 }
303 
304 // static
SerializeDeserializeAndVerifyForTesting(Isolate * isolate,Handle<Context> default_context)305 void Snapshot::SerializeDeserializeAndVerifyForTesting(
306     Isolate* isolate, Handle<Context> default_context) {
307   StartupData serialized_data;
308   std::unique_ptr<const char[]> auto_delete_serialized_data;
309 
310   isolate->heap()->CollectAllAvailableGarbage(
311       i::GarbageCollectionReason::kSnapshotCreator);
312 
313   // Test serialization.
314   {
315     GlobalSafepointScope global_safepoint(isolate);
316     DisallowGarbageCollection no_gc;
317 
318     Snapshot::SerializerFlags flags(
319         Snapshot::kAllowUnknownExternalReferencesForTesting |
320         Snapshot::kAllowActiveIsolateForTesting |
321         ((isolate->shared_isolate() || ReadOnlyHeap::IsReadOnlySpaceShared())
322              ? Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting
323              : 0));
324     serialized_data = Snapshot::Create(isolate, *default_context,
325                                        global_safepoint, no_gc, flags);
326     auto_delete_serialized_data.reset(serialized_data.data);
327   }
328 
329   // Test deserialization.
330   Isolate* new_isolate = Isolate::New();
331   {
332     // Set serializer_enabled() to not install extensions and experimental
333     // natives on the new isolate.
334     // TODO(v8:10416): This should be a separate setting on the isolate.
335     new_isolate->enable_serializer();
336     new_isolate->Enter();
337     new_isolate->set_snapshot_blob(&serialized_data);
338     new_isolate->set_array_buffer_allocator(
339         v8::ArrayBuffer::Allocator::NewDefaultAllocator());
340     if (Isolate* shared_isolate = isolate->shared_isolate()) {
341       new_isolate->set_shared_isolate(shared_isolate);
342     }
343     CHECK(Snapshot::Initialize(new_isolate));
344 
345     HandleScope scope(new_isolate);
346     Handle<Context> new_native_context =
347         new_isolate->bootstrapper()->CreateEnvironmentForTesting();
348     CHECK(new_native_context->IsNativeContext());
349 
350 #ifdef VERIFY_HEAP
351     if (FLAG_verify_heap) new_isolate->heap()->Verify();
352 #endif  // VERIFY_HEAP
353   }
354   new_isolate->Exit();
355   Isolate::Delete(new_isolate);
356 }
357 
358 // static
359 constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags;
360 
361 // static
Create(Isolate * isolate,std::vector<Context> * contexts,const std::vector<SerializeInternalFieldsCallback> & embedder_fields_serializers,const GlobalSafepointScope & global_safepoint,const DisallowGarbageCollection & no_gc,SerializerFlags flags)362 v8::StartupData Snapshot::Create(
363     Isolate* isolate, std::vector<Context>* contexts,
364     const std::vector<SerializeInternalFieldsCallback>&
365         embedder_fields_serializers,
366     const GlobalSafepointScope& global_safepoint,
367     const DisallowGarbageCollection& no_gc, SerializerFlags flags) {
368   TRACE_EVENT0("v8", "V8.SnapshotCreate");
369   DCHECK_EQ(contexts->size(), embedder_fields_serializers.size());
370   DCHECK_GT(contexts->size(), 0);
371   HandleScope scope(isolate);
372 
373   // The GlobalSafepointScope ensures we are in a safepoint scope so that the
374   // string table is safe to iterate. Unlike mksnapshot, embedders may have
375   // background threads running.
376 
377   ReadOnlySerializer read_only_serializer(isolate, flags);
378   read_only_serializer.SerializeReadOnlyRoots();
379 
380   SharedHeapSerializer shared_heap_serializer(isolate, flags,
381                                               &read_only_serializer);
382 
383   StartupSerializer startup_serializer(isolate, flags, &read_only_serializer,
384                                        &shared_heap_serializer);
385   startup_serializer.SerializeStrongReferences(no_gc);
386 
387   // Serialize each context with a new serializer.
388   const int num_contexts = static_cast<int>(contexts->size());
389   std::vector<SnapshotData*> context_snapshots;
390   context_snapshots.reserve(num_contexts);
391 
392   // TODO(v8:6593): generalize rehashing, and remove this flag.
393   bool can_be_rehashed = true;
394 
395   std::vector<int> context_allocation_sizes;
396   for (int i = 0; i < num_contexts; i++) {
397     ContextSerializer context_serializer(isolate, flags, &startup_serializer,
398                                          embedder_fields_serializers[i]);
399     context_serializer.Serialize(&contexts->at(i), no_gc);
400     can_be_rehashed = can_be_rehashed && context_serializer.can_be_rehashed();
401     context_snapshots.push_back(new SnapshotData(&context_serializer));
402     if (FLAG_serialization_statistics) {
403       context_allocation_sizes.push_back(
404           context_serializer.TotalAllocationSize());
405     }
406   }
407 
408   startup_serializer.SerializeWeakReferencesAndDeferred();
409   can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();
410 
411   startup_serializer.CheckNoDirtyFinalizationRegistries();
412 
413   shared_heap_serializer.FinalizeSerialization();
414   can_be_rehashed = can_be_rehashed && shared_heap_serializer.can_be_rehashed();
415 
416   read_only_serializer.FinalizeSerialization();
417   can_be_rehashed = can_be_rehashed && read_only_serializer.can_be_rehashed();
418 
419   if (FLAG_serialization_statistics) {
420     // These prints should match the regexp in test/memory/Memory.json
421     DCHECK_NE(read_only_serializer.TotalAllocationSize(), 0);
422     DCHECK_NE(shared_heap_serializer.TotalAllocationSize(), 0);
423     DCHECK_NE(startup_serializer.TotalAllocationSize(), 0);
424     PrintF("Deserialization will allocate:\n");
425     PrintF("%10d bytes per isolate\n",
426            read_only_serializer.TotalAllocationSize() +
427                startup_serializer.TotalAllocationSize());
428     for (int i = 0; i < num_contexts; i++) {
429       DCHECK_NE(context_allocation_sizes[i], 0);
430       PrintF("%10d bytes per context #%d\n", context_allocation_sizes[i], i);
431     }
432     PrintF("\n");
433   }
434 
435   SnapshotData read_only_snapshot(&read_only_serializer);
436   SnapshotData shared_heap_snapshot(&shared_heap_serializer);
437   SnapshotData startup_snapshot(&startup_serializer);
438   v8::StartupData result = SnapshotImpl::CreateSnapshotBlob(
439       &startup_snapshot, &read_only_snapshot, &shared_heap_snapshot,
440       context_snapshots, can_be_rehashed);
441 
442   for (const SnapshotData* ptr : context_snapshots) delete ptr;
443 
444   CHECK(Snapshot::VerifyChecksum(&result));
445   return result;
446 }
447 
448 // static
Create(Isolate * isolate,Context default_context,const GlobalSafepointScope & global_safepoint,const DisallowGarbageCollection & no_gc,SerializerFlags flags)449 v8::StartupData Snapshot::Create(Isolate* isolate, Context default_context,
450                                  const GlobalSafepointScope& global_safepoint,
451                                  const DisallowGarbageCollection& no_gc,
452                                  SerializerFlags flags) {
453   std::vector<Context> contexts{default_context};
454   std::vector<SerializeInternalFieldsCallback> callbacks{{}};
455   return Snapshot::Create(isolate, &contexts, callbacks, global_safepoint,
456                           no_gc, flags);
457 }
458 
CreateSnapshotBlob(const SnapshotData * startup_snapshot_in,const SnapshotData * read_only_snapshot_in,const SnapshotData * shared_heap_snapshot_in,const std::vector<SnapshotData * > & context_snapshots_in,bool can_be_rehashed)459 v8::StartupData SnapshotImpl::CreateSnapshotBlob(
460     const SnapshotData* startup_snapshot_in,
461     const SnapshotData* read_only_snapshot_in,
462     const SnapshotData* shared_heap_snapshot_in,
463     const std::vector<SnapshotData*>& context_snapshots_in,
464     bool can_be_rehashed) {
465   TRACE_EVENT0("v8", "V8.SnapshotCompress");
466   // Have these separate from snapshot_in for compression, since we need to
467   // access the compressed data as well as the uncompressed reservations.
468   const SnapshotData* startup_snapshot;
469   const SnapshotData* read_only_snapshot;
470   const SnapshotData* shared_heap_snapshot;
471   const std::vector<SnapshotData*>* context_snapshots;
472 #ifdef V8_SNAPSHOT_COMPRESSION
473   SnapshotData startup_compressed(
474       SnapshotCompression::Compress(startup_snapshot_in));
475   SnapshotData read_only_compressed(
476       SnapshotCompression::Compress(read_only_snapshot_in));
477   SnapshotData shared_heap_compressed(
478       SnapshotCompression::Compress(shared_heap_snapshot_in));
479   startup_snapshot = &startup_compressed;
480   read_only_snapshot = &read_only_compressed;
481   shared_heap_snapshot = &shared_heap_compressed;
482   std::vector<SnapshotData> context_snapshots_compressed;
483   context_snapshots_compressed.reserve(context_snapshots_in.size());
484   std::vector<SnapshotData*> context_snapshots_compressed_ptrs;
485   for (unsigned int i = 0; i < context_snapshots_in.size(); ++i) {
486     context_snapshots_compressed.push_back(
487         SnapshotCompression::Compress(context_snapshots_in[i]));
488     context_snapshots_compressed_ptrs.push_back(
489         &context_snapshots_compressed[i]);
490   }
491   context_snapshots = &context_snapshots_compressed_ptrs;
492 #else
493   startup_snapshot = startup_snapshot_in;
494   read_only_snapshot = read_only_snapshot_in;
495   shared_heap_snapshot = shared_heap_snapshot_in;
496   context_snapshots = &context_snapshots_in;
497 #endif
498 
499   uint32_t num_contexts = static_cast<uint32_t>(context_snapshots->size());
500   uint32_t startup_snapshot_offset =
501       SnapshotImpl::StartupSnapshotOffset(num_contexts);
502   uint32_t total_length = startup_snapshot_offset;
503   total_length += static_cast<uint32_t>(startup_snapshot->RawData().length());
504   total_length += static_cast<uint32_t>(read_only_snapshot->RawData().length());
505   total_length +=
506       static_cast<uint32_t>(shared_heap_snapshot->RawData().length());
507   for (const auto context_snapshot : *context_snapshots) {
508     total_length += static_cast<uint32_t>(context_snapshot->RawData().length());
509   }
510 
511   char* data = new char[total_length];
512   // Zero out pre-payload data. Part of that is only used for padding.
513   memset(data, 0, SnapshotImpl::StartupSnapshotOffset(num_contexts));
514 
515   SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kNumberOfContextsOffset,
516                                num_contexts);
517   SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kRehashabilityOffset,
518                                can_be_rehashed ? 1 : 0);
519 
520   // Write version string into snapshot data.
521   memset(data + SnapshotImpl::kVersionStringOffset, 0,
522          SnapshotImpl::kVersionStringLength);
523   Version::GetString(
524       base::Vector<char>(data + SnapshotImpl::kVersionStringOffset,
525                          SnapshotImpl::kVersionStringLength));
526 
527   // Startup snapshot (isolate-specific data).
528   uint32_t payload_offset = startup_snapshot_offset;
529   uint32_t payload_length =
530       static_cast<uint32_t>(startup_snapshot->RawData().length());
531   CopyBytes(data + payload_offset,
532             reinterpret_cast<const char*>(startup_snapshot->RawData().begin()),
533             payload_length);
534   if (FLAG_serialization_statistics) {
535     PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
536            payload_length);
537   }
538   payload_offset += payload_length;
539 
540   // Read-only.
541   SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kReadOnlyOffsetOffset,
542                                payload_offset);
543   payload_length = read_only_snapshot->RawData().length();
544   CopyBytes(
545       data + payload_offset,
546       reinterpret_cast<const char*>(read_only_snapshot->RawData().begin()),
547       payload_length);
548   if (FLAG_serialization_statistics) {
549     PrintF("%10d bytes for read-only\n", payload_length);
550   }
551   payload_offset += payload_length;
552 
553   // Shared heap.
554   SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kSharedHeapOffsetOffset,
555                                payload_offset);
556   payload_length = shared_heap_snapshot->RawData().length();
557   CopyBytes(
558       data + payload_offset,
559       reinterpret_cast<const char*>(shared_heap_snapshot->RawData().begin()),
560       payload_length);
561   if (FLAG_serialization_statistics) {
562     PrintF("%10d bytes for shared heap\n", payload_length);
563   }
564   payload_offset += payload_length;
565 
566   // Context snapshots (context-specific data).
567   for (uint32_t i = 0; i < num_contexts; i++) {
568     SnapshotImpl::SetHeaderValue(
569         data, SnapshotImpl::ContextSnapshotOffsetOffset(i), payload_offset);
570     SnapshotData* context_snapshot = (*context_snapshots)[i];
571     payload_length = context_snapshot->RawData().length();
572     CopyBytes(
573         data + payload_offset,
574         reinterpret_cast<const char*>(context_snapshot->RawData().begin()),
575         payload_length);
576     if (FLAG_serialization_statistics) {
577       PrintF("%10d bytes for context #%d\n", payload_length, i);
578     }
579     payload_offset += payload_length;
580   }
581   if (FLAG_serialization_statistics) PrintF("\n");
582 
583   DCHECK_EQ(total_length, payload_offset);
584   v8::StartupData result = {data, static_cast<int>(total_length)};
585 
586   SnapshotImpl::SetHeaderValue(
587       data, SnapshotImpl::kChecksumOffset,
588       Checksum(SnapshotImpl::ChecksummedContent(&result)));
589 
590   return result;
591 }
592 
ExtractNumContexts(const v8::StartupData * data)593 uint32_t SnapshotImpl::ExtractNumContexts(const v8::StartupData* data) {
594   CHECK_LT(kNumberOfContextsOffset, data->raw_size);
595   uint32_t num_contexts = GetHeaderValue(data, kNumberOfContextsOffset);
596   return num_contexts;
597 }
598 
VerifyChecksum(const v8::StartupData * data)599 bool Snapshot::VerifyChecksum(const v8::StartupData* data) {
600   base::ElapsedTimer timer;
601   if (FLAG_profile_deserialization) timer.Start();
602   uint32_t expected =
603       SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kChecksumOffset);
604   uint32_t result = Checksum(SnapshotImpl::ChecksummedContent(data));
605   if (FLAG_profile_deserialization) {
606     double ms = timer.Elapsed().InMillisecondsF();
607     PrintF("[Verifying snapshot checksum took %0.3f ms]\n", ms);
608   }
609   return result == expected;
610 }
611 
ExtractContextOffset(const v8::StartupData * data,uint32_t index)612 uint32_t SnapshotImpl::ExtractContextOffset(const v8::StartupData* data,
613                                             uint32_t index) {
614   // Extract the offset of the context at a given index from the StartupData,
615   // and check that it is within bounds.
616   uint32_t context_offset =
617       GetHeaderValue(data, ContextSnapshotOffsetOffset(index));
618   CHECK_LT(context_offset, static_cast<uint32_t>(data->raw_size));
619   return context_offset;
620 }
621 
ExtractRehashability(const v8::StartupData * data)622 bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
623   CHECK_LT(SnapshotImpl::kRehashabilityOffset,
624            static_cast<uint32_t>(data->raw_size));
625   uint32_t rehashability =
626       SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kRehashabilityOffset);
627   CHECK_IMPLIES(rehashability != 0, rehashability == 1);
628   return rehashability != 0;
629 }
630 
631 namespace {
ExtractData(const v8::StartupData * snapshot,uint32_t start_offset,uint32_t end_offset)632 base::Vector<const byte> ExtractData(const v8::StartupData* snapshot,
633                                      uint32_t start_offset,
634                                      uint32_t end_offset) {
635   CHECK_LT(start_offset, end_offset);
636   CHECK_LT(end_offset, snapshot->raw_size);
637   uint32_t length = end_offset - start_offset;
638   const byte* data =
639       reinterpret_cast<const byte*>(snapshot->data + start_offset);
640   return base::Vector<const byte>(data, length);
641 }
642 }  // namespace
643 
ExtractStartupData(const v8::StartupData * data)644 base::Vector<const byte> SnapshotImpl::ExtractStartupData(
645     const v8::StartupData* data) {
646   DCHECK(Snapshot::SnapshotIsValid(data));
647 
648   uint32_t num_contexts = ExtractNumContexts(data);
649   return ExtractData(data, StartupSnapshotOffset(num_contexts),
650                      GetHeaderValue(data, kReadOnlyOffsetOffset));
651 }
652 
ExtractReadOnlyData(const v8::StartupData * data)653 base::Vector<const byte> SnapshotImpl::ExtractReadOnlyData(
654     const v8::StartupData* data) {
655   DCHECK(Snapshot::SnapshotIsValid(data));
656 
657   return ExtractData(data, GetHeaderValue(data, kReadOnlyOffsetOffset),
658                      GetHeaderValue(data, kSharedHeapOffsetOffset));
659 }
660 
ExtractSharedHeapData(const v8::StartupData * data)661 base::Vector<const byte> SnapshotImpl::ExtractSharedHeapData(
662     const v8::StartupData* data) {
663   DCHECK(Snapshot::SnapshotIsValid(data));
664 
665   return ExtractData(data, GetHeaderValue(data, kSharedHeapOffsetOffset),
666                      GetHeaderValue(data, ContextSnapshotOffsetOffset(0)));
667 }
668 
ExtractContextData(const v8::StartupData * data,uint32_t index)669 base::Vector<const byte> SnapshotImpl::ExtractContextData(
670     const v8::StartupData* data, uint32_t index) {
671   uint32_t num_contexts = ExtractNumContexts(data);
672   CHECK_LT(index, num_contexts);
673 
674   uint32_t context_offset = ExtractContextOffset(data, index);
675   uint32_t next_context_offset;
676   if (index == num_contexts - 1) {
677     next_context_offset = data->raw_size;
678   } else {
679     next_context_offset = ExtractContextOffset(data, index + 1);
680     CHECK_LT(next_context_offset, data->raw_size);
681   }
682 
683   const byte* context_data =
684       reinterpret_cast<const byte*>(data->data + context_offset);
685   uint32_t context_length = next_context_offset - context_offset;
686   return base::Vector<const byte>(context_data, context_length);
687 }
688 
CheckVersion(const v8::StartupData * data)689 void SnapshotImpl::CheckVersion(const v8::StartupData* data) {
690   if (!Snapshot::VersionIsValid(data)) {
691     char version[kVersionStringLength];
692     memset(version, 0, kVersionStringLength);
693     CHECK_LT(kVersionStringOffset + kVersionStringLength,
694              static_cast<uint32_t>(data->raw_size));
695     Version::GetString(base::Vector<char>(version, kVersionStringLength));
696     FATAL(
697         "Version mismatch between V8 binary and snapshot.\n"
698         "#   V8 binary version: %.*s\n"
699         "#    Snapshot version: %.*s\n"
700         "# The snapshot consists of %d bytes and contains %d context(s).",
701         kVersionStringLength, version, kVersionStringLength,
702         data->data + kVersionStringOffset, data->raw_size,
703         ExtractNumContexts(data));
704   }
705 }
706 
707 namespace {
708 
RunExtraCode(v8::Isolate * isolate,v8::Local<v8::Context> context,const char * utf8_source,const char * name)709 bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context,
710                   const char* utf8_source, const char* name) {
711   v8::Context::Scope context_scope(context);
712   v8::TryCatch try_catch(isolate);
713   v8::Local<v8::String> source_string;
714   if (!v8::String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
715     return false;
716   }
717   v8::Local<v8::String> resource_name =
718       v8::String::NewFromUtf8(isolate, name).ToLocalChecked();
719   v8::ScriptOrigin origin(isolate, resource_name);
720   v8::ScriptCompiler::Source source(source_string, origin);
721   v8::Local<v8::Script> script;
722   if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script))
723     return false;
724   if (script->Run(context).IsEmpty()) return false;
725   CHECK(!try_catch.HasCaught());
726   return true;
727 }
728 
729 }  // namespace
730 
CreateSnapshotDataBlobInternal(v8::SnapshotCreator::FunctionCodeHandling function_code_handling,const char * embedded_source,v8::Isolate * isolate)731 v8::StartupData CreateSnapshotDataBlobInternal(
732     v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
733     const char* embedded_source, v8::Isolate* isolate) {
734   // If no isolate is passed in, create it (and a new context) from scratch.
735   if (isolate == nullptr) isolate = v8::Isolate::Allocate();
736 
737   // Optionally run a script to embed, and serialize to create a snapshot blob.
738   v8::SnapshotCreator snapshot_creator(isolate);
739   {
740     v8::HandleScope scope(isolate);
741     v8::Local<v8::Context> context = v8::Context::New(isolate);
742     if (embedded_source != nullptr &&
743         !RunExtraCode(isolate, context, embedded_source, "<embedded>")) {
744       return {};
745     }
746     snapshot_creator.SetDefaultContext(context);
747   }
748   return snapshot_creator.CreateBlob(function_code_handling);
749 }
750 
WarmUpSnapshotDataBlobInternal(v8::StartupData cold_snapshot_blob,const char * warmup_source)751 v8::StartupData WarmUpSnapshotDataBlobInternal(
752     v8::StartupData cold_snapshot_blob, const char* warmup_source) {
753   CHECK(cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr);
754   CHECK_NOT_NULL(warmup_source);
755 
756   // Use following steps to create a warmed up snapshot blob from a cold one:
757   //  - Create a new isolate from the cold snapshot.
758   //  - Create a new context to run the warmup script. This will trigger
759   //    compilation of executed functions.
760   //  - Create a new context. This context will be unpolluted.
761   //  - Serialize the isolate and the second context into a new snapshot blob.
762   v8::SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
763   v8::Isolate* isolate = snapshot_creator.GetIsolate();
764   {
765     v8::HandleScope scope(isolate);
766     v8::Local<v8::Context> context = v8::Context::New(isolate);
767     if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
768       return {};
769     }
770   }
771   {
772     v8::HandleScope handle_scope(isolate);
773     isolate->ContextDisposedNotification(false);
774     v8::Local<v8::Context> context = v8::Context::New(isolate);
775     snapshot_creator.SetDefaultContext(context);
776   }
777 
778   return snapshot_creator.CreateBlob(
779       v8::SnapshotCreator::FunctionCodeHandling::kKeep);
780 }
781 
782 }  // namespace internal
783 }  // namespace v8
784