test-serialize.cc 92.3 KB
Newer Older
1
// Copyright 2007-2010 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <signal.h>

30
#include <sys/stat.h>
31

32
#include "src/v8.h"
33

34 35
#include "src/api.h"
#include "src/assembler-inl.h"
36
#include "src/bootstrapper.h"
37
#include "src/compilation-cache.h"
38
#include "src/compiler.h"
39
#include "src/debug/debug.h"
40
#include "src/heap/spaces.h"
41
#include "src/macro-assembler-inl.h"
42
#include "src/objects-inl.h"
43
#include "src/runtime/runtime.h"
44 45
#include "src/snapshot/code-serializer.h"
#include "src/snapshot/deserializer.h"
46
#include "src/snapshot/natives.h"
47
#include "src/snapshot/partial-serializer.h"
48
#include "src/snapshot/snapshot.h"
49
#include "src/snapshot/startup-serializer.h"
50
#include "test/cctest/cctest.h"
51
#include "test/cctest/heap/heap-utils.h"
52
#include "test/cctest/setup-isolate-for-tests.h"
53 54 55

using namespace v8::internal;

56 57 58 59
void DisableAlwaysOpt() {
  // Isolates prepared for serialization do not optimize. The only exception is
  // with the flag --always-opt.
  FLAG_always_opt = false;
60 61 62
}


63 64 65 66 67
// TestIsolate is used for testing isolate serialization.
class TestIsolate : public Isolate {
 public:
  static v8::Isolate* NewInitialized(bool enable_serializer) {
    i::Isolate* isolate = new TestIsolate(enable_serializer);
68
    isolate->setup_delegate_ = new SetupIsolateDelegateForTests();
69 70 71 72 73
    v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    v8::Isolate::Scope isolate_scope(v8_isolate);
    isolate->Init(NULL);
    return v8_isolate;
  }
74 75 76 77 78 79 80 81
  // Wraps v8::Isolate::New, but with a TestIsolate under the hood.
  // Allows flexibility to bootstrap with or without snapshot even when
  // the production Isolate class has one or the other behavior baked in.
  static v8::Isolate* New(const v8::Isolate::CreateParams& params) {
    i::Isolate* isolate = new TestIsolate(false);
    isolate->setup_delegate_ = new SetupIsolateDelegateForTests();
    return v8::IsolateNewImpl(isolate, params);
  }
82 83 84
  explicit TestIsolate(bool enable_serializer) : Isolate(enable_serializer) {
    set_array_buffer_allocator(CcTest::array_buffer_allocator());
  }
85 86 87
  void CreateSetupDelegateForTests() {
    setup_delegate_ = new SetupIsolateDelegateForTests();
  }
88 89
};

90 91 92 93 94
static Vector<const byte> WritePayload(const Vector<const byte>& payload) {
  int length = payload.length();
  byte* blob = NewArray<byte>(length);
  memcpy(blob, payload.begin(), length);
  return Vector<const byte>(const_cast<const byte*>(blob), length);
95
}
96

97
static Vector<const byte> Serialize(v8::Isolate* isolate) {
98 99 100 101
  // We have to create one context.  One reason for this is so that the builtins
  // can be loaded from v8natives.js and their addresses can be processed.  This
  // will clear the pending fixups array, which would otherwise contain GC roots
  // that would confuse the serialization/deserialization process.
102
  v8::Isolate::Scope isolate_scope(isolate);
103 104 105 106
  {
    v8::HandleScope scope(isolate);
    v8::Context::New(isolate);
  }
107

108
  Isolate* internal_isolate = reinterpret_cast<Isolate*>(isolate);
109 110
  internal_isolate->heap()->CollectAllAvailableGarbage(
      i::GarbageCollectionReason::kTesting);
111
  StartupSerializer ser(internal_isolate,
112
                        v8::SnapshotCreator::FunctionCodeHandling::kClear);
113 114
  ser.SerializeStrongReferences();
  ser.SerializeWeakReferencesAndDeferred();
115
  SnapshotData snapshot_data(&ser);
116
  return WritePayload(snapshot_data.RawData());
117 118 119
}


120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
Vector<const uint8_t> ConstructSource(Vector<const uint8_t> head,
                                      Vector<const uint8_t> body,
                                      Vector<const uint8_t> tail, int repeats) {
  int source_length = head.length() + body.length() * repeats + tail.length();
  uint8_t* source = NewArray<uint8_t>(static_cast<size_t>(source_length));
  CopyChars(source, head.start(), head.length());
  for (int i = 0; i < repeats; i++) {
    CopyChars(source + head.length() + i * body.length(), body.start(),
              body.length());
  }
  CopyChars(source + head.length() + repeats * body.length(), tail.start(),
            tail.length());
  return Vector<const uint8_t>(const_cast<const uint8_t*>(source),
                               source_length);
}

136
v8::Isolate* InitializeFromBlob(Vector<const byte> blob) {
137
  v8::Isolate* v8_isolate = NULL;
138
  {
139
    SnapshotData snapshot_data(blob);
140
    Deserializer deserializer(&snapshot_data);
141
    TestIsolate* isolate = new TestIsolate(false);
142 143
    v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
    v8::Isolate::Scope isolate_scope(v8_isolate);
144
    isolate->CreateSetupDelegateForTests();
145
    isolate->Init(&deserializer);
146
  }
147
  return v8_isolate;
148 149
}

150 151
static v8::Isolate* Deserialize(Vector<const byte> blob) {
  v8::Isolate* isolate = InitializeFromBlob(blob);
152 153
  CHECK(isolate);
  return isolate;
154 155 156
}


157 158 159
static void SanityCheck(v8::Isolate* v8_isolate) {
  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
  v8::HandleScope scope(v8_isolate);
160
#ifdef VERIFY_HEAP
161
  isolate->heap()->Verify();
162
#endif
163 164
  CHECK(isolate->global_object()->IsJSObject());
  CHECK(isolate->native_context()->IsContext());
165
  CHECK(isolate->heap()->string_table()->IsStringTable());
166
  isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("Empty"));
167 168
}

169
UNINITIALIZED_TEST(StartupSerializerOnce) {
170
  DisableAlwaysOpt();
171
  v8::Isolate* isolate = TestIsolate::NewInitialized(true);
172 173 174
  Vector<const byte> blob = Serialize(isolate);
  isolate = Deserialize(blob);
  blob.Dispose();
175 176 177
  {
    v8::HandleScope handle_scope(isolate);
    v8::Isolate::Scope isolate_scope(isolate);
178

179 180
    v8::Local<v8::Context> env = v8::Context::New(isolate);
    env->Enter();
181

182
    SanityCheck(isolate);
183
  }
184
  isolate->Dispose();
185 186
}

187
UNINITIALIZED_TEST(StartupSerializerTwice) {
188
  DisableAlwaysOpt();
189
  v8::Isolate* isolate = TestIsolate::NewInitialized(true);
190 191 192 193 194
  Vector<const byte> blob1 = Serialize(isolate);
  Vector<const byte> blob2 = Serialize(isolate);
  blob1.Dispose();
  isolate = Deserialize(blob2);
  blob2.Dispose();
195 196 197
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
198

199 200
    v8::Local<v8::Context> env = v8::Context::New(isolate);
    env->Enter();
201

202
    SanityCheck(isolate);
203
  }
204
  isolate->Dispose();
205 206
}

207
UNINITIALIZED_TEST(StartupSerializerOnceRunScript) {
208
  DisableAlwaysOpt();
209
  v8::Isolate* isolate = TestIsolate::NewInitialized(true);
210 211 212
  Vector<const byte> blob = Serialize(isolate);
  isolate = Deserialize(blob);
  blob.Dispose();
213 214 215
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
216

217

218 219
    v8::Local<v8::Context> env = v8::Context::New(isolate);
    env->Enter();
220

221
    const char* c_source = "\"1234\".length";
222 223 224 225 226
    v8::Local<v8::Script> script = v8_compile(c_source);
    v8::Maybe<int32_t> result = script->Run(isolate->GetCurrentContext())
                                    .ToLocalChecked()
                                    ->Int32Value(isolate->GetCurrentContext());
    CHECK_EQ(4, result.FromJust());
227
  }
228
  isolate->Dispose();
229 230
}

231
UNINITIALIZED_TEST(StartupSerializerTwiceRunScript) {
232
  DisableAlwaysOpt();
233
  v8::Isolate* isolate = TestIsolate::NewInitialized(true);
234 235 236 237 238
  Vector<const byte> blob1 = Serialize(isolate);
  Vector<const byte> blob2 = Serialize(isolate);
  blob1.Dispose();
  isolate = Deserialize(blob2);
  blob2.Dispose();
239 240 241
  {
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
242

243 244
    v8::Local<v8::Context> env = v8::Context::New(isolate);
    env->Enter();
245

246
    const char* c_source = "\"1234\".length";
247 248 249 250 251
    v8::Local<v8::Script> script = v8_compile(c_source);
    v8::Maybe<int32_t> result = script->Run(isolate->GetCurrentContext())
                                    .ToLocalChecked()
                                    ->Int32Value(isolate->GetCurrentContext());
    CHECK_EQ(4, result.FromJust());
252
  }
253
  isolate->Dispose();
254 255
}

256 257
static void PartiallySerializeObject(Vector<const byte>* startup_blob_out,
                                     Vector<const byte>* partial_blob_out) {
258 259 260 261 262
  v8::Isolate* v8_isolate = TestIsolate::NewInitialized(true);
  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
  v8_isolate->Enter();
  {
    Heap* heap = isolate->heap();
263

264 265 266 267 268
    v8::Persistent<v8::Context> env;
    {
      HandleScope scope(isolate);
      env.Reset(v8_isolate, v8::Context::New(v8_isolate));
    }
269
    CHECK(!env.IsEmpty());
270 271 272 273
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
    }
274 275 276

    heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
    heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
277

278 279 280
    Object* raw_foo;
    {
      v8::HandleScope handle_scope(v8_isolate);
281
      v8::Local<v8::String> foo = v8_str("foo");
282
      CHECK(!foo.IsEmpty());
283 284
      raw_foo = *(v8::Utils::OpenHandle(*foo));
    }
285

286 287 288 289 290
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
    }
    env.Reset();
291

292
    StartupSerializer startup_serializer(
293
        isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear);
294
    startup_serializer.SerializeStrongReferences();
295

296 297
    PartialSerializer partial_serializer(isolate, &startup_serializer,
                                         v8::SerializeInternalFieldsCallback());
298
    partial_serializer.Serialize(&raw_foo, false);
299

300
    startup_serializer.SerializeWeakReferencesAndDeferred();
301

302 303
    SnapshotData startup_snapshot(&startup_serializer);
    SnapshotData partial_snapshot(&partial_serializer);
304

305 306
    *partial_blob_out = WritePayload(partial_snapshot.RawData());
    *startup_blob_out = WritePayload(startup_snapshot.RawData());
307
  }
308 309
  v8_isolate->Exit();
  v8_isolate->Dispose();
310 311
}

312
UNINITIALIZED_TEST(PartialSerializerObject) {
313
  DisableAlwaysOpt();
314 315 316
  Vector<const byte> startup_blob;
  Vector<const byte> partial_blob;
  PartiallySerializeObject(&startup_blob, &partial_blob);
317

318 319
  v8::Isolate* v8_isolate = InitializeFromBlob(startup_blob);
  startup_blob.Dispose();
320 321 322
  CHECK(v8_isolate);
  {
    v8::Isolate::Scope isolate_scope(v8_isolate);
323

324 325 326 327 328 329
    Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
    HandleScope handle_scope(isolate);
    Handle<Object> root;
    // Intentionally empty handle. The deserializer should not come across
    // any references to the global proxy in this test.
    Handle<JSGlobalProxy> global_proxy = Handle<JSGlobalProxy>::null();
330
    {
331
      SnapshotData snapshot_data(partial_blob);
332
      Deserializer deserializer(&snapshot_data);
333 334 335
      root = deserializer
                 .DeserializePartial(isolate, global_proxy,
                                     v8::DeserializeInternalFieldsCallback())
336
                 .ToHandleChecked();
337 338
      CHECK(root->IsString());
    }
339

340 341
    Handle<Object> root2;
    {
342
      SnapshotData snapshot_data(partial_blob);
343
      Deserializer deserializer(&snapshot_data);
344 345 346
      root2 = deserializer
                  .DeserializePartial(isolate, global_proxy,
                                      v8::DeserializeInternalFieldsCallback())
347
                  .ToHandleChecked();
348 349
      CHECK(root2->IsString());
      CHECK(root.is_identical_to(root2));
350
    }
351
    partial_blob.Dispose();
352
  }
353
  v8_isolate->Dispose();
354
}
355

356 357
static void PartiallySerializeContext(Vector<const byte>* startup_blob_out,
                                      Vector<const byte>* partial_blob_out) {
358 359 360 361 362
  v8::Isolate* v8_isolate = TestIsolate::NewInitialized(true);
  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
  Heap* heap = isolate->heap();
  {
    v8::Isolate::Scope isolate_scope(v8_isolate);
363

364 365 366 367 368
    v8::Persistent<v8::Context> env;
    {
      HandleScope scope(isolate);
      env.Reset(v8_isolate, v8::Context::New(v8_isolate));
    }
369
    CHECK(!env.IsEmpty());
370 371 372 373
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
    }
374

375 376
    // If we don't do this then we end up with a stray root pointing at the
    // context even after we have disposed of env.
377
    heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
378

379 380 381 382
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
    }
383

384
    i::Object* raw_context = *v8::Utils::OpenPersistent(env);
385

386
    env.Reset();
387

388
    SnapshotByteSink startup_sink;
389
    StartupSerializer startup_serializer(
390
        isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear);
391
    startup_serializer.SerializeStrongReferences();
392

393
    SnapshotByteSink partial_sink;
394 395
    PartialSerializer partial_serializer(isolate, &startup_serializer,
                                         v8::SerializeInternalFieldsCallback());
396
    partial_serializer.Serialize(&raw_context, false);
397
    startup_serializer.SerializeWeakReferencesAndDeferred();
398

399 400
    SnapshotData startup_snapshot(&startup_serializer);
    SnapshotData partial_snapshot(&partial_serializer);
401

402 403
    *partial_blob_out = WritePayload(partial_snapshot.RawData());
    *startup_blob_out = WritePayload(startup_snapshot.RawData());
404
  }
405
  v8_isolate->Dispose();
406 407
}

408
UNINITIALIZED_TEST(PartialSerializerContext) {
409
  DisableAlwaysOpt();
410 411 412
  Vector<const byte> startup_blob;
  Vector<const byte> partial_blob;
  PartiallySerializeContext(&startup_blob, &partial_blob);
413

414
  v8::Isolate* v8_isolate = InitializeFromBlob(startup_blob);
415
  CHECK(v8_isolate);
416
  startup_blob.Dispose();
417 418
  {
    v8::Isolate::Scope isolate_scope(v8_isolate);
419

420 421 422 423
    Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
    HandleScope handle_scope(isolate);
    Handle<Object> root;
    Handle<JSGlobalProxy> global_proxy =
424
        isolate->factory()->NewUninitializedJSGlobalProxy(
425
            JSGlobalProxy::SizeWithEmbedderFields(0));
426
    {
427
      SnapshotData snapshot_data(partial_blob);
428
      Deserializer deserializer(&snapshot_data);
429 430 431
      root = deserializer
                 .DeserializePartial(isolate, global_proxy,
                                     v8::DeserializeInternalFieldsCallback())
432
                 .ToHandleChecked();
433 434 435
      CHECK(root->IsContext());
      CHECK(Handle<Context>::cast(root)->global_proxy() == *global_proxy);
    }
436

437 438
    Handle<Object> root2;
    {
439
      SnapshotData snapshot_data(partial_blob);
440
      Deserializer deserializer(&snapshot_data);
441 442 443
      root2 = deserializer
                  .DeserializePartial(isolate, global_proxy,
                                      v8::DeserializeInternalFieldsCallback())
444
                  .ToHandleChecked();
445 446
      CHECK(root2->IsContext());
      CHECK(!root.is_identical_to(root2));
447
    }
448
    partial_blob.Dispose();
449
  }
450
  v8_isolate->Dispose();
451 452
}

453 454 455
static void PartiallySerializeCustomContext(
    Vector<const byte>* startup_blob_out,
    Vector<const byte>* partial_blob_out) {
456 457 458 459
  v8::Isolate* v8_isolate = TestIsolate::NewInitialized(true);
  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
  {
    v8::Isolate::Scope isolate_scope(v8_isolate);
460

461 462 463 464 465
    v8::Persistent<v8::Context> env;
    {
      HandleScope scope(isolate);
      env.Reset(v8_isolate, v8::Context::New(v8_isolate));
    }
466
    CHECK(!env.IsEmpty());
467 468 469 470 471 472 473 474 475 476
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
      // After execution, e's function context refers to the global object.
      CompileRun(
          "var e;"
          "(function() {"
          "  e = function(s) { return eval (s); }"
          "})();"
          "var o = this;"
477 478
          "var r = Math.random();"
          "var c = Math.sin(0) + Math.cos(0);"
479 480 481 482 483 484 485
          "var f = (function(a, b) { return a + b; }).bind(1, 2, 3);"
          "var s = parseInt('12345');");

      Vector<const uint8_t> source = ConstructSource(
          STATIC_CHAR_VECTOR("function g() { return [,"),
          STATIC_CHAR_VECTOR("1,"),
          STATIC_CHAR_VECTOR("];} a = g(); b = g(); b.push(1);"), 100000);
486 487
      v8::MaybeLocal<v8::String> source_str = v8::String::NewFromOneByte(
          v8_isolate, source.start(), v8::NewStringType::kNormal,
488
          source.length());
489
      CompileRun(source_str.ToLocalChecked());
490 491 492 493
      source.Dispose();
    }
    // If we don't do this then we end up with a stray root pointing at the
    // context even after we have disposed of env.
494 495
    isolate->heap()->CollectAllAvailableGarbage(
        i::GarbageCollectionReason::kTesting);
496

497 498 499 500
    {
      v8::HandleScope handle_scope(v8_isolate);
      v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
    }
501

502
    i::Object* raw_context = *v8::Utils::OpenPersistent(env);
503

504
    env.Reset();
505

506
    SnapshotByteSink startup_sink;
507
    StartupSerializer startup_serializer(
508
        isolate, v8::SnapshotCreator::FunctionCodeHandling::kClear);
509
    startup_serializer.SerializeStrongReferences();
510

511
    SnapshotByteSink partial_sink;
512 513
    PartialSerializer partial_serializer(isolate, &startup_serializer,
                                         v8::SerializeInternalFieldsCallback());
514
    partial_serializer.Serialize(&raw_context, false);
515
    startup_serializer.SerializeWeakReferencesAndDeferred();
516

517 518
    SnapshotData startup_snapshot(&startup_serializer);
    SnapshotData partial_snapshot(&partial_serializer);
519

520 521
    *partial_blob_out = WritePayload(partial_snapshot.RawData());
    *startup_blob_out = WritePayload(startup_snapshot.RawData());
522
  }
523
  v8_isolate->Dispose();
524 525
}

526
UNINITIALIZED_TEST(PartialSerializerCustomContext) {
527
  DisableAlwaysOpt();
528 529 530
  Vector<const byte> startup_blob;
  Vector<const byte> partial_blob;
  PartiallySerializeCustomContext(&startup_blob, &partial_blob);
531

532
  v8::Isolate* v8_isolate = InitializeFromBlob(startup_blob);
533
  CHECK(v8_isolate);
534
  startup_blob.Dispose();
535 536
  {
    v8::Isolate::Scope isolate_scope(v8_isolate);
537

538 539 540 541
    Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
    HandleScope handle_scope(isolate);
    Handle<Object> root;
    Handle<JSGlobalProxy> global_proxy =
542
        isolate->factory()->NewUninitializedJSGlobalProxy(
543
            JSGlobalProxy::SizeWithEmbedderFields(0));
544
    {
545
      SnapshotData snapshot_data(partial_blob);
546
      Deserializer deserializer(&snapshot_data);
547 548 549
      root = deserializer
                 .DeserializePartial(isolate, global_proxy,
                                     v8::DeserializeInternalFieldsCallback())
550
                 .ToHandleChecked();
551 552
      CHECK(root->IsContext());
      Handle<Context> context = Handle<Context>::cast(root);
553 554 555 556 557 558 559

      // Add context to the weak native context list
      context->set(Context::NEXT_CONTEXT_LINK,
                   isolate->heap()->native_contexts_list(),
                   UPDATE_WEAK_WRITE_BARRIER);
      isolate->heap()->set_native_contexts_list(*context);

560 561 562
      CHECK(context->global_proxy() == *global_proxy);
      Handle<String> o = isolate->factory()->NewStringFromAsciiChecked("o");
      Handle<JSObject> global_object(context->global_object(), isolate);
563
      Handle<Object> property = JSReceiver::GetDataProperty(global_object, o);
564 565
      CHECK(property.is_identical_to(global_proxy));

566
      v8::Local<v8::Context> v8_context = v8::Utils::ToLocal(context);
567
      v8::Context::Scope context_scope(v8_context);
568 569 570 571
      double r = CompileRun("r")
                     ->ToNumber(v8_isolate->GetCurrentContext())
                     .ToLocalChecked()
                     ->Value();
572 573 574 575 576 577 578 579 580 581 582 583
      CHECK(0.0 <= r && r < 1.0);
      // Math.random still works.
      double random = CompileRun("Math.random()")
                          ->ToNumber(v8_isolate->GetCurrentContext())
                          .ToLocalChecked()
                          ->Value();
      CHECK(0.0 <= random && random < 1.0);
      double c = CompileRun("c")
                     ->ToNumber(v8_isolate->GetCurrentContext())
                     .ToLocalChecked()
                     ->Value();
      CHECK_EQ(1, c);
584 585 586 587 588
      int f = CompileRun("f()")
                  ->ToNumber(v8_isolate->GetCurrentContext())
                  .ToLocalChecked()
                  ->Int32Value(v8_isolate->GetCurrentContext())
                  .FromJust();
589
      CHECK_EQ(5, f);
590 591 592 593 594
      f = CompileRun("e('f()')")
              ->ToNumber(v8_isolate->GetCurrentContext())
              .ToLocalChecked()
              ->Int32Value(v8_isolate->GetCurrentContext())
              .FromJust();
595
      CHECK_EQ(5, f);
596 597 598 599 600 601 602 603 604 605
      v8::Local<v8::String> s = CompileRun("s")
                                    ->ToString(v8_isolate->GetCurrentContext())
                                    .ToLocalChecked();
      CHECK(s->Equals(v8_isolate->GetCurrentContext(), v8_str("12345"))
                .FromJust());
      int a = CompileRun("a.length")
                  ->ToNumber(v8_isolate->GetCurrentContext())
                  .ToLocalChecked()
                  ->Int32Value(v8_isolate->GetCurrentContext())
                  .FromJust();
606
      CHECK_EQ(100001, a);
607 608 609 610 611
      int b = CompileRun("b.length")
                  ->ToNumber(v8_isolate->GetCurrentContext())
                  .ToLocalChecked()
                  ->Int32Value(v8_isolate->GetCurrentContext())
                  .FromJust();
612
      CHECK_EQ(100002, b);
613
    }
614
    partial_blob.Dispose();
615 616 617 618
  }
  v8_isolate->Dispose();
}

619
TEST(CustomSnapshotDataBlob1) {
620
  DisableAlwaysOpt();
621 622 623 624 625 626
  const char* source1 = "function f() { return 42; }";

  v8::StartupData data1 = v8::V8::CreateSnapshotDataBlob(source1);

  v8::Isolate::CreateParams params1;
  params1.snapshot_blob = &data1;
627 628
  params1.array_buffer_allocator = CcTest::array_buffer_allocator();

629 630
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate1 = TestIsolate::New(params1);
631 632 633 634 635 636
  {
    v8::Isolate::Scope i_scope(isolate1);
    v8::HandleScope h_scope(isolate1);
    v8::Local<v8::Context> context = v8::Context::New(isolate1);
    delete[] data1.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
637 638 639
    v8::Maybe<int32_t> result =
        CompileRun("f()")->Int32Value(isolate1->GetCurrentContext());
    CHECK_EQ(42, result.FromJust());
640 641 642
    CHECK(CompileRun("this.g")->IsUndefined());
  }
  isolate1->Dispose();
643 644 645
}

TEST(CustomSnapshotDataBlob2) {
646
  DisableAlwaysOpt();
647 648 649 650 651 652
  const char* source2 =
      "function f() { return g() * 2; }"
      "function g() { return 43; }"
      "/./.test('a')";

  v8::StartupData data2 = v8::V8::CreateSnapshotDataBlob(source2);
653 654 655

  v8::Isolate::CreateParams params2;
  params2.snapshot_blob = &data2;
656
  params2.array_buffer_allocator = CcTest::array_buffer_allocator();
657 658
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate2 = TestIsolate::New(params2);
659 660 661 662 663 664
  {
    v8::Isolate::Scope i_scope(isolate2);
    v8::HandleScope h_scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
    delete[] data2.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
665 666 667 668 669
    v8::Maybe<int32_t> result =
        CompileRun("f()")->Int32Value(isolate2->GetCurrentContext());
    CHECK_EQ(86, result.FromJust());
    result = CompileRun("g()")->Int32Value(isolate2->GetCurrentContext());
    CHECK_EQ(43, result.FromJust());
670
  }
671
  isolate2->Dispose();
672 673
}

674 675 676 677 678
static void SerializationFunctionTemplate(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  args.GetReturnValue().Set(args[0]);
}

679
TEST(CustomSnapshotDataBlobOutdatedContextWithOverflow) {
680
  DisableAlwaysOpt();
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
  const char* source1 =
      "var o = {};"
      "(function() {"
      "  function f1(x) { return f2(x) instanceof Array; }"
      "  function f2(x) { return foo.bar(x); }"
      "  o.a = f2.bind(null);"
      "  o.b = 1;"
      "  o.c = 2;"
      "  o.d = 3;"
      "  o.e = 4;"
      "})();\n";

  const char* source2 = "o.a(42)";

  v8::StartupData data = v8::V8::CreateSnapshotDataBlob(source1);

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &data;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();

701 702
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
  {
    v8::Isolate::Scope i_scope(isolate);
    v8::HandleScope h_scope(isolate);

    v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
    v8::Local<v8::ObjectTemplate> property = v8::ObjectTemplate::New(isolate);
    v8::Local<v8::FunctionTemplate> function =
        v8::FunctionTemplate::New(isolate, SerializationFunctionTemplate);
    property->Set(isolate, "bar", function);
    global->Set(isolate, "foo", property);

    v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
    delete[] data.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
    v8::Local<v8::Value> result = CompileRun(source2);
718 719 720
    v8::Maybe<bool> compare = v8_str("42")->Equals(
        v8::Isolate::GetCurrent()->GetCurrentContext(), result);
    CHECK(compare.FromJust());
721 722 723 724
  }
  isolate->Dispose();
}

725
TEST(CustomSnapshotDataBlobWithLocker) {
726
  DisableAlwaysOpt();
727 728 729
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate0 = v8::Isolate::New(create_params);
730 731 732 733 734 735
  {
    v8::Locker locker(isolate0);
    v8::Isolate::Scope i_scope(isolate0);
    v8::HandleScope h_scope(isolate0);
    v8::Local<v8::Context> context = v8::Context::New(isolate0);
    v8::Context::Scope c_scope(context);
736 737 738
    v8::Maybe<int32_t> result =
        CompileRun("Math.cos(0)")->Int32Value(isolate0->GetCurrentContext());
    CHECK_EQ(1, result.FromJust());
739 740 741 742 743 744 745 746 747
  }
  isolate0->Dispose();

  const char* source1 = "function f() { return 42; }";

  v8::StartupData data1 = v8::V8::CreateSnapshotDataBlob(source1);

  v8::Isolate::CreateParams params1;
  params1.snapshot_blob = &data1;
748
  params1.array_buffer_allocator = CcTest::array_buffer_allocator();
749 750
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate1 = TestIsolate::New(params1);
751 752 753 754 755 756 757
  {
    v8::Locker locker(isolate1);
    v8::Isolate::Scope i_scope(isolate1);
    v8::HandleScope h_scope(isolate1);
    v8::Local<v8::Context> context = v8::Context::New(isolate1);
    delete[] data1.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
758 759
    v8::Maybe<int32_t> result = CompileRun("f()")->Int32Value(context);
    CHECK_EQ(42, result.FromJust());
760 761 762 763
  }
  isolate1->Dispose();
}

764
TEST(CustomSnapshotDataBlobStackOverflow) {
765
  DisableAlwaysOpt();
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
  const char* source =
      "var a = [0];"
      "var b = a;"
      "for (var i = 0; i < 10000; i++) {"
      "  var c = [i];"
      "  b.push(c);"
      "  b.push(c);"
      "  b = c;"
      "}";

  v8::StartupData data = v8::V8::CreateSnapshotDataBlob(source);

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &data;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();

782 783
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
784 785 786 787 788 789 790 791 792 793 794 795 796
  {
    v8::Isolate::Scope i_scope(isolate);
    v8::HandleScope h_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    delete[] data.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
    const char* test =
        "var sum = 0;"
        "while (a) {"
        "  sum += a[0];"
        "  a = a[1];"
        "}"
        "sum";
797 798 799
    v8::Maybe<int32_t> result =
        CompileRun(test)->Int32Value(isolate->GetCurrentContext());
    CHECK_EQ(9999 * 5000, result.FromJust());
800 801 802 803
  }
  isolate->Dispose();
}

804 805 806 807 808 809 810 811
bool IsCompiled(const char* name) {
  return i::Handle<i::JSFunction>::cast(
             v8::Utils::OpenHandle(*CompileRun(name)))
      ->shared()
      ->is_compiled();
}

TEST(SnapshotDataBlobWithWarmup) {
812
  DisableAlwaysOpt();
813
  const char* warmup = "Math.abs(1); Math.random = 1;";
814 815 816 817 818 819 820 821 822

  v8::StartupData cold = v8::V8::CreateSnapshotDataBlob();
  v8::StartupData warm = v8::V8::WarmUpSnapshotDataBlob(cold, warmup);
  delete[] cold.data;

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &warm;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();

823 824
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
825 826 827 828 829 830 831 832
  {
    v8::Isolate::Scope i_scope(isolate);
    v8::HandleScope h_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    delete[] warm.data;
    v8::Context::Scope c_scope(context);
    // Running the warmup script has effect on whether functions are
    // pre-compiled, but does not pollute the context.
833
    CHECK(IsCompiled("Math.abs"));
834
    CHECK(!IsCompiled("String.raw"));
835
    CHECK(CompileRun("Math.random")->IsFunction());
836 837 838 839 840
  }
  isolate->Dispose();
}

TEST(CustomSnapshotDataBlobWithWarmup) {
841
  DisableAlwaysOpt();
842
  const char* source =
843
      "function f() { return Math.abs(1); }\n"
844
      "function g() { return String.raw(1); }\n"
845
      "Object.valueOf(1);"
846 847 848 849 850 851 852 853 854 855 856
      "var a = 5";
  const char* warmup = "a = f()";

  v8::StartupData cold = v8::V8::CreateSnapshotDataBlob(source);
  v8::StartupData warm = v8::V8::WarmUpSnapshotDataBlob(cold, warmup);
  delete[] cold.data;

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &warm;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();

857 858
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
859 860 861 862 863 864 865 866 867
  {
    v8::Isolate::Scope i_scope(isolate);
    v8::HandleScope h_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    delete[] warm.data;
    v8::Context::Scope c_scope(context);
    // Running the warmup script has effect on whether functions are
    // pre-compiled, but does not pollute the context.
    CHECK(IsCompiled("f"));
868
    CHECK(IsCompiled("Math.abs"));
869
    CHECK(!IsCompiled("g"));
870
    CHECK(!IsCompiled("String.raw"));
871
    CHECK(!IsCompiled("Array.prototype.sort"));
872 873 874 875
    CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
  }
  isolate->Dispose();
}
876

877
TEST(CustomSnapshotDataBlobImmortalImmovableRoots) {
878
  DisableAlwaysOpt();
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
  // Flood the startup snapshot with shared function infos. If they are
  // serialized before the immortal immovable root, the root will no longer end
  // up on the first page.
  Vector<const uint8_t> source =
      ConstructSource(STATIC_CHAR_VECTOR("var a = [];"),
                      STATIC_CHAR_VECTOR("a.push(function() {return 7});"),
                      STATIC_CHAR_VECTOR("\0"), 10000);

  v8::StartupData data = v8::V8::CreateSnapshotDataBlob(
      reinterpret_cast<const char*>(source.start()));

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &data;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();

894 895
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
896 897 898 899 900 901 902 903 904 905 906 907
  {
    v8::Isolate::Scope i_scope(isolate);
    v8::HandleScope h_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    delete[] data.data;  // We can dispose of the snapshot blob now.
    v8::Context::Scope c_scope(context);
    CHECK_EQ(7, CompileRun("a[0]()")->Int32Value(context).FromJust());
  }
  isolate->Dispose();
  source.Dispose();
}

908 909 910 911 912 913 914 915 916 917
TEST(TestThatAlwaysSucceeds) {
}


TEST(TestThatAlwaysFails) {
  bool ArtificialFailure = false;
  CHECK(ArtificialFailure);
}


918 919 920 921 922 923 924 925 926 927 928 929
int CountBuiltins() {
  // Check that we have not deserialized any additional builtin.
  HeapIterator iterator(CcTest::heap());
  DisallowHeapAllocation no_allocation;
  int counter = 0;
  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
    if (obj->IsCode() && Code::cast(obj)->kind() == Code::BUILTIN) counter++;
  }
  return counter;
}


930 931 932
static Handle<SharedFunctionInfo> CompileScript(
    Isolate* isolate, Handle<String> source, Handle<String> name,
    ScriptData** cached_data, v8::ScriptCompiler::CompileOptions options) {
933
  return Compiler::GetSharedFunctionInfoForScript(
934
      source, name, 0, 0, v8::ScriptOriginOptions(), Handle<Object>(),
935
      Handle<Context>(isolate->native_context()), NULL, cached_data, options,
936
      NOT_NATIVES_CODE);
937 938
}

939
TEST(CodeSerializerOnePlusOne) {
940
  FLAG_serialize_toplevel = true;
941
  LocalContext context;
942 943
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.
944

945
  v8::HandleScope scope(CcTest::isolate());
946

947
  const char* source = "1 + 1";
948

949 950 951 952 953 954 955 956
  Handle<String> orig_source = isolate->factory()
                                   ->NewStringFromUtf8(CStrVector(source))
                                   .ToHandleChecked();
  Handle<String> copy_source = isolate->factory()
                                   ->NewStringFromUtf8(CStrVector(source))
                                   .ToHandleChecked();
  CHECK(!orig_source.is_identical_to(copy_source));
  CHECK(orig_source->Equals(*copy_source));
957 958 959

  ScriptData* cache = NULL;

960 961 962
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, orig_source, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
963 964 965

  int builtins_count = CountBuiltins();

966 967 968
  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
969 970
    copy = CompileScript(isolate, copy_source, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
971
  }
972

973
  CHECK_NE(*orig, *copy);
974
  CHECK(Script::cast(copy->script())->source() == *copy_source);
975

976
  Handle<JSFunction> copy_fun =
977
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
978
          copy, isolate->native_context());
979
  Handle<JSObject> global(isolate->context()->global_object());
980 981 982
  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
  CHECK_EQ(2, Handle<Smi>::cast(copy_result)->value());
983 984 985 986 987

  CHECK_EQ(builtins_count, CountBuiltins());

  delete cache;
}
988

989
TEST(CodeSerializerPromotedToCompilationCache) {
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();

  v8::HandleScope scope(CcTest::isolate());

  const char* source = "1 + 1";

  Handle<String> src = isolate->factory()
                           ->NewStringFromUtf8(CStrVector(source))
                           .ToHandleChecked();
  ScriptData* cache = NULL;

  CompileScript(isolate, src, src, &cache,
                v8::ScriptCompiler::kProduceCodeCache);

  DisallowCompilation no_compile_expected(isolate);
  Handle<SharedFunctionInfo> copy = CompileScript(
      isolate, src, src, &cache, v8::ScriptCompiler::kConsumeCodeCache);

1010 1011 1012 1013 1014
  InfoVectorPair pair = isolate->compilation_cache()->LookupScript(
      src, src, 0, 0, v8::ScriptOriginOptions(), isolate->native_context(),
      SLOPPY);

  CHECK(pair.shared() == *copy);
1015 1016 1017 1018

  delete cache;
}

1019
TEST(CodeSerializerInternalizedString) {
1020 1021
  FLAG_serialize_toplevel = true;
  LocalContext context;
1022 1023 1024
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

1025 1026
  v8::HandleScope scope(CcTest::isolate());

1027
  const char* source = "'string1'";
1028

1029 1030 1031 1032 1033 1034 1035 1036
  Handle<String> orig_source = isolate->factory()
                                   ->NewStringFromUtf8(CStrVector(source))
                                   .ToHandleChecked();
  Handle<String> copy_source = isolate->factory()
                                   ->NewStringFromUtf8(CStrVector(source))
                                   .ToHandleChecked();
  CHECK(!orig_source.is_identical_to(copy_source));
  CHECK(orig_source->Equals(*copy_source));
1037 1038 1039 1040

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1041 1042 1043
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, orig_source, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1044 1045 1046 1047 1048 1049 1050 1051 1052
  Handle<JSFunction> orig_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          orig, isolate->native_context());
  Handle<Object> orig_result =
      Execution::Call(isolate, orig_fun, global, 0, NULL).ToHandleChecked();
  CHECK(orig_result->IsInternalizedString());

  int builtins_count = CountBuiltins();

1053 1054 1055
  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1056 1057
    copy = CompileScript(isolate, copy_source, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1058
  }
1059
  CHECK_NE(*orig, *copy);
1060
  CHECK(Script::cast(copy->script())->source() == *copy_source);
1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());
  CHECK_NE(*orig_fun, *copy_fun);
  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();
  CHECK(orig_result.is_identical_to(copy_result));
  Handle<String> expected =
      isolate->factory()->NewStringFromAsciiChecked("string1");

  CHECK(Handle<String>::cast(copy_result)->Equals(*expected));
  CHECK_EQ(builtins_count, CountBuiltins());

  delete cache;
}
1077

1078
TEST(CodeSerializerLargeCodeObject) {
1079 1080 1081 1082 1083 1084 1085
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

1086 1087 1088 1089
  // The serializer only tests the shared code, which is always the unoptimized
  // code. Don't even bother generating optimized code to avoid timeouts.
  FLAG_always_opt = false;

1090
  Vector<const uint8_t> source =
1091
      ConstructSource(STATIC_CHAR_VECTOR("var j=1; if (j == 0) {"),
1092
                      STATIC_CHAR_VECTOR("for (let i of Object.prototype);"),
1093
                      STATIC_CHAR_VECTOR("} j=7; j"), 1100);
1094 1095 1096 1097 1098 1099
  Handle<String> source_str =
      isolate->factory()->NewStringFromOneByte(source).ToHandleChecked();

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1100 1101 1102
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_str, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1103

1104
  CHECK(isolate->heap()->InSpace(orig->abstract_code(), LO_SPACE));
1105 1106 1107 1108

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1109 1110
    copy = CompileScript(isolate, source_str, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

  int result_int;
  CHECK(copy_result->ToInt32(&result_int));
  CHECK_EQ(7, result_int);

  delete cache;
  source.Dispose();
}

1129
TEST(CodeSerializerLargeCodeObjectWithIncrementalMarking) {
1130
  FLAG_stress_incremental_marking = false;
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
  FLAG_serialize_toplevel = true;
  FLAG_always_opt = false;
  // This test relies on (full-codegen) code objects going to large object
  // space. Once FCG goes away, it must either be redesigned (to put some
  // other large deserialized object into LO space), or it can be deleted.
  FLAG_ignition = false;
  const char* filter_flag = "--turbo-filter=NOTHING";
  FlagList::SetFlagsFromString(filter_flag, StrLength(filter_flag));
  FLAG_black_allocation = true;
  FLAG_manual_evacuation_candidates_selection = true;

  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  Heap* heap = isolate->heap();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

  Vector<const uint8_t> source = ConstructSource(
      STATIC_CHAR_VECTOR("var j=1; if (j == 0) {"),
      STATIC_CHAR_VECTOR("for (var i = 0; i < Object.prototype; i++);"),
      STATIC_CHAR_VECTOR("} j=7; var s = 'happy_hippo'; j"), 2100);
  Handle<String> source_str =
      isolate->factory()->NewStringFromOneByte(source).ToHandleChecked();

  // Create a string on an evacuation candidate in old space.
  Handle<String> moving_object;
  Page* ec_page;
  {
    AlwaysAllocateScope always_allocate(isolate);
    heap::SimulateFullSpace(heap->old_space());
    moving_object = isolate->factory()->InternalizeString(
        isolate->factory()->NewStringFromAsciiChecked("happy_hippo"));
    ec_page = Page::FromAddress(moving_object->address());
  }

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_str, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);

  CHECK(heap->InSpace(orig->abstract_code(), LO_SPACE));

  // Pretend that incremental marking is on when deserialization begins.
  heap::ForceEvacuationCandidate(ec_page);
  heap::SimulateIncrementalMarking(heap, false);
  IncrementalMarking* marking = heap->incremental_marking();
  marking->StartBlackAllocationForTesting();
  CHECK(marking->IsCompacting());
  CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*moving_object));

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
    copy = CompileScript(isolate, source_str, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
  }
  CHECK_NE(*orig, *copy);

  // We should have missed a write barrier. Complete incremental marking
  // to flush out the bug.
  heap::SimulateIncrementalMarking(heap, true);
  CcTest::CollectAllGarbage();

  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

  int result_int;
  CHECK(copy_result->ToInt32(&result_int));
  CHECK_EQ(7, result_int);

  delete cache;
  source.Dispose();
}
1211
TEST(CodeSerializerLargeStrings) {
1212 1213 1214
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
1215
  Factory* f = isolate->factory();
1216 1217 1218 1219
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

1220
  Vector<const uint8_t> source_s = ConstructSource(
1221
      STATIC_CHAR_VECTOR("var s = \""), STATIC_CHAR_VECTOR("abcdef"),
1222 1223 1224 1225
      STATIC_CHAR_VECTOR("\";"), 1000000);
  Vector<const uint8_t> source_t = ConstructSource(
      STATIC_CHAR_VECTOR("var t = \""), STATIC_CHAR_VECTOR("uvwxyz"),
      STATIC_CHAR_VECTOR("\"; s + t"), 999999);
1226
  Handle<String> source_str =
1227 1228 1229
      f->NewConsString(f->NewStringFromOneByte(source_s).ToHandleChecked(),
                       f->NewStringFromOneByte(source_t).ToHandleChecked())
          .ToHandleChecked();
1230 1231 1232 1233

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1234 1235 1236
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_str, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1237 1238 1239 1240

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1241 1242
    copy = CompileScript(isolate, source_str, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

1253
  CHECK_EQ(6 * 1999999, Handle<String>::cast(copy_result)->length());
1254
  Handle<Object> property = JSReceiver::GetDataProperty(
1255 1256
      isolate->global_object(), f->NewStringFromAsciiChecked("s"));
  CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
1257 1258
  property = JSReceiver::GetDataProperty(isolate->global_object(),
                                         f->NewStringFromAsciiChecked("t"));
1259
  CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
1260
  // Make sure we do not serialize too much, e.g. include the source string.
1261
  CHECK_LT(cache->length(), 13000000);
1262 1263

  delete cache;
1264 1265
  source_s.Dispose();
  source_t.Dispose();
1266 1267
}

1268
TEST(CodeSerializerThreeBigStrings) {
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  Factory* f = isolate->factory();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

  Vector<const uint8_t> source_a =
      ConstructSource(STATIC_CHAR_VECTOR("var a = \""), STATIC_CHAR_VECTOR("a"),
                      STATIC_CHAR_VECTOR("\";"), 700000);
  Handle<String> source_a_str =
      f->NewStringFromOneByte(source_a).ToHandleChecked();

  Vector<const uint8_t> source_b =
      ConstructSource(STATIC_CHAR_VECTOR("var b = \""), STATIC_CHAR_VECTOR("b"),
1285
                      STATIC_CHAR_VECTOR("\";"), 400000);
1286 1287 1288 1289 1290
  Handle<String> source_b_str =
      f->NewStringFromOneByte(source_b).ToHandleChecked();

  Vector<const uint8_t> source_c =
      ConstructSource(STATIC_CHAR_VECTOR("var c = \""), STATIC_CHAR_VECTOR("c"),
1291
                      STATIC_CHAR_VECTOR("\";"), 400000);
1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
  Handle<String> source_c_str =
      f->NewStringFromOneByte(source_c).ToHandleChecked();

  Handle<String> source_str =
      f->NewConsString(
             f->NewConsString(source_a_str, source_b_str).ToHandleChecked(),
             source_c_str).ToHandleChecked();

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1303 1304 1305
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_str, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1306 1307 1308 1309

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1310 1311
    copy = CompileScript(isolate, source_str, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1312 1313 1314 1315 1316 1317 1318
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());

1319
  USE(Execution::Call(isolate, copy_fun, global, 0, NULL));
1320

1321 1322 1323
  v8::Maybe<int32_t> result =
      CompileRun("(a + b).length")
          ->Int32Value(v8::Isolate::GetCurrent()->GetCurrentContext());
1324
  CHECK_EQ(400000 + 700000, result.FromJust());
1325 1326
  result = CompileRun("(b + c).length")
               ->Int32Value(v8::Isolate::GetCurrent()->GetCurrentContext());
1327
  CHECK_EQ(400000 + 400000, result.FromJust());
1328
  Heap* heap = isolate->heap();
1329 1330 1331 1332
  v8::Local<v8::String> result_str =
      CompileRun("a")
          ->ToString(CcTest::isolate()->GetCurrentContext())
          .ToLocalChecked();
1333
  CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), LO_SPACE));
1334 1335 1336
  result_str = CompileRun("b")
                   ->ToString(CcTest::isolate()->GetCurrentContext())
                   .ToLocalChecked();
1337
  CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), OLD_SPACE));
1338 1339 1340
  result_str = CompileRun("c")
                   ->ToString(CcTest::isolate()->GetCurrentContext())
                   .ToLocalChecked();
1341
  CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), OLD_SPACE));
1342 1343 1344 1345

  delete cache;
  source_a.Dispose();
  source_b.Dispose();
1346
  source_c.Dispose();
1347 1348 1349
}


1350 1351
class SerializerOneByteResource
    : public v8::String::ExternalOneByteStringResource {
1352
 public:
1353
  SerializerOneByteResource(const char* data, size_t length)
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363
      : data_(data), length_(length) {}
  virtual const char* data() const { return data_; }
  virtual size_t length() const { return length_; }

 private:
  const char* data_;
  size_t length_;
};


1364
class SerializerTwoByteResource : public v8::String::ExternalStringResource {
1365
 public:
1366
  SerializerTwoByteResource(const char* data, size_t length)
1367
      : data_(AsciiToTwoByteString(data)), length_(length) {}
1368
  ~SerializerTwoByteResource() { DeleteArray<const uint16_t>(data_); }
1369 1370 1371 1372 1373 1374 1375 1376 1377

  virtual const uint16_t* data() const { return data_; }
  virtual size_t length() const { return length_; }

 private:
  const uint16_t* data_;
  size_t length_;
};

1378
TEST(CodeSerializerExternalString) {
1379 1380 1381 1382 1383 1384 1385 1386
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

  // Obtain external internalized one-byte string.
1387
  SerializerOneByteResource one_byte_resource("one_byte", 8);
1388 1389 1390 1391 1392 1393 1394 1395
  Handle<String> one_byte_string =
      isolate->factory()->NewStringFromAsciiChecked("one_byte");
  one_byte_string = isolate->factory()->InternalizeString(one_byte_string);
  one_byte_string->MakeExternal(&one_byte_resource);
  CHECK(one_byte_string->IsExternalOneByteString());
  CHECK(one_byte_string->IsInternalizedString());

  // Obtain external internalized two-byte string.
1396
  SerializerTwoByteResource two_byte_resource("two_byte", 8);
1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
  Handle<String> two_byte_string =
      isolate->factory()->NewStringFromAsciiChecked("two_byte");
  two_byte_string = isolate->factory()->InternalizeString(two_byte_string);
  two_byte_string->MakeExternal(&two_byte_resource);
  CHECK(two_byte_string->IsExternalTwoByteString());
  CHECK(two_byte_string->IsInternalizedString());

  const char* source =
      "var o = {}               \n"
      "o.one_byte = 7;          \n"
      "o.two_byte = 8;          \n"
      "o.one_byte + o.two_byte; \n";
  Handle<String> source_string = isolate->factory()
                                     ->NewStringFromUtf8(CStrVector(source))
                                     .ToHandleChecked();

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1416 1417 1418
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_string, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1419 1420 1421 1422

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1423 1424
    copy = CompileScript(isolate, source_string, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(
          copy, isolate->native_context());

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

1435
  CHECK_EQ(15.0, copy_result->Number());
1436 1437 1438 1439

  delete cache;
}

1440
TEST(CodeSerializerLargeExternalString) {
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  Factory* f = isolate->factory();

  v8::HandleScope scope(CcTest::isolate());

  // Create a huge external internalized string to use as variable name.
  Vector<const uint8_t> string =
      ConstructSource(STATIC_CHAR_VECTOR(""), STATIC_CHAR_VECTOR("abcdef"),
1453
                      STATIC_CHAR_VECTOR(""), 999999);
1454
  Handle<String> name = f->NewStringFromOneByte(string).ToHandleChecked();
1455
  SerializerOneByteResource one_byte_resource(
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
      reinterpret_cast<const char*>(string.start()), string.length());
  name = f->InternalizeString(name);
  name->MakeExternal(&one_byte_resource);
  CHECK(name->IsExternalOneByteString());
  CHECK(name->IsInternalizedString());
  CHECK(isolate->heap()->InSpace(*name, LO_SPACE));

  // Create the source, which is "var <literal> = 42; <literal>".
  Handle<String> source_str =
      f->NewConsString(
             f->NewConsString(f->NewStringFromAsciiChecked("var "), name)
                 .ToHandleChecked(),
             f->NewConsString(f->NewStringFromAsciiChecked(" = 42; "), name)
                 .ToHandleChecked()).ToHandleChecked();

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1474 1475 1476
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_str, Handle<String>(), &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1477 1478 1479 1480

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1481 1482
    copy = CompileScript(isolate, source_str, Handle<String>(), &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1483 1484 1485 1486
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
1487
      f->NewFunctionFromSharedFunctionInfo(copy, isolate->native_context());
1488 1489 1490 1491

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

1492
  CHECK_EQ(42.0, copy_result->Number());
1493 1494 1495 1496 1497

  delete cache;
  string.Dispose();
}

1498
TEST(CodeSerializerExternalScriptName) {
1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  Factory* f = isolate->factory();

  v8::HandleScope scope(CcTest::isolate());

  const char* source =
      "var a = [1, 2, 3, 4];"
      "a.reduce(function(x, y) { return x + y }, 0)";

  Handle<String> source_string =
      f->NewStringFromUtf8(CStrVector(source)).ToHandleChecked();

  const SerializerOneByteResource one_byte_resource("one_byte", 8);
  Handle<String> name =
      f->NewExternalStringFromOneByte(&one_byte_resource).ToHandleChecked();
  CHECK(name->IsExternalOneByteString());
  CHECK(!name->IsInternalizedString());

  Handle<JSObject> global(isolate->context()->global_object());
  ScriptData* cache = NULL;

1524 1525 1526
  Handle<SharedFunctionInfo> orig =
      CompileScript(isolate, source_string, name, &cache,
                    v8::ScriptCompiler::kProduceCodeCache);
1527 1528 1529 1530

  Handle<SharedFunctionInfo> copy;
  {
    DisallowCompilation no_compile_expected(isolate);
1531 1532
    copy = CompileScript(isolate, source_string, name, &cache,
                         v8::ScriptCompiler::kConsumeCodeCache);
1533 1534 1535 1536 1537 1538 1539 1540 1541
  }
  CHECK_NE(*orig, *copy);

  Handle<JSFunction> copy_fun =
      f->NewFunctionFromSharedFunctionInfo(copy, isolate->native_context());

  Handle<Object> copy_result =
      Execution::Call(isolate, copy_fun, global, 0, NULL).ToHandleChecked();

1542
  CHECK_EQ(10.0, copy_result->Number());
1543 1544 1545 1546 1547

  delete cache;
}


1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558
static bool toplevel_test_code_event_found = false;


static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
  if (event->type == v8::JitCodeEvent::CODE_ADDED &&
      memcmp(event->name.str, "Script:~test", 12) == 0) {
    toplevel_test_code_event_found = true;
  }
}


1559
v8::ScriptCompiler::CachedData* ProduceCache(const char* source) {
1560
  v8::ScriptCompiler::CachedData* cache;
1561 1562 1563
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate1 = v8::Isolate::New(create_params);
1564
  {
1565 1566 1567
    v8::Isolate::Scope iscope(isolate1);
    v8::HandleScope scope(isolate1);
    v8::Local<v8::Context> context = v8::Context::New(isolate1);
1568 1569 1570 1571 1572
    v8::Context::Scope context_scope(context);

    v8::Local<v8::String> source_str = v8_str(source);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin);
1573 1574 1575 1576
    v8::Local<v8::UnboundScript> script =
        v8::ScriptCompiler::CompileUnboundScript(
            isolate1, &source, v8::ScriptCompiler::kProduceCodeCache)
            .ToLocalChecked();
1577
    const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
1578
    CHECK(data);
1579 1580 1581 1582 1583 1584
    // Persist cached data.
    uint8_t* buffer = NewArray<uint8_t>(data->length);
    MemCopy(buffer, data->data, data->length);
    cache = new v8::ScriptCompiler::CachedData(
        buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);

1585 1586 1587 1588 1589 1590 1591
    v8::Local<v8::Value> result = script->BindToCurrentContext()
                                      ->Run(isolate1->GetCurrentContext())
                                      .ToLocalChecked();
    v8::Local<v8::String> result_string =
        result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
    CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef"))
              .FromJust());
1592
  }
1593
  isolate1->Dispose();
1594 1595 1596
  return cache;
}

1597
TEST(CodeSerializerIsolates) {
1598 1599 1600 1601
  FLAG_serialize_toplevel = true;

  const char* source = "function f() { return 'abc'; }; f() + 'def'";
  v8::ScriptCompiler::CachedData* cache = ProduceCache(source);
1602

1603 1604 1605
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
1606 1607 1608
  isolate2->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
                                   SerializerCodeEventListener);
  toplevel_test_code_event_found = false;
1609
  {
1610 1611 1612
    v8::Isolate::Scope iscope(isolate2);
    v8::HandleScope scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
1613 1614 1615 1616 1617 1618 1619
    v8::Context::Scope context_scope(context);

    v8::Local<v8::String> source_str = v8_str(source);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin, cache);
    v8::Local<v8::UnboundScript> script;
    {
1620
      DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
1621 1622 1623
      script = v8::ScriptCompiler::CompileUnboundScript(
                   isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
                   .ToLocalChecked();
1624
    }
1625
    CHECK(!cache->rejected);
1626 1627 1628 1629 1630 1631 1632
    v8::Local<v8::Value> result = script->BindToCurrentContext()
                                      ->Run(isolate2->GetCurrentContext())
                                      .ToLocalChecked();
    CHECK(result->ToString(isolate2->GetCurrentContext())
              .ToLocalChecked()
              ->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
              .FromJust());
1633
  }
1634
  CHECK(toplevel_test_code_event_found);
1635
  isolate2->Dispose();
1636
}
1637

1638
TEST(CodeSerializerFlagChange) {
1639 1640 1641
  FLAG_serialize_toplevel = true;

  const char* source = "function f() { return 'abc'; }; f() + 'def'";
1642
  v8::ScriptCompiler::CachedData* cache = ProduceCache(source);
1643

1644 1645 1646
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
1647

1648
  FLAG_allow_natives_syntax = true;  // Flag change should trigger cache reject.
1649
  FlagList::EnforceFlagImplications();
1650
  {
1651 1652 1653
    v8::Isolate::Scope iscope(isolate2);
    v8::HandleScope scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
1654 1655 1656 1657
    v8::Context::Scope context_scope(context);

    v8::Local<v8::String> source_str = v8_str(source);
    v8::ScriptOrigin origin(v8_str("test"));
1658
    v8::ScriptCompiler::Source source(source_str, origin, cache);
1659 1660 1661
    v8::ScriptCompiler::CompileUnboundScript(
        isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
        .ToLocalChecked();
1662
    CHECK(cache->rejected);
1663
  }
1664 1665 1666
  isolate2->Dispose();
}

1667
TEST(CodeSerializerBitFlip) {
1668 1669 1670 1671 1672 1673 1674
  FLAG_serialize_toplevel = true;

  const char* source = "function f() { return 'abc'; }; f() + 'def'";
  v8::ScriptCompiler::CachedData* cache = ProduceCache(source);

  // Random bit flip.
  const_cast<uint8_t*>(cache->data)[337] ^= 0x40;
1675

1676 1677 1678
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
1679 1680 1681 1682 1683 1684 1685 1686 1687
  {
    v8::Isolate::Scope iscope(isolate2);
    v8::HandleScope scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
    v8::Context::Scope context_scope(context);

    v8::Local<v8::String> source_str = v8_str(source);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin, cache);
1688 1689 1690
    v8::ScriptCompiler::CompileUnboundScript(
        isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
        .ToLocalChecked();
1691 1692 1693 1694 1695
    CHECK(cache->rejected);
  }
  isolate2->Dispose();
}

1696
TEST(CodeSerializerWithHarmonyScoping) {
1697 1698 1699 1700 1701 1702 1703 1704
  FLAG_serialize_toplevel = true;

  const char* source1 = "'use strict'; let x = 'X'";
  const char* source2 = "'use strict'; let y = 'Y'";
  const char* source3 = "'use strict'; x + y";

  v8::ScriptCompiler::CachedData* cache;

1705 1706 1707
  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate1 = v8::Isolate::New(create_params);
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719
  {
    v8::Isolate::Scope iscope(isolate1);
    v8::HandleScope scope(isolate1);
    v8::Local<v8::Context> context = v8::Context::New(isolate1);
    v8::Context::Scope context_scope(context);

    CompileRun(source1);
    CompileRun(source2);

    v8::Local<v8::String> source_str = v8_str(source3);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin);
1720 1721 1722 1723
    v8::Local<v8::UnboundScript> script =
        v8::ScriptCompiler::CompileUnboundScript(
            isolate1, &source, v8::ScriptCompiler::kProduceCodeCache)
            .ToLocalChecked();
1724
    const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
1725
    CHECK(data);
1726 1727 1728 1729 1730 1731
    // Persist cached data.
    uint8_t* buffer = NewArray<uint8_t>(data->length);
    MemCopy(buffer, data->data, data->length);
    cache = new v8::ScriptCompiler::CachedData(
        buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);

1732 1733 1734 1735 1736 1737 1738
    v8::Local<v8::Value> result = script->BindToCurrentContext()
                                      ->Run(isolate1->GetCurrentContext())
                                      .ToLocalChecked();
    v8::Local<v8::String> result_str =
        result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
    CHECK(result_str->Equals(isolate1->GetCurrentContext(), v8_str("XY"))
              .FromJust());
1739 1740 1741
  }
  isolate1->Dispose();

1742
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758
  {
    v8::Isolate::Scope iscope(isolate2);
    v8::HandleScope scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
    v8::Context::Scope context_scope(context);

    // Reverse order of prior running scripts.
    CompileRun(source2);
    CompileRun(source1);

    v8::Local<v8::String> source_str = v8_str(source3);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin, cache);
    v8::Local<v8::UnboundScript> script;
    {
      DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
1759 1760 1761
      script = v8::ScriptCompiler::CompileUnboundScript(
                   isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
                   .ToLocalChecked();
1762
    }
1763 1764 1765 1766 1767 1768 1769
    v8::Local<v8::Value> result = script->BindToCurrentContext()
                                      ->Run(isolate2->GetCurrentContext())
                                      .ToLocalChecked();
    v8::Local<v8::String> result_str =
        result->ToString(isolate2->GetCurrentContext()).ToLocalChecked();
    CHECK(result_str->Equals(isolate2->GetCurrentContext(), v8_str("XY"))
              .FromJust());
1770 1771 1772
  }
  isolate2->Dispose();
}
1773

1774
TEST(CodeSerializerEagerCompilationAndPreAge) {
1775
  if (FLAG_ignition || FLAG_turbo) return;
1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817

  FLAG_lazy = true;
  FLAG_serialize_toplevel = true;
  FLAG_serialize_age_code = true;
  FLAG_serialize_eager = true;

  static const char* source =
      "function f() {"
      "  function g() {"
      "    return 1;"
      "  }"
      "  return g();"
      "}"
      "'abcdef';";

  v8::ScriptCompiler::CachedData* cache = ProduceCache(source);

  v8::Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
  v8::Isolate* isolate2 = v8::Isolate::New(create_params);
  {
    v8::Isolate::Scope iscope(isolate2);
    v8::HandleScope scope(isolate2);
    v8::Local<v8::Context> context = v8::Context::New(isolate2);
    v8::Context::Scope context_scope(context);

    v8::Local<v8::String> source_str = v8_str(source);
    v8::ScriptOrigin origin(v8_str("test"));
    v8::ScriptCompiler::Source source(source_str, origin, cache);
    v8::Local<v8::UnboundScript> unbound =
        v8::ScriptCompiler::CompileUnboundScript(
            isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
            .ToLocalChecked();

    CHECK(!cache->rejected);

    Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate2);
    HandleScope i_scope(i_isolate);
    Handle<SharedFunctionInfo> toplevel = v8::Utils::OpenHandle(*unbound);
    Handle<Script> script(Script::cast(toplevel->script()));
    // Every function has been pre-compiled from the code cache.
    int count = 0;
1818 1819
    SharedFunctionInfo::ScriptIterator iterator(script);
    while (SharedFunctionInfo* shared = iterator.Next()) {
1820 1821 1822 1823 1824 1825 1826 1827
      CHECK(shared->is_compiled());
      CHECK_EQ(Code::kPreAgedCodeAge, shared->code()->GetAge());
      count++;
    }
    CHECK_EQ(3, count);
  }
  isolate2->Dispose();
}
1828

1829
TEST(Regress503552) {
1830
  if (!FLAG_incremental_marking) return;
1831 1832 1833 1834 1835 1836 1837 1838 1839
  // Test that the code serializer can deal with weak cells that form a linked
  // list during incremental marking.
  CcTest::InitializeVM();
  Isolate* isolate = CcTest::i_isolate();

  HandleScope scope(isolate);
  Handle<String> source = isolate->factory()->NewStringFromAsciiChecked(
      "function f() {} function g() {}");
  ScriptData* script_data = NULL;
1840
  Handle<SharedFunctionInfo> shared = Compiler::GetSharedFunctionInfoForScript(
1841 1842
      source, Handle<String>(), 0, 0, v8::ScriptOriginOptions(),
      Handle<Object>(), Handle<Context>(isolate->native_context()), NULL,
1843
      &script_data, v8::ScriptCompiler::kProduceCodeCache, NOT_NATIVES_CODE);
1844 1845
  delete script_data;

1846
  heap::SimulateIncrementalMarking(isolate->heap());
1847 1848 1849 1850 1851

  script_data = CodeSerializer::Serialize(isolate, shared, source);
  delete script_data;
}

1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871
#if V8_TARGET_ARCH_X64
TEST(CodeSerializerCell) {
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.

  v8::HandleScope scope(CcTest::isolate());

  size_t actual_size;
  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
      Assembler::kMinimalBufferSize, &actual_size, true));
  CHECK(buffer);
  HandleScope handles(isolate);

  MacroAssembler assembler(isolate, buffer, static_cast<int>(actual_size),
                           v8::internal::CodeObjectRequired::kYes);
  assembler.enable_serializer();
  Handle<HeapNumber> number = isolate->factory()->NewHeapNumber(0.3);
  CHECK(isolate->heap()->InNewSpace(*number));
1872 1873 1874 1875 1876 1877 1878 1879
  Handle<Code> code;
  {
    MacroAssembler* masm = &assembler;
    Handle<Cell> cell = isolate->factory()->NewCell(number);
    masm->Move(rax, cell, RelocInfo::CELL);
    masm->movp(rax, Operand(rax, 0));
    masm->ret(0);
    CodeDesc desc;
1880
    masm->GetCode(isolate, &desc);
1881 1882 1883 1884
    code = isolate->factory()->NewCode(desc, Code::ComputeFlags(Code::FUNCTION),
                                       masm->CodeObject());
    code->set_has_reloc_info_for_serialization(true);
  }
1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904
  RelocIterator rit1(*code, 1 << RelocInfo::CELL);
  CHECK_EQ(*number, rit1.rinfo()->target_cell()->value());

  Handle<String> source = isolate->factory()->empty_string();
  Handle<SharedFunctionInfo> sfi =
      isolate->factory()->NewSharedFunctionInfo(source, code, false);
  ScriptData* script_data = CodeSerializer::Serialize(isolate, sfi, source);

  Handle<SharedFunctionInfo> copy =
      CodeSerializer::Deserialize(isolate, script_data, source)
          .ToHandleChecked();
  RelocIterator rit2(copy->code(), 1 << RelocInfo::CELL);
  CHECK(rit2.rinfo()->target_cell()->IsCell());
  Handle<Cell> cell(rit2.rinfo()->target_cell());
  CHECK(cell->value()->IsHeapNumber());
  CHECK_EQ(0.3, HeapNumber::cast(cell->value())->value());

  delete script_data;
}
#endif  // V8_TARGET_ARCH_X64
1905

1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928
TEST(CodeSerializerEmbeddedObject) {
  FLAG_serialize_toplevel = true;
  LocalContext context;
  Isolate* isolate = CcTest::i_isolate();
  isolate->compilation_cache()->Disable();  // Disable same-isolate code cache.
  v8::HandleScope scope(CcTest::isolate());

  size_t actual_size;
  byte* buffer = static_cast<byte*>(v8::base::OS::Allocate(
      Assembler::kMinimalBufferSize, &actual_size, true));
  CHECK(buffer);
  HandleScope handles(isolate);

  MacroAssembler assembler(isolate, buffer, static_cast<int>(actual_size),
                           v8::internal::CodeObjectRequired::kYes);
  assembler.enable_serializer();
  Handle<Object> number = isolate->factory()->NewHeapNumber(0.3);
  CHECK(isolate->heap()->InNewSpace(*number));
  Handle<Code> code;
  {
    MacroAssembler* masm = &assembler;
    masm->Push(number);
    CodeDesc desc;
1929
    masm->GetCode(isolate, &desc);
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949
    code = isolate->factory()->NewCode(desc, Code::ComputeFlags(Code::FUNCTION),
                                       masm->CodeObject());
    code->set_has_reloc_info_for_serialization(true);
  }
  RelocIterator rit1(*code, RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT));
  CHECK_EQ(*number, rit1.rinfo()->target_object());

  Handle<String> source = isolate->factory()->empty_string();
  Handle<SharedFunctionInfo> sfi =
      isolate->factory()->NewSharedFunctionInfo(source, code, false);
  ScriptData* script_data = CodeSerializer::Serialize(isolate, sfi, source);

  Handle<SharedFunctionInfo> copy =
      CodeSerializer::Deserialize(isolate, script_data, source)
          .ToHandleChecked();
  RelocIterator rit2(copy->code(),
                     RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT));
  CHECK(rit2.rinfo()->target_object()->IsHeapNumber());
  CHECK_EQ(0.3, HeapNumber::cast(rit2.rinfo()->target_object())->value());

1950
  CcTest::CollectAllAvailableGarbage();
1951 1952 1953 1954 1955 1956 1957 1958 1959

  RelocIterator rit3(copy->code(),
                     RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT));
  CHECK(rit3.rinfo()->target_object()->IsHeapNumber());
  CHECK_EQ(0.3, HeapNumber::cast(rit3.rinfo()->target_object())->value());

  delete script_data;
}

1960
TEST(SnapshotCreatorMultipleContexts) {
1961
  DisableAlwaysOpt();
1962 1963 1964 1965 1966 1967 1968 1969 1970
  v8::StartupData blob;
  {
    v8::SnapshotCreator creator;
    v8::Isolate* isolate = creator.GetIsolate();
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context = v8::Context::New(isolate);
      v8::Context::Scope context_scope(context);
      CompileRun("var f = function() { return 1; }");
1971
      creator.SetDefaultContext(context);
1972 1973 1974 1975 1976 1977
    }
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context = v8::Context::New(isolate);
      v8::Context::Scope context_scope(context);
      CompileRun("var f = function() { return 2; }");
1978
      CHECK_EQ(0u, creator.AddContext(context));
1979 1980 1981 1982
    }
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context = v8::Context::New(isolate);
1983
      CHECK_EQ(1u, creator.AddContext(context));
1984 1985 1986 1987 1988 1989 1990 1991
    }
    blob =
        creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
  }

  v8::Isolate::CreateParams params;
  params.snapshot_blob = &blob;
  params.array_buffer_allocator = CcTest::array_buffer_allocator();
1992 1993
  // Test-appropriate equivalent of v8::Isolate::New.
  v8::Isolate* isolate = TestIsolate::New(params);
1994 1995 1996 1997
  {
    v8::Isolate::Scope isolate_scope(isolate);
    {
      v8::HandleScope handle_scope(isolate);
1998
      v8::Local<v8::Context> context = v8::Context::New(isolate);
1999 2000 2001 2002 2003 2004
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 1);
    }
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context =
2005
          v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
2006 2007 2008 2009 2010 2011
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 2);
    }
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context =
2012
          v8::Context::FromSnapshot(isolate, 1).ToLocalChecked();
2013 2014 2015 2016 2017 2018 2019 2020 2021
      v8::Context::Scope context_scope(context);
      ExpectUndefined("this.f");
    }
  }

  isolate->Dispose();
  delete[] blob.data;
}

2022 2023
static void SerializedCallback(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
2024 2025 2026
  args.GetReturnValue().Set(v8_num(42));
}

2027
static void SerializedCallbackReplacement(
2028 2029 2030 2031
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  args.GetReturnValue().Set(v8_num(1337));
}

2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045
static void NamedPropertyGetterForSerialization(
    v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
  if (name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("x"))
          .FromJust()) {
    info.GetReturnValue().Set(v8_num(2016));
  }
}

static void AccessorForSerialization(
    v8::Local<v8::String> property,
    const v8::PropertyCallbackInfo<v8::Value>& info) {
  info.GetReturnValue().Set(v8_num(2017));
}

2046 2047
static int serialized_static_field = 314;

2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
class SerializedExtension : public v8::Extension {
 public:
  SerializedExtension()
      : v8::Extension("serialized extension",
                      "native function g();"
                      "function h() { return 13; };"
                      "function i() { return 14; };"
                      "var o = { p: 7 };") {}

  virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
      v8::Isolate* isolate, v8::Local<v8::String> name) {
    CHECK(name->Equals(isolate->GetCurrentContext(), v8_str("g")).FromJust());
    return v8::FunctionTemplate::New(isolate, FunctionCallback);
  }

  static void FunctionCallback(
      const v8::FunctionCallbackInfo<v8::Value>& args) {
    args.GetReturnValue().Set(v8_num(12));
  }
};

2069
intptr_t original_external_references[] = {
2070
    reinterpret_cast<intptr_t>(SerializedCallback),
2071 2072 2073 2074
    reinterpret_cast<intptr_t>(&serialized_static_field),
    reinterpret_cast<intptr_t>(&NamedPropertyGetterForSerialization),
    reinterpret_cast<intptr_t>(&AccessorForSerialization),
    reinterpret_cast<intptr_t>(&SerializedExtension::FunctionCallback),
2075
    reinterpret_cast<intptr_t>(&serialized_static_field),  // duplicate entry
2076
    0};
2077 2078

intptr_t replaced_external_references[] = {
2079
    reinterpret_cast<intptr_t>(SerializedCallbackReplacement),
2080 2081 2082 2083
    reinterpret_cast<intptr_t>(&serialized_static_field),
    reinterpret_cast<intptr_t>(&NamedPropertyGetterForSerialization),
    reinterpret_cast<intptr_t>(&AccessorForSerialization),
    reinterpret_cast<intptr_t>(&SerializedExtension::FunctionCallback),
2084
    reinterpret_cast<intptr_t>(&serialized_static_field),
2085
    0};
2086 2087

TEST(SnapshotCreatorExternalReferences) {
2088
  DisableAlwaysOpt();
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102
  v8::StartupData blob;
  {
    v8::SnapshotCreator creator(original_external_references);
    v8::Isolate* isolate = creator.GetIsolate();
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context = v8::Context::New(isolate);
      v8::Context::Scope context_scope(context);
      v8::Local<v8::FunctionTemplate> callback =
          v8::FunctionTemplate::New(isolate, SerializedCallback);
      v8::Local<v8::Value> function =
          callback->GetFunction(context).ToLocalChecked();
      CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
      ExpectInt32("f()", 42);
2103
      creator.SetDefaultContext(context);
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114
    }
    blob =
        creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
  }

  // Deserialize with the original external reference.
  {
    v8::Isolate::CreateParams params;
    params.snapshot_blob = &blob;
    params.array_buffer_allocator = CcTest::array_buffer_allocator();
    params.external_references = original_external_references;
2115 2116
    // Test-appropriate equivalent of v8::Isolate::New.
    v8::Isolate* isolate = TestIsolate::New(params);
2117 2118 2119
    {
      v8::Isolate::Scope isolate_scope(isolate);
      v8::HandleScope handle_scope(isolate);
2120
      v8::Local<v8::Context> context = v8::Context::New(isolate);
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 42);
    }
    isolate->Dispose();
  }

  // Deserialize with the some other external reference.
  {
    v8::Isolate::CreateParams params;
    params.snapshot_blob = &blob;
    params.array_buffer_allocator = CcTest::array_buffer_allocator();
    params.external_references = replaced_external_references;
2133 2134
    // Test-appropriate equivalent of v8::Isolate::New.
    v8::Isolate* isolate = TestIsolate::New(params);
2135 2136 2137
    {
      v8::Isolate::Scope isolate_scope(isolate);
      v8::HandleScope handle_scope(isolate);
2138
      v8::Local<v8::Context> context = v8::Context::New(isolate);
2139 2140 2141 2142 2143 2144 2145 2146
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 1337);
    }
    isolate->Dispose();
  }
  delete[] blob.data;
}

2147
TEST(SnapshotCreatorUnknownExternalReferences) {
2148
  DisableAlwaysOpt();
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162
  v8::SnapshotCreator creator;
  v8::Isolate* isolate = creator.GetIsolate();
  {
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);

    v8::Local<v8::FunctionTemplate> callback =
        v8::FunctionTemplate::New(isolate, SerializedCallback);
    v8::Local<v8::Value> function =
        callback->GetFunction(context).ToLocalChecked();
    CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
    ExpectInt32("f()", 42);

2163
    creator.SetDefaultContext(context);
2164 2165 2166 2167 2168 2169 2170
  }
  v8::StartupData blob =
      creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);

  delete[] blob.data;
}

2171 2172 2173 2174
struct InternalFieldData {
  uint32_t data;
};

2175 2176 2177
v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index,
                                        void* data) {
  CHECK_EQ(reinterpret_cast<void*>(2016), data);
2178
  InternalFieldData* embedder_field = static_cast<InternalFieldData*>(
2179
      holder->GetAlignedPointerFromInternalField(index));
2180
  int size = sizeof(*embedder_field);
2181 2182
  char* payload = new char[size];
  // We simply use memcpy to serialize the content.
2183
  memcpy(payload, embedder_field, size);
2184 2185 2186
  return {payload, size};
}

2187 2188
std::vector<InternalFieldData*> deserialized_data;

2189
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
2190 2191
                               v8::StartupData payload, void* data) {
  CHECK_EQ(reinterpret_cast<void*>(2017), data);
2192 2193 2194 2195
  InternalFieldData* embedder_field = new InternalFieldData{0};
  memcpy(embedder_field, payload.data, payload.raw_size);
  holder->SetAlignedPointerInInternalField(index, embedder_field);
  deserialized_data.push_back(embedder_field);
2196 2197
}

2198
TEST(SnapshotCreatorTemplates) {
2199
  DisableAlwaysOpt();
2200
  v8::StartupData blob;
2201

2202
  {
2203 2204 2205 2206
    InternalFieldData* a1 = new InternalFieldData{11};
    InternalFieldData* b0 = new InternalFieldData{20};
    InternalFieldData* c0 = new InternalFieldData{30};

2207 2208 2209 2210 2211 2212 2213
    v8::SnapshotCreator creator(original_external_references);
    v8::Isolate* isolate = creator.GetIsolate();
    {
      v8::HandleScope handle_scope(isolate);
      v8::ExtensionConfiguration* no_extension = nullptr;
      v8::Local<v8::ObjectTemplate> global_template =
          v8::ObjectTemplate::New(isolate);
2214 2215 2216
      v8::Local<v8::FunctionTemplate> callback =
          v8::FunctionTemplate::New(isolate, SerializedCallback);
      global_template->Set(v8_str("f"), callback);
2217 2218
      v8::Local<v8::Context> context =
          v8::Context::New(isolate, no_extension, global_template);
2219 2220
      creator.SetDefaultContext(context);
      context = v8::Context::New(isolate, no_extension, global_template);
2221 2222
      v8::Local<v8::ObjectTemplate> object_template =
          v8::ObjectTemplate::New(isolate);
2223
      object_template->SetInternalFieldCount(3);
2224

2225 2226
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 42);
2227 2228 2229 2230 2231 2232 2233

      v8::Local<v8::Object> a =
          object_template->NewInstance(context).ToLocalChecked();
      v8::Local<v8::Object> b =
          object_template->NewInstance(context).ToLocalChecked();
      v8::Local<v8::Object> c =
          object_template->NewInstance(context).ToLocalChecked();
2234 2235 2236 2237
      v8::Local<v8::External> null_external =
          v8::External::New(isolate, nullptr);
      v8::Local<v8::External> field_external =
          v8::External::New(isolate, &serialized_static_field);
2238 2239 2240 2241 2242
      a->SetInternalField(0, b);
      a->SetAlignedPointerInInternalField(1, a1);
      b->SetAlignedPointerInInternalField(0, b0);
      b->SetInternalField(1, c);
      c->SetAlignedPointerInInternalField(0, c0);
2243 2244
      c->SetInternalField(1, null_external);
      c->SetInternalField(2, field_external);
2245 2246
      CHECK(context->Global()->Set(context, v8_str("a"), a).FromJust());

2247 2248 2249 2250
      CHECK_EQ(0u,
               creator.AddContext(context, v8::SerializeInternalFieldsCallback(
                                               SerializeInternalFields,
                                               reinterpret_cast<void*>(2016))));
2251 2252
      CHECK_EQ(0u, creator.AddTemplate(callback));
      CHECK_EQ(1u, creator.AddTemplate(global_template));
2253
    }
2254 2255
    blob =
        creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
2256 2257 2258 2259

    delete a1;
    delete b0;
    delete c0;
2260 2261 2262 2263 2264 2265 2266
  }

  {
    v8::Isolate::CreateParams params;
    params.snapshot_blob = &blob;
    params.array_buffer_allocator = CcTest::array_buffer_allocator();
    params.external_references = original_external_references;
2267 2268
    // Test-appropriate equivalent of v8::Isolate::New.
    v8::Isolate* isolate = TestIsolate::New(params);
2269 2270 2271 2272 2273
    {
      v8::Isolate::Scope isolate_scope(isolate);
      {
        // Create a new context without a new object template.
        v8::HandleScope handle_scope(isolate);
2274
        v8::Local<v8::Context> context =
2275 2276 2277 2278
            v8::Context::FromSnapshot(
                isolate, 0,
                v8::DeserializeInternalFieldsCallback(
                    DeserializeInternalFields, reinterpret_cast<void*>(2017)))
2279
                .ToLocalChecked();
2280 2281
        v8::Context::Scope context_scope(context);
        ExpectInt32("f()", 42);
2282 2283 2284

        // Retrieve the snapshotted object template.
        v8::Local<v8::ObjectTemplate> obj_template =
2285
            v8::ObjectTemplate::FromSnapshot(isolate, 1).ToLocalChecked();
2286 2287 2288 2289 2290 2291 2292 2293 2294 2295
        CHECK(!obj_template.IsEmpty());
        v8::Local<v8::Object> object =
            obj_template->NewInstance(context).ToLocalChecked();
        CHECK(context->Global()->Set(context, v8_str("o"), object).FromJust());
        ExpectInt32("o.f()", 42);
        // Check that it instantiates to the same prototype.
        ExpectTrue("o.f.prototype === f.prototype");

        // Retrieve the snapshotted function template.
        v8::Local<v8::FunctionTemplate> fun_template =
2296
            v8::FunctionTemplate::FromSnapshot(isolate, 0).ToLocalChecked();
2297 2298 2299 2300 2301 2302 2303
        CHECK(!fun_template.IsEmpty());
        v8::Local<v8::Function> fun =
            fun_template->GetFunction(context).ToLocalChecked();
        CHECK(context->Global()->Set(context, v8_str("g"), fun).FromJust());
        ExpectInt32("g()", 42);
        // Check that it instantiates to the same prototype.
        ExpectTrue("g.prototype === f.prototype");
2304

2305
        // Retrieve embedder fields.
2306 2307 2308 2309 2310 2311 2312 2313 2314
        v8::Local<v8::Object> a = context->Global()
                                      ->Get(context, v8_str("a"))
                                      .ToLocalChecked()
                                      ->ToObject(context)
                                      .ToLocalChecked();
        v8::Local<v8::Object> b =
            a->GetInternalField(0)->ToObject(context).ToLocalChecked();
        InternalFieldData* a1 = reinterpret_cast<InternalFieldData*>(
            a->GetAlignedPointerFromInternalField(1));
2315 2316
        v8::Local<v8::Value> a2 = a->GetInternalField(2);

2317 2318 2319 2320
        InternalFieldData* b0 = reinterpret_cast<InternalFieldData*>(
            b->GetAlignedPointerFromInternalField(0));
        v8::Local<v8::Object> c =
            b->GetInternalField(1)->ToObject(context).ToLocalChecked();
2321 2322
        v8::Local<v8::Value> b2 = b->GetInternalField(2);

2323 2324
        InternalFieldData* c0 = reinterpret_cast<InternalFieldData*>(
            c->GetAlignedPointerFromInternalField(0));
2325 2326
        v8::Local<v8::Value> c1 = c->GetInternalField(1);
        v8::Local<v8::Value> c2 = c->GetInternalField(2);
2327

2328
        CHECK_EQ(11u, a1->data);
2329
        CHECK(a2->IsUndefined());
2330
        CHECK_EQ(20u, b0->data);
2331
        CHECK(b2->IsUndefined());
2332
        CHECK_EQ(30u, c0->data);
2333 2334 2335 2336
        CHECK(c1->IsExternal());
        CHECK_NULL(v8::Local<v8::External>::Cast(c1)->Value());
        CHECK_EQ(static_cast<void*>(&serialized_static_field),
                 v8::Local<v8::External>::Cast(c2)->Value());
2337

2338 2339 2340
        // Accessing out of bound returns empty MaybeHandle.
        CHECK(v8::ObjectTemplate::FromSnapshot(isolate, 2).IsEmpty());
        CHECK(v8::FunctionTemplate::FromSnapshot(isolate, 2).IsEmpty());
2341
        CHECK(v8::Context::FromSnapshot(isolate, 1).IsEmpty());
2342

2343 2344
        for (auto data : deserialized_data) delete data;
        deserialized_data.clear();
2345 2346 2347 2348 2349 2350 2351
      }
    }
    isolate->Dispose();
  }
  delete[] blob.data;
}

2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394
TEST(SnapshotCreatorIncludeGlobalProxy) {
  DisableAlwaysOpt();
  v8::StartupData blob;

  {
    v8::SnapshotCreator creator(original_external_references);
    v8::Isolate* isolate = creator.GetIsolate();
    v8::RegisterExtension(new SerializedExtension);
    const char* extension_names[] = {"serialized extension"};
    v8::ExtensionConfiguration extensions(1, extension_names);
    {
      // Set default context. This context implicitly does *not* serialize
      // the global proxy, and upon deserialization one has to be created
      // in the bootstrapper from the global object template.
      // Side effects from extensions are persisted though.
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::ObjectTemplate> global_template =
          v8::ObjectTemplate::New(isolate);
      v8::Local<v8::FunctionTemplate> callback =
          v8::FunctionTemplate::New(isolate, SerializedCallback);
      global_template->Set(v8_str("f"), callback);
      global_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
          NamedPropertyGetterForSerialization));
      v8::Local<v8::Context> context =
          v8::Context::New(isolate, &extensions, global_template);
      v8::Context::Scope context_scope(context);
      ExpectInt32("f()", 42);
      ExpectInt32("g()", 12);
      ExpectInt32("h()", 13);
      ExpectInt32("o.p", 7);
      ExpectInt32("x", 2016);
      creator.SetDefaultContext(context);
    }
    {
      // Add additional context. This context implicitly *does* serialize
      // the global proxy, and upon deserialization one has to be created
      // in the bootstrapper from the global object template.
      // Side effects from extensions are persisted.
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::ObjectTemplate> global_template =
          v8::ObjectTemplate::New(isolate);
      v8::Local<v8::FunctionTemplate> callback =
          v8::FunctionTemplate::New(isolate, SerializedCallback);
2395
      global_template->SetInternalFieldCount(3);
2396 2397 2398 2399
      global_template->Set(v8_str("f"), callback);
      global_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
          NamedPropertyGetterForSerialization));
      global_template->SetAccessor(v8_str("y"), AccessorForSerialization);
2400 2401 2402 2403 2404 2405
      v8::Local<v8::Private> priv =
          v8::Private::ForApi(isolate, v8_str("cached"));
      global_template->SetAccessorProperty(
          v8_str("cached"),
          v8::FunctionTemplate::NewWithCache(isolate, SerializedCallback, priv,
                                             v8::Local<v8::Value>()));
2406 2407 2408
      v8::Local<v8::Context> context =
          v8::Context::New(isolate, &extensions, global_template);
      v8::Context::Scope context_scope(context);
2409 2410 2411 2412 2413 2414 2415 2416 2417 2418

      CHECK(context->Global()
                ->SetPrivate(context, priv, v8_str("cached string"))
                .FromJust());
      v8::Local<v8::Private> hidden =
          v8::Private::ForApi(isolate, v8_str("hidden"));
      CHECK(context->Global()
                ->SetPrivate(context, hidden, v8_str("hidden string"))
                .FromJust());

2419 2420 2421 2422 2423 2424
      ExpectInt32("f()", 42);
      ExpectInt32("g()", 12);
      ExpectInt32("h()", 13);
      ExpectInt32("o.p", 7);
      ExpectInt32("x", 2016);
      ExpectInt32("y", 2017);
2425 2426 2427 2428 2429 2430
      CHECK(v8_str("hidden string")
                ->Equals(context, context->Global()
                                      ->GetPrivate(context, hidden)
                                      .ToLocalChecked())
                .FromJust());

2431 2432 2433 2434
      CHECK_EQ(0u,
               creator.AddContext(context, v8::SerializeInternalFieldsCallback(
                                               SerializeInternalFields,
                                               reinterpret_cast<void*>(2016))));
2435
    }
2436 2437
    blob =
        creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
2438 2439 2440 2441 2442 2443 2444
  }

  {
    v8::Isolate::CreateParams params;
    params.snapshot_blob = &blob;
    params.array_buffer_allocator = CcTest::array_buffer_allocator();
    params.external_references = original_external_references;
2445 2446
    // Test-appropriate equivalent of v8::Isolate::New.
    v8::Isolate* isolate = TestIsolate::New(params);
2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478
    {
      v8::Isolate::Scope isolate_scope(isolate);
      // We can introduce new extensions, which could override the already
      // snapshotted extension.
      v8::Extension* extension = new v8::Extension("new extension",
                                                   "function i() { return 24; }"
                                                   "function j() { return 25; }"
                                                   "if (o.p == 7) o.p++;");
      extension->set_auto_enable(true);
      v8::RegisterExtension(extension);
      {
        // Create a new context from default context snapshot. This will
        // create a new global object from a new global object template
        // without the interceptor.
        v8::HandleScope handle_scope(isolate);
        v8::Local<v8::Context> context = v8::Context::New(isolate);
        v8::Context::Scope context_scope(context);
        ExpectInt32("f()", 42);
        ExpectInt32("g()", 12);
        ExpectInt32("h()", 13);
        ExpectInt32("i()", 24);
        ExpectInt32("j()", 25);
        ExpectInt32("o.p", 8);
        v8::TryCatch try_catch(isolate);
        CHECK(CompileRun("x").IsEmpty());
        CHECK(try_catch.HasCaught());
      }
      {
        // Create a new context from first additional context snapshot. This
        // will use the global object from the snapshot, including interceptor.
        v8::HandleScope handle_scope(isolate);
        v8::Local<v8::Context> context =
2479 2480 2481 2482
            v8::Context::FromSnapshot(
                isolate, 0,
                v8::DeserializeInternalFieldsCallback(
                    DeserializeInternalFields, reinterpret_cast<void*>(2017)))
2483
                .ToLocalChecked();
2484

2485 2486 2487 2488 2489 2490 2491 2492 2493
        {
          v8::Context::Scope context_scope(context);
          ExpectInt32("f()", 42);
          ExpectInt32("g()", 12);
          ExpectInt32("h()", 13);
          ExpectInt32("i()", 24);
          ExpectInt32("j()", 25);
          ExpectInt32("o.p", 8);
          ExpectInt32("x", 2016);
2494 2495 2496 2497 2498 2499 2500 2501
          v8::Local<v8::Private> hidden =
              v8::Private::ForApi(isolate, v8_str("hidden"));
          CHECK(v8_str("hidden string")
                    ->Equals(context, context->Global()
                                          ->GetPrivate(context, hidden)
                                          .ToLocalChecked())
                    .FromJust());
          ExpectString("cached", "cached string");
2502 2503 2504
        }

        v8::Local<v8::Object> global = context->Global();
2505
        CHECK_EQ(3, global->InternalFieldCount());
2506 2507 2508 2509 2510
        context->DetachGlobal();

        // New context, but reuse global proxy.
        v8::ExtensionConfiguration* no_extensions = nullptr;
        v8::Local<v8::Context> context2 =
2511 2512 2513 2514 2515
            v8::Context::FromSnapshot(
                isolate, 0,
                v8::DeserializeInternalFieldsCallback(
                    DeserializeInternalFields, reinterpret_cast<void*>(2017)),
                no_extensions, global)
2516 2517 2518 2519 2520 2521 2522 2523 2524 2525
                .ToLocalChecked();
        {
          v8::Context::Scope context_scope(context2);
          ExpectInt32("f()", 42);
          ExpectInt32("g()", 12);
          ExpectInt32("h()", 13);
          ExpectInt32("i()", 24);
          ExpectInt32("j()", 25);
          ExpectInt32("o.p", 8);
          ExpectInt32("x", 2016);
2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540
          v8::Local<v8::Private> hidden =
              v8::Private::ForApi(isolate, v8_str("hidden"));
          CHECK(v8_str("hidden string")
                    ->Equals(context2, context2->Global()
                                           ->GetPrivate(context2, hidden)
                                           .ToLocalChecked())
                    .FromJust());

          // Set cached accessor property again.
          v8::Local<v8::Private> priv =
              v8::Private::ForApi(isolate, v8_str("cached"));
          CHECK(context2->Global()
                    ->SetPrivate(context2, priv, v8_str("cached string 1"))
                    .FromJust());
          ExpectString("cached", "cached string 1");
2541 2542 2543
        }

        CHECK(context2->Global()->Equals(context2, global).FromJust());
2544 2545 2546 2547 2548 2549 2550
      }
    }
    isolate->Dispose();
  }
  delete[] blob.data;
}

2551 2552 2553 2554 2555 2556
TEST(SerializationMemoryStats) {
  FLAG_profile_deserialization = true;
  FLAG_always_opt = false;
  v8::StartupData blob = v8::V8::CreateSnapshotDataBlob();
  delete[] blob.data;
}