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