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