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
33namespace v8 {
34namespace internal {
35
36namespace {
37
38class 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
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  }
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
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
110  static uint32_t StartupSnapshotOffset(int num_contexts) {
111    return POINTER_SIZE_ALIGN(kFirstContextOffsetOffset +
112                              num_contexts * kInt32Size);
113  }
114
115  static uint32_t ContextSnapshotOffsetOffset(int index) {
116    return kFirstContextOffsetOffset + index * kInt32Size;
117  }
118};
119
120}  // namespace
121
122SnapshotData 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
134bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) {
135  return SnapshotImpl::ExtractNumContexts(snapshot_blob) > 0;
136}
137#endif  // DEBUG
138
139bool 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
149bool 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
161bool 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
195MaybeHandle<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
227void 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
305void 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
359constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags;
360
361// static
362v8::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
449v8::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
459v8::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
593uint32_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
599bool 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
612uint32_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
622bool 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
631namespace {
632base::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
644base::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
653base::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
661base::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
669base::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
689void 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
707namespace {
708
709bool 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
731v8::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
751v8::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