snapshot.cc 26.4 KB
Newer Older
1
// Copyright 2006-2008 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4 5 6

// The common functionality when building with or without snapshots.

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 25
#include "src/utils/memcopy.h"
#include "src/utils/version.h"
26

27 28 29 30
#ifdef V8_SNAPSHOT_COMPRESSION
#include "src/snapshot/snapshot-compression.h"
#endif

31 32
namespace v8 {
namespace internal {
33

34 35 36 37
namespace {

class SnapshotImpl : public AllStatic {
 public:
38 39 40 41 42 43
  static v8::StartupData CreateSnapshotBlob(
      const SnapshotData* startup_snapshot_in,
      const SnapshotData* read_only_snapshot_in,
      const std::vector<SnapshotData*>& context_snapshots_in,
      bool can_be_rehashed);

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
  static uint32_t ExtractNumContexts(const v8::StartupData* data);
  static uint32_t ExtractContextOffset(const v8::StartupData* data,
                                       uint32_t index);
  static Vector<const byte> ExtractStartupData(const v8::StartupData* data);
  static Vector<const byte> ExtractReadOnlyData(const v8::StartupData* data);
  static Vector<const byte> ExtractContextData(const v8::StartupData* data,
                                               uint32_t index);

  static uint32_t GetHeaderValue(const v8::StartupData* data, uint32_t offset) {
    return base::ReadLittleEndianValue<uint32_t>(
        reinterpret_cast<Address>(data->data) + offset);
  }
  static void SetHeaderValue(char* data, uint32_t offset, uint32_t value) {
    base::WriteLittleEndianValue(reinterpret_cast<Address>(data) + offset,
                                 value);
  }

  static void CheckVersion(const v8::StartupData* data);

  // Snapshot blob layout:
  // [0] number of contexts N
  // [1] rehashability
  // [2] checksum
  // [3] (128 bytes) version string
  // [4] offset to readonly
  // [5] offset to context 0
  // [6] offset to context 1
  // ...
  // ... offset to context N - 1
  // ... startup snapshot data
  // ... read-only snapshot data
  // ... context 0 snapshot data
  // ... context 1 snapshot data

  static const uint32_t kNumberOfContextsOffset = 0;
  // TODO(yangguo): generalize rehashing, and remove this flag.
  static const uint32_t kRehashabilityOffset =
      kNumberOfContextsOffset + kUInt32Size;
  static const uint32_t kChecksumOffset = kRehashabilityOffset + kUInt32Size;
  static const uint32_t kVersionStringOffset = kChecksumOffset + kUInt32Size;
  static const uint32_t kVersionStringLength = 64;
  static const uint32_t kReadOnlyOffsetOffset =
      kVersionStringOffset + kVersionStringLength;
  static const uint32_t kFirstContextOffsetOffset =
      kReadOnlyOffsetOffset + kUInt32Size;

  static Vector<const byte> ChecksummedContent(const v8::StartupData* data) {
    STATIC_ASSERT(kVersionStringOffset == kChecksumOffset + kUInt32Size);
    const uint32_t kChecksumStart = kVersionStringOffset;
    return Vector<const byte>(
        reinterpret_cast<const byte*>(data->data + kChecksumStart),
        data->raw_size - kChecksumStart);
  }

  static uint32_t StartupSnapshotOffset(int num_contexts) {
    return POINTER_SIZE_ALIGN(kFirstContextOffsetOffset +
                              num_contexts * kInt32Size);
  }

  static uint32_t ContextSnapshotOffsetOffset(int index) {
    return kFirstContextOffsetOffset + index * kInt32Size;
  }
};

}  // namespace

110 111 112 113 114 115 116 117
SnapshotData MaybeDecompress(const Vector<const byte>& snapshot_data) {
#ifdef V8_SNAPSHOT_COMPRESSION
  return SnapshotCompression::Decompress(snapshot_data);
#else
  return SnapshotData(snapshot_data);
#endif
}

118
#ifdef DEBUG
119
bool Snapshot::SnapshotIsValid(const v8::StartupData* snapshot_blob) {
120
  return SnapshotImpl::ExtractNumContexts(snapshot_blob) > 0;
121 122 123
}
#endif  // DEBUG

124
bool Snapshot::HasContextSnapshot(Isolate* isolate, size_t index) {
125
  // Do not use snapshots if the isolate is used to create snapshots.
126 127 128
  const v8::StartupData* blob = isolate->snapshot_blob();
  if (blob == nullptr) return false;
  if (blob->data == nullptr) return false;
129 130
  size_t num_contexts =
      static_cast<size_t>(SnapshotImpl::ExtractNumContexts(blob));
131
  return index < num_contexts;
132 133
}

134 135 136 137 138 139 140 141 142 143 144
bool Snapshot::VersionIsValid(const v8::StartupData* data) {
  char version[SnapshotImpl::kVersionStringLength];
  memset(version, 0, SnapshotImpl::kVersionStringLength);
  CHECK_LT(
      SnapshotImpl::kVersionStringOffset + SnapshotImpl::kVersionStringLength,
      static_cast<uint32_t>(data->raw_size));
  Version::GetString(Vector<char>(version, SnapshotImpl::kVersionStringLength));
  return strncmp(version, data->data + SnapshotImpl::kVersionStringOffset,
                 SnapshotImpl::kVersionStringLength) == 0;
}

145
bool Snapshot::Initialize(Isolate* isolate) {
146
  if (!isolate->snapshot_available()) return false;
147 148
  RuntimeCallTimerScope rcs_timer(isolate,
                                  RuntimeCallCounterId::kDeserializeIsolate);
149 150 151
  base::ElapsedTimer timer;
  if (FLAG_profile_deserialization) timer.Start();

152
  const v8::StartupData* blob = isolate->snapshot_blob();
153
  SnapshotImpl::CheckVersion(blob);
154
  CHECK(VerifyChecksum(blob));
155 156
  Vector<const byte> startup_data = SnapshotImpl::ExtractStartupData(blob);
  Vector<const byte> read_only_data = SnapshotImpl::ExtractReadOnlyData(blob);
157 158 159 160

  SnapshotData startup_snapshot_data(MaybeDecompress(startup_data));
  SnapshotData read_only_snapshot_data(MaybeDecompress(read_only_data));

161 162 163
  bool success = isolate->InitWithSnapshot(&startup_snapshot_data,
                                           &read_only_snapshot_data,
                                           ExtractRehashability(blob));
164 165
  if (FLAG_profile_deserialization) {
    double ms = timer.Elapsed().InMillisecondsF();
166 167
    int bytes = startup_data.length();
    PrintF("[Deserializing isolate (%d bytes) took %0.3f ms]\n", bytes, ms);
168 169
  }
  return success;
170 171
}

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 177
  RuntimeCallTimerScope rcs_timer(isolate,
                                  RuntimeCallCounterId::kDeserializeContext);
178 179
  base::ElapsedTimer timer;
  if (FLAG_profile_deserialization) timer.Start();
180

181
  const v8::StartupData* blob = isolate->snapshot_blob();
182
  bool can_rehash = ExtractRehashability(blob);
183 184
  Vector<const byte> context_data = SnapshotImpl::ExtractContextData(
      blob, static_cast<uint32_t>(context_index));
185
  SnapshotData snapshot_data(MaybeDecompress(context_data));
186

187
  MaybeHandle<Context> maybe_result = ContextDeserializer::DeserializeContext(
188 189 190 191 192 193
      isolate, &snapshot_data, can_rehash, global_proxy,
      embedder_fields_deserializer);

  Handle<Context> result;
  if (!maybe_result.ToHandle(&result)) return MaybeHandle<Context>();

194 195 196
  if (FLAG_profile_deserialization) {
    double ms = timer.Elapsed().InMillisecondsF();
    int bytes = context_data.length();
197 198
    PrintF("[Deserializing context #%zu (%d bytes) took %0.3f ms]\n",
           context_index, bytes, ms);
199
  }
200
  return result;
201 202
}

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
// static
void Snapshot::ClearReconstructableDataForSerialization(
    Isolate* isolate, bool clear_recompilable_data) {
  // Clear SFIs and JSRegExps.

  if (clear_recompilable_data) {
    HandleScope scope(isolate);
    std::vector<i::Handle<i::SharedFunctionInfo>> sfis_to_clear;
    {  // Heap allocation is disallowed within this scope.
      i::HeapObjectIterator it(isolate->heap());
      for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
        if (o.IsSharedFunctionInfo()) {
          i::SharedFunctionInfo shared = i::SharedFunctionInfo::cast(o);
          if (shared.script().IsScript() &&
              Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
            continue;  // Don't clear extensions, they cannot be recompiled.
          }
          if (shared.CanDiscardCompiled()) {
            sfis_to_clear.emplace_back(shared, isolate);
          }
        } else if (o.IsJSRegExp()) {
          i::JSRegExp regexp = i::JSRegExp::cast(o);
          if (regexp.HasCompiledCode()) {
            regexp.DiscardCompiledCodeForSerialization();
          }
        }
      }
    }

    // Must happen after heap iteration since SFI::DiscardCompiled may allocate.
    for (i::Handle<i::SharedFunctionInfo> shared : sfis_to_clear) {
      i::SharedFunctionInfo::DiscardCompiled(isolate, shared);
    }
  }

  // Clear JSFunctions.

  i::HeapObjectIterator it(isolate->heap());
  for (i::HeapObject o = it.Next(); !o.is_null(); o = it.Next()) {
    if (!o.IsJSFunction()) continue;

    i::JSFunction fun = i::JSFunction::cast(o);
    fun.CompleteInobjectSlackTrackingIfActive();

    i::SharedFunctionInfo shared = fun.shared();
    if (shared.script().IsScript() &&
        Script::cast(shared.script()).type() == Script::TYPE_EXTENSION) {
      continue;  // Don't clear extensions, they cannot be recompiled.
    }

253 254
    // Also, clear out feedback vectors and recompilable code.
    if (fun.CanDiscardCompiled()) {
255 256 257
      fun.set_code(*BUILTIN_CODE(isolate, CompileLazy));
    }
    if (!fun.raw_feedback_cell().value().IsUndefined()) {
258 259 260 261 262 263 264 265 266 267 268 269 270
      fun.raw_feedback_cell().set_value(
          i::ReadOnlyRoots(isolate).undefined_value());
    }
#ifdef DEBUG
    if (clear_recompilable_data) {
      DCHECK(fun.shared().HasWasmExportedFunctionData() ||
             fun.shared().HasBuiltinId() || fun.shared().IsApiFunction() ||
             fun.shared().HasUncompiledDataWithoutPreparseData());
    }
#endif  // DEBUG
  }
}

271 272 273 274 275 276 277 278 279 280 281
// static
void Snapshot::SerializeDeserializeAndVerifyForTesting(
    Isolate* isolate, Handle<Context> default_context) {
  StartupData serialized_data;
  std::unique_ptr<const char[]> auto_delete_serialized_data;

  isolate->heap()->CollectAllAvailableGarbage(
      i::GarbageCollectionReason::kSnapshotCreator);

  // Test serialization.
  {
282
    DisallowGarbageCollection no_gc;
283 284 285

    Snapshot::SerializerFlags flags(
        Snapshot::kAllowUnknownExternalReferencesForTesting |
286 287
        Snapshot::kAllowActiveIsolateForTesting);
    serialized_data = Snapshot::Create(isolate, *default_context, no_gc, flags);
288 289 290 291 292 293 294 295 296 297 298 299
    auto_delete_serialized_data.reset(serialized_data.data);
  }

  // Test deserialization.
  Isolate* new_isolate = Isolate::New();
  {
    // Set serializer_enabled() to not install extensions and experimental
    // natives on the new isolate.
    // TODO(v8:10416): This should be a separate setting on the isolate.
    new_isolate->enable_serializer();
    new_isolate->Enter();
    new_isolate->set_snapshot_blob(&serialized_data);
300 301
    new_isolate->set_array_buffer_allocator(
        v8::ArrayBuffer::Allocator::NewDefaultAllocator());
302 303 304 305 306 307 308 309
    CHECK(Snapshot::Initialize(new_isolate));

    HandleScope scope(new_isolate);
    Handle<Context> new_native_context =
        new_isolate->bootstrapper()->CreateEnvironmentForTesting();
    CHECK(new_native_context->IsNativeContext());

#ifdef VERIFY_HEAP
310
    if (FLAG_verify_heap) new_isolate->heap()->Verify();
311 312 313 314 315 316 317 318 319
#endif  // VERIFY_HEAP
  }
  new_isolate->Exit();
  Isolate::Delete(new_isolate);
}

// static
constexpr Snapshot::SerializerFlags Snapshot::kDefaultSerializerFlags;

320 321 322 323 324
// static
v8::StartupData Snapshot::Create(
    Isolate* isolate, std::vector<Context>* contexts,
    const std::vector<SerializeInternalFieldsCallback>&
        embedder_fields_serializers,
325
    const DisallowGarbageCollection& no_gc, SerializerFlags flags) {
326 327
  DCHECK_EQ(contexts->size(), embedder_fields_serializers.size());
  DCHECK_GT(contexts->size(), 0);
328
  HandleScope scope(isolate);
329

330 331 332 333 334 335 336
  // Enter a safepoint so that the heap is safe to iterate.
  // TODO(leszeks): This safepoint's scope could be tightened to just string
  // table iteration, as that iteration relies on there not being any concurrent
  // threads mutating the string table. But, there's currently no harm in
  // holding it for the entire snapshot serialization.
  SafepointScope safepoint(isolate->heap());

337
  ReadOnlySerializer read_only_serializer(isolate, flags);
338 339
  read_only_serializer.SerializeReadOnlyRoots();

340
  StartupSerializer startup_serializer(isolate, flags, &read_only_serializer);
341
  startup_serializer.SerializeStrongReferences(no_gc);
342 343 344 345 346 347 348 349 350

  // Serialize each context with a new serializer.
  const int num_contexts = static_cast<int>(contexts->size());
  std::vector<SnapshotData*> context_snapshots;
  context_snapshots.reserve(num_contexts);

  // TODO(v8:6593): generalize rehashing, and remove this flag.
  bool can_be_rehashed = true;

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 357
    can_be_rehashed = can_be_rehashed && context_serializer.can_be_rehashed();
    context_snapshots.push_back(new SnapshotData(&context_serializer));
358 359 360 361
    if (FLAG_profile_deserialization) {
      context_allocation_sizes.push_back(
          context_serializer.TotalAllocationSize());
    }
362 363 364 365 366 367 368 369 370 371
  }

  startup_serializer.SerializeWeakReferencesAndDeferred();
  can_be_rehashed = can_be_rehashed && startup_serializer.can_be_rehashed();

  startup_serializer.CheckNoDirtyFinalizationRegistries();

  read_only_serializer.FinalizeSerialization();
  can_be_rehashed = can_be_rehashed && read_only_serializer.can_be_rehashed();

372 373 374 375 376 377 378 379 380 381 382
  if (FLAG_profile_deserialization) {
    // These prints should match the regexp in test/memory/Memory.json
    PrintF("Deserialization will allocate:\n");
    PrintF("%10d bytes per isolate\n",
           read_only_serializer.TotalAllocationSize() +
               startup_serializer.TotalAllocationSize());
    for (int i = 0; i < num_contexts; i++) {
      PrintF("%10d bytes per context #%d\n", context_allocation_sizes[i], i);
    }
  }

383 384 385 386 387 388 389 390 391 392 393 394 395 396
  SnapshotData read_only_snapshot(&read_only_serializer);
  SnapshotData startup_snapshot(&startup_serializer);
  v8::StartupData result =
      SnapshotImpl::CreateSnapshotBlob(&startup_snapshot, &read_only_snapshot,
                                       context_snapshots, can_be_rehashed);

  for (const SnapshotData* ptr : context_snapshots) delete ptr;

  CHECK(Snapshot::VerifyChecksum(&result));
  return result;
}

// static
v8::StartupData Snapshot::Create(Isolate* isolate, Context default_context,
397
                                 const DisallowGarbageCollection& no_gc,
398
                                 SerializerFlags flags) {
399 400
  std::vector<Context> contexts{default_context};
  std::vector<SerializeInternalFieldsCallback> callbacks{{}};
401
  return Snapshot::Create(isolate, &contexts, callbacks, no_gc, flags);
402 403 404
}

v8::StartupData SnapshotImpl::CreateSnapshotBlob(
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    const SnapshotData* startup_snapshot_in,
    const SnapshotData* read_only_snapshot_in,
    const std::vector<SnapshotData*>& context_snapshots_in,
    bool can_be_rehashed) {
  // Have these separate from snapshot_in for compression, since we need to
  // access the compressed data as well as the uncompressed reservations.
  const SnapshotData* startup_snapshot;
  const SnapshotData* read_only_snapshot;
  const std::vector<SnapshotData*>* context_snapshots;
#ifdef V8_SNAPSHOT_COMPRESSION
  SnapshotData startup_compressed(
      SnapshotCompression::Compress(startup_snapshot_in));
  SnapshotData read_only_compressed(
      SnapshotCompression::Compress(read_only_snapshot_in));
  startup_snapshot = &startup_compressed;
  read_only_snapshot = &read_only_compressed;
  std::vector<SnapshotData> context_snapshots_compressed;
  context_snapshots_compressed.reserve(context_snapshots_in.size());
  std::vector<SnapshotData*> context_snapshots_compressed_ptrs;
  for (unsigned int i = 0; i < context_snapshots_in.size(); ++i) {
    context_snapshots_compressed.push_back(
        SnapshotCompression::Compress(context_snapshots_in[i]));
    context_snapshots_compressed_ptrs.push_back(
        &context_snapshots_compressed[i]);
  }
  context_snapshots = &context_snapshots_compressed_ptrs;
#else
  startup_snapshot = startup_snapshot_in;
  read_only_snapshot = read_only_snapshot_in;
  context_snapshots = &context_snapshots_in;
#endif

  uint32_t num_contexts = static_cast<uint32_t>(context_snapshots->size());
438 439
  uint32_t startup_snapshot_offset =
      SnapshotImpl::StartupSnapshotOffset(num_contexts);
440 441
  uint32_t total_length = startup_snapshot_offset;
  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 452 453 454
  SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kNumberOfContextsOffset,
                               num_contexts);
  SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kRehashabilityOffset,
                               can_be_rehashed ? 1 : 0);
455

456
  // Write version string into snapshot data.
457 458 459 460
  memset(data + SnapshotImpl::kVersionStringOffset, 0,
         SnapshotImpl::kVersionStringLength);
  Version::GetString(Vector<char>(data + SnapshotImpl::kVersionStringOffset,
                                  SnapshotImpl::kVersionStringLength));
461

462 463
  // Startup snapshot (isolate-specific data).
  uint32_t payload_offset = startup_snapshot_offset;
464 465 466
  uint32_t payload_length =
      static_cast<uint32_t>(startup_snapshot->RawData().length());
  CopyBytes(data + payload_offset,
467
            reinterpret_cast<const char*>(startup_snapshot->RawData().begin()),
468
            payload_length);
469
  if (FLAG_profile_deserialization) {
470 471
    PrintF("Snapshot blob consists of:\n%10d bytes for startup\n",
           payload_length);
472
  }
473
  payload_offset += payload_length;
474

475
  // Read-only.
476 477
  SnapshotImpl::SetHeaderValue(data, SnapshotImpl::kReadOnlyOffsetOffset,
                               payload_offset);
478 479 480
  payload_length = read_only_snapshot->RawData().length();
  CopyBytes(
      data + payload_offset,
481
      reinterpret_cast<const char*>(read_only_snapshot->RawData().begin()),
482 483 484 485 486 487
      payload_length);
  if (FLAG_profile_deserialization) {
    PrintF("%10d bytes for read-only\n", payload_length);
  }
  payload_offset += payload_length;

488
  // Context snapshots (context-specific data).
489
  for (uint32_t i = 0; i < num_contexts; i++) {
490 491
    SnapshotImpl::SetHeaderValue(
        data, SnapshotImpl::ContextSnapshotOffsetOffset(i), payload_offset);
492
    SnapshotData* context_snapshot = (*context_snapshots)[i];
493
    payload_length = context_snapshot->RawData().length();
494 495
    CopyBytes(
        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 502 503
    }
    payload_offset += payload_length;
  }

504
  DCHECK_EQ(total_length, payload_offset);
505 506
  v8::StartupData result = {data, static_cast<int>(total_length)};

507 508 509
  SnapshotImpl::SetHeaderValue(
      data, SnapshotImpl::kChecksumOffset,
      Checksum(SnapshotImpl::ChecksummedContent(&result)));
510

511 512 513
  return result;
}

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 518 519
  return num_contexts;
}

520 521 522
bool Snapshot::VerifyChecksum(const v8::StartupData* data) {
  base::ElapsedTimer timer;
  if (FLAG_profile_deserialization) timer.Start();
523 524 525
  uint32_t expected =
      SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kChecksumOffset);
  uint32_t result = Checksum(SnapshotImpl::ChecksummedContent(data));
526 527 528 529
  if (FLAG_profile_deserialization) {
    double ms = timer.Elapsed().InMillisecondsF();
    PrintF("[Verifying snapshot checksum took %0.3f ms]\n", ms);
  }
530
  return result == expected;
531 532
}

533 534
uint32_t SnapshotImpl::ExtractContextOffset(const v8::StartupData* data,
                                            uint32_t index) {
535 536 537 538 539 540 541 542
  // Extract the offset of the context at a given index from the StartupData,
  // and check that it is within bounds.
  uint32_t context_offset =
      GetHeaderValue(data, ContextSnapshotOffsetOffset(index));
  CHECK_LT(context_offset, static_cast<uint32_t>(data->raw_size));
  return context_offset;
}

543
bool Snapshot::ExtractRehashability(const v8::StartupData* data) {
544 545 546 547
  CHECK_LT(SnapshotImpl::kRehashabilityOffset,
           static_cast<uint32_t>(data->raw_size));
  uint32_t rehashability =
      SnapshotImpl::GetHeaderValue(data, SnapshotImpl::kRehashabilityOffset);
548 549
  CHECK_IMPLIES(rehashability != 0, rehashability == 1);
  return rehashability != 0;
550 551
}

552 553 554 555 556 557 558 559 560 561 562 563
namespace {
Vector<const byte> ExtractData(const v8::StartupData* snapshot,
                               uint32_t start_offset, uint32_t end_offset) {
  CHECK_LT(start_offset, end_offset);
  CHECK_LT(end_offset, snapshot->raw_size);
  uint32_t length = end_offset - start_offset;
  const byte* data =
      reinterpret_cast<const byte*>(snapshot->data + start_offset);
  return Vector<const byte>(data, length);
}
}  // namespace

564 565 566
Vector<const byte> SnapshotImpl::ExtractStartupData(
    const v8::StartupData* data) {
  DCHECK(Snapshot::SnapshotIsValid(data));
567

568
  uint32_t num_contexts = ExtractNumContexts(data);
569 570 571
  return ExtractData(data, StartupSnapshotOffset(num_contexts),
                     GetHeaderValue(data, kReadOnlyOffsetOffset));
}
572

573 574 575
Vector<const byte> SnapshotImpl::ExtractReadOnlyData(
    const v8::StartupData* data) {
  DCHECK(Snapshot::SnapshotIsValid(data));
576

577 578
  return ExtractData(data, GetHeaderValue(data, kReadOnlyOffsetOffset),
                     GetHeaderValue(data, ContextSnapshotOffsetOffset(0)));
579 580
}

581 582
Vector<const byte> SnapshotImpl::ExtractContextData(const v8::StartupData* data,
                                                    uint32_t index) {
583
  uint32_t num_contexts = ExtractNumContexts(data);
584 585
  CHECK_LT(index, num_contexts);

586 587
  uint32_t context_offset = ExtractContextOffset(data, index);
  uint32_t next_context_offset;
588 589 590
  if (index == num_contexts - 1) {
    next_context_offset = data->raw_size;
  } else {
591
    next_context_offset = ExtractContextOffset(data, index + 1);
592 593
    CHECK_LT(next_context_offset, data->raw_size);
  }
594 595 596

  const byte* context_data =
      reinterpret_cast<const byte*>(data->data + context_offset);
597
  uint32_t context_length = next_context_offset - context_offset;
598 599
  return Vector<const byte>(context_data, context_length);
}
600

601
void SnapshotImpl::CheckVersion(const v8::StartupData* data) {
602 603 604 605 606 607
  if (!Snapshot::VersionIsValid(data)) {
    char version[kVersionStringLength];
    memset(version, 0, kVersionStringLength);
    CHECK_LT(kVersionStringOffset + kVersionStringLength,
             static_cast<uint32_t>(data->raw_size));
    Version::GetString(Vector<char>(version, kVersionStringLength));
608 609 610 611 612 613 614 615
    FATAL(
        "Version mismatch between V8 binary and snapshot.\n"
        "#   V8 binary version: %.*s\n"
        "#    Snapshot version: %.*s\n"
        "# The snapshot consists of %d bytes and contains %d context(s).",
        kVersionStringLength, version, kVersionStringLength,
        data->data + kVersionStringOffset, data->raw_size,
        ExtractNumContexts(data));
616 617 618
  }
}

619 620 621 622 623 624 625
namespace {

bool RunExtraCode(v8::Isolate* isolate, v8::Local<v8::Context> context,
                  const char* utf8_source, const char* name) {
  v8::Context::Scope context_scope(context);
  v8::TryCatch try_catch(isolate);
  v8::Local<v8::String> source_string;
626
  if (!v8::String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
627 628 629
    return false;
  }
  v8::Local<v8::String> resource_name =
630
      v8::String::NewFromUtf8(isolate, name).ToLocalChecked();
631
  v8::ScriptOrigin origin(isolate, resource_name);
632 633 634 635 636 637 638 639 640 641 642 643 644
  v8::ScriptCompiler::Source source(source_string, origin);
  v8::Local<v8::Script> script;
  if (!v8::ScriptCompiler::Compile(context, &source).ToLocal(&script))
    return false;
  if (script->Run(context).IsEmpty()) return false;
  CHECK(!try_catch.HasCaught());
  return true;
}

}  // namespace

v8::StartupData CreateSnapshotDataBlobInternal(
    v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
645 646 647 648 649 650
    const char* embedded_source, v8::Isolate* isolate) {
  // If no isolate is passed in, create it (and a new context) from scratch.
  if (isolate == nullptr) isolate = v8::Isolate::Allocate();

  // Optionally run a script to embed, and serialize to create a snapshot blob.
  v8::SnapshotCreator snapshot_creator(isolate);
651
  {
652 653 654 655 656
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    if (embedded_source != nullptr &&
        !RunExtraCode(isolate, context, embedded_source, "<embedded>")) {
      return {};
657
    }
658
    snapshot_creator.SetDefaultContext(context);
659
  }
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
  return snapshot_creator.CreateBlob(function_code_handling);
}

v8::StartupData WarmUpSnapshotDataBlobInternal(
    v8::StartupData cold_snapshot_blob, const char* warmup_source) {
  CHECK(cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr);
  CHECK_NOT_NULL(warmup_source);

  // Use following steps to create a warmed up snapshot blob from a cold one:
  //  - Create a new isolate from the cold snapshot.
  //  - Create a new context to run the warmup script. This will trigger
  //    compilation of executed functions.
  //  - Create a new context. This context will be unpolluted.
  //  - Serialize the isolate and the second context into a new snapshot blob.
  v8::SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
  v8::Isolate* isolate = snapshot_creator.GetIsolate();
  {
    v8::HandleScope scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    if (!RunExtraCode(isolate, context, warmup_source, "<warm-up>")) {
      return {};
    }
  }
  {
    v8::HandleScope handle_scope(isolate);
    isolate->ContextDisposedNotification(false);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    snapshot_creator.SetDefaultContext(context);
  }

  return snapshot_creator.CreateBlob(
      v8::SnapshotCreator::FunctionCodeHandling::kKeep);
692 693
}

694 695
}  // namespace internal
}  // namespace v8