LCOV - code coverage report
Current view: top level - test/unittests - microtask-queue-unittest.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 190 208 91.3 %
Date: 2019-03-21 Functions: 41 54 75.9 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/microtask-queue.h"
       6             : 
       7             : #include <algorithm>
       8             : #include <functional>
       9             : #include <memory>
      10             : #include <vector>
      11             : 
      12             : #include "src/heap/factory.h"
      13             : #include "src/objects-inl.h"
      14             : #include "src/objects/foreign.h"
      15             : #include "src/objects/js-array-inl.h"
      16             : #include "src/objects/js-objects-inl.h"
      17             : #include "src/objects/promise-inl.h"
      18             : #include "src/visitors.h"
      19             : #include "test/unittests/test-utils.h"
      20             : #include "testing/gtest/include/gtest/gtest.h"
      21             : 
      22             : namespace v8 {
      23             : namespace internal {
      24             : 
      25             : using Closure = std::function<void()>;
      26             : 
      27          21 : void RunStdFunction(void* data) {
      28             :   std::unique_ptr<Closure> f(static_cast<Closure*>(data));
      29             :   (*f)();
      30          21 : }
      31             : 
      32             : template <typename TMixin>
      33             : class WithFinalizationGroupMixin : public TMixin {
      34             :  public:
      35          10 :   WithFinalizationGroupMixin() = default;
      36          10 :   ~WithFinalizationGroupMixin() override = default;
      37             : 
      38          10 :   static void SetUpTestCase() {
      39          10 :     CHECK_NULL(save_flags_);
      40          10 :     save_flags_ = new SaveFlags();
      41          10 :     FLAG_harmony_weak_refs = true;
      42          10 :     FLAG_expose_gc = true;
      43             :     TMixin::SetUpTestCase();
      44          10 :   }
      45             : 
      46          10 :   static void TearDownTestCase() {
      47             :     TMixin::TearDownTestCase();
      48          10 :     CHECK_NOT_NULL(save_flags_);
      49          10 :     delete save_flags_;
      50          10 :     save_flags_ = nullptr;
      51          10 :   }
      52             : 
      53             :  private:
      54             :   static SaveFlags* save_flags_;
      55             : 
      56             :   DISALLOW_COPY_AND_ASSIGN(WithFinalizationGroupMixin);
      57             : };
      58             : 
      59             : template <typename TMixin>
      60             : SaveFlags* WithFinalizationGroupMixin<TMixin>::save_flags_ = nullptr;
      61             : 
      62             : using TestWithNativeContextAndFinalizationGroup =  //
      63             :     WithInternalIsolateMixin<                      //
      64             :         WithContextMixin<                          //
      65             :             WithFinalizationGroupMixin<            //
      66             :                 WithIsolateScopeMixin<             //
      67             :                     WithSharedIsolateMixin<        //
      68             :                         ::testing::Test>>>>>;
      69             : 
      70          30 : class MicrotaskQueueTest : public TestWithNativeContextAndFinalizationGroup {
      71             :  public:
      72             :   template <typename F>
      73          21 :   Handle<Microtask> NewMicrotask(F&& f) {
      74             :     Handle<Foreign> runner =
      75          42 :         factory()->NewForeign(reinterpret_cast<Address>(&RunStdFunction));
      76          42 :     Handle<Foreign> data = factory()->NewForeign(
      77          21 :         reinterpret_cast<Address>(new Closure(std::forward<F>(f))));
      78          21 :     return factory()->NewCallbackTask(runner, data);
      79             :   }
      80             : 
      81          10 :   void SetUp() override {
      82          20 :     microtask_queue_ = MicrotaskQueue::New(isolate());
      83             :     native_context()->set_microtask_queue(microtask_queue());
      84          10 :   }
      85             : 
      86          10 :   void TearDown() override {
      87          10 :     if (microtask_queue()) {
      88           9 :       microtask_queue()->RunMicrotasks(isolate());
      89           9 :       context()->DetachGlobal();
      90             :     }
      91          10 :   }
      92             : 
      93             :   MicrotaskQueue* microtask_queue() const { return microtask_queue_.get(); }
      94             : 
      95           1 :   void ClearTestMicrotaskQueue() {
      96           1 :     context()->DetachGlobal();
      97             :     microtask_queue_ = nullptr;
      98           1 :   }
      99             : 
     100             :   template <size_t N>
     101             :   Handle<Name> NameFromChars(const char (&chars)[N]) {
     102           4 :     return isolate()->factory()->NewStringFromStaticChars(chars);
     103             :   }
     104             : 
     105             :  private:
     106             :   std::unique_ptr<MicrotaskQueue> microtask_queue_;
     107             : };
     108             : 
     109             : class RecordingVisitor : public RootVisitor {
     110             :  public:
     111           1 :   RecordingVisitor() = default;
     112           2 :   ~RecordingVisitor() override = default;
     113             : 
     114           2 :   void VisitRootPointers(Root root, const char* description,
     115             :                          FullObjectSlot start, FullObjectSlot end) override {
     116           9 :     for (FullObjectSlot current = start; current != end; ++current) {
     117           5 :       visited_.push_back(*current);
     118             :     }
     119           2 :   }
     120             : 
     121             :   const std::vector<Object>& visited() const { return visited_; }
     122             : 
     123             :  private:
     124             :   std::vector<Object> visited_;
     125             : };
     126             : 
     127             : // Sanity check. Ensure a microtask is stored in a queue and run.
     128       15373 : TEST_F(MicrotaskQueueTest, EnqueueAndRun) {
     129           1 :   bool ran = false;
     130           2 :   EXPECT_EQ(0, microtask_queue()->capacity());
     131           2 :   EXPECT_EQ(0, microtask_queue()->size());
     132           4 :   microtask_queue()->EnqueueMicrotask(*NewMicrotask([&ran] {
     133           2 :     EXPECT_FALSE(ran);
     134           1 :     ran = true;
     135           4 :   }));
     136           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
     137           2 :   EXPECT_EQ(1, microtask_queue()->size());
     138           2 :   EXPECT_EQ(1, microtask_queue()->RunMicrotasks(isolate()));
     139           1 :   EXPECT_TRUE(ran);
     140           2 :   EXPECT_EQ(0, microtask_queue()->size());
     141           1 : }
     142             : 
     143             : // Check for a buffer growth.
     144       15373 : TEST_F(MicrotaskQueueTest, BufferGrowth) {
     145           1 :   int count = 0;
     146             : 
     147             :   // Enqueue and flush the queue first to have non-zero |start_|.
     148             :   microtask_queue()->EnqueueMicrotask(
     149           5 :       *NewMicrotask([&count] { EXPECT_EQ(0, count++); }));
     150           2 :   EXPECT_EQ(1, microtask_queue()->RunMicrotasks(isolate()));
     151             : 
     152           1 :   EXPECT_LT(0, microtask_queue()->capacity());
     153           2 :   EXPECT_EQ(0, microtask_queue()->size());
     154           2 :   EXPECT_EQ(1, microtask_queue()->start());
     155             : 
     156             :   // Fill the queue with Microtasks.
     157          17 :   for (int i = 1; i <= MicrotaskQueue::kMinimumCapacity; ++i) {
     158             :     microtask_queue()->EnqueueMicrotask(
     159          40 :         *NewMicrotask([&count, i] { EXPECT_EQ(i, count++); }));
     160             :   }
     161           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
     162           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity, microtask_queue()->size());
     163             : 
     164             :   // Add another to grow the ring buffer.
     165           2 :   microtask_queue()->EnqueueMicrotask(*NewMicrotask(
     166           6 :       [&] { EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, count++); }));
     167             : 
     168           1 :   EXPECT_LT(MicrotaskQueue::kMinimumCapacity, microtask_queue()->capacity());
     169           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1, microtask_queue()->size());
     170             : 
     171             :   // Run all pending Microtasks to ensure they run in the proper order.
     172           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 1,
     173           0 :             microtask_queue()->RunMicrotasks(isolate()));
     174           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity + 2, count);
     175           1 : }
     176             : 
     177             : // MicrotaskQueue instances form a doubly linked list.
     178       15373 : TEST_F(MicrotaskQueueTest, InstanceChain) {
     179           1 :   ClearTestMicrotaskQueue();
     180             : 
     181           1 :   MicrotaskQueue* default_mtq = isolate()->default_microtask_queue();
     182           1 :   ASSERT_TRUE(default_mtq);
     183           2 :   EXPECT_EQ(default_mtq, default_mtq->next());
     184           2 :   EXPECT_EQ(default_mtq, default_mtq->prev());
     185             : 
     186             :   // Create two instances, and check their connection.
     187             :   // The list contains all instances in the creation order, and the next of the
     188             :   // last instance is the first instance:
     189             :   //   default_mtq -> mtq1 -> mtq2 -> default_mtq.
     190           1 :   std::unique_ptr<MicrotaskQueue> mtq1 = MicrotaskQueue::New(isolate());
     191           1 :   std::unique_ptr<MicrotaskQueue> mtq2 = MicrotaskQueue::New(isolate());
     192           2 :   EXPECT_EQ(default_mtq->next(), mtq1.get());
     193           3 :   EXPECT_EQ(mtq1->next(), mtq2.get());
     194           2 :   EXPECT_EQ(mtq2->next(), default_mtq);
     195           2 :   EXPECT_EQ(default_mtq, mtq1->prev());
     196           3 :   EXPECT_EQ(mtq1.get(), mtq2->prev());
     197           3 :   EXPECT_EQ(mtq2.get(), default_mtq->prev());
     198             : 
     199             :   // Deleted item should be also removed from the list.
     200             :   mtq1 = nullptr;
     201           2 :   EXPECT_EQ(default_mtq->next(), mtq2.get());
     202           2 :   EXPECT_EQ(mtq2->next(), default_mtq);
     203           2 :   EXPECT_EQ(default_mtq, mtq2->prev());
     204           3 :   EXPECT_EQ(mtq2.get(), default_mtq->prev());
     205             : }
     206             : 
     207             : // Pending Microtasks in MicrotaskQueues are strong roots. Ensure they are
     208             : // visited exactly once.
     209       15373 : TEST_F(MicrotaskQueueTest, VisitRoot) {
     210             :   // Ensure that the ring buffer has separate in-use region.
     211          11 :   for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
     212          10 :     microtask_queue()->EnqueueMicrotask(*NewMicrotask([] {}));
     213             :   }
     214           2 :   EXPECT_EQ(MicrotaskQueue::kMinimumCapacity / 2 + 1,
     215           0 :             microtask_queue()->RunMicrotasks(isolate()));
     216             : 
     217             :   std::vector<Object> expected;
     218          11 :   for (int i = 0; i < MicrotaskQueue::kMinimumCapacity / 2 + 1; ++i) {
     219           5 :     Handle<Microtask> microtask = NewMicrotask([] {});
     220          10 :     expected.push_back(*microtask);
     221           5 :     microtask_queue()->EnqueueMicrotask(*microtask);
     222             :   }
     223           1 :   EXPECT_GT(microtask_queue()->start() + microtask_queue()->size(),
     224           0 :             microtask_queue()->capacity());
     225             : 
     226             :   RecordingVisitor visitor;
     227           1 :   microtask_queue()->IterateMicrotasks(&visitor);
     228             : 
     229           1 :   std::vector<Object> actual = visitor.visited();
     230             :   std::sort(expected.begin(), expected.end());
     231             :   std::sort(actual.begin(), actual.end());
     232           1 :   EXPECT_EQ(expected, actual);
     233           1 : }
     234             : 
     235       15373 : TEST_F(MicrotaskQueueTest, PromiseHandlerContext) {
     236           1 :   Local<v8::Context> v8_context2 = v8::Context::New(v8_isolate());
     237           1 :   Local<v8::Context> v8_context3 = v8::Context::New(v8_isolate());
     238           1 :   Local<v8::Context> v8_context4 = v8::Context::New(v8_isolate());
     239             :   Handle<Context> context2 = Utils::OpenHandle(*v8_context2, isolate());
     240             :   Handle<Context> context3 = Utils::OpenHandle(*v8_context3, isolate());
     241             :   Handle<Context> context4 = Utils::OpenHandle(*v8_context3, isolate());
     242           2 :   context2->native_context()->set_microtask_queue(microtask_queue());
     243           2 :   context3->native_context()->set_microtask_queue(microtask_queue());
     244           2 :   context4->native_context()->set_microtask_queue(microtask_queue());
     245             : 
     246             :   Handle<JSFunction> handler;
     247             :   Handle<JSProxy> proxy;
     248             :   Handle<JSProxy> revoked_proxy;
     249             :   Handle<JSBoundFunction> bound;
     250             : 
     251             :   // Create a JSFunction on |context2|
     252             :   {
     253             :     v8::Context::Scope scope(v8_context2);
     254             :     handler = RunJS<JSFunction>("()=>{}");
     255           4 :     EXPECT_EQ(*context2,
     256           0 :               *JSReceiver::GetContextForMicrotask(handler).ToHandleChecked());
     257             :   }
     258             : 
     259             :   // Create a JSProxy on |context3|.
     260             :   {
     261             :     v8::Context::Scope scope(v8_context3);
     262           4 :     ASSERT_TRUE(
     263             :         v8_context3->Global()
     264             :             ->Set(v8_context3, NewString("handler"), Utils::ToLocal(handler))
     265             :             .FromJust());
     266             :     proxy = RunJS<JSProxy>("new Proxy(handler, {})");
     267             :     revoked_proxy = RunJS<JSProxy>(
     268             :         "let {proxy, revoke} = Proxy.revocable(handler, {});"
     269             :         "revoke();"
     270             :         "proxy");
     271           4 :     EXPECT_EQ(*context2,
     272           0 :               *JSReceiver::GetContextForMicrotask(proxy).ToHandleChecked());
     273           2 :     EXPECT_TRUE(JSReceiver::GetContextForMicrotask(revoked_proxy).is_null());
     274             :   }
     275             : 
     276             :   // Create a JSBoundFunction on |context4|.
     277             :   // Note that its CreationContext and ContextForTaskCancellation is |context2|.
     278             :   {
     279             :     v8::Context::Scope scope(v8_context4);
     280           4 :     ASSERT_TRUE(
     281             :         v8_context4->Global()
     282             :             ->Set(v8_context4, NewString("handler"), Utils::ToLocal(handler))
     283             :             .FromJust());
     284             :     bound = RunJS<JSBoundFunction>("handler.bind()");
     285           4 :     EXPECT_EQ(*context2,
     286           0 :               *JSReceiver::GetContextForMicrotask(bound).ToHandleChecked());
     287             :   }
     288             : 
     289             :   // Give the objects to the main context.
     290           1 :   SetGlobalProperty("handler", Utils::ToLocal(handler));
     291           1 :   SetGlobalProperty("proxy", Utils::ToLocal(proxy));
     292           1 :   SetGlobalProperty("revoked_proxy", Utils::ToLocal(revoked_proxy));
     293           1 :   SetGlobalProperty("bound", Utils::ToLocal(Handle<JSReceiver>::cast(bound)));
     294             :   RunJS(
     295             :       "Promise.resolve().then(handler);"
     296             :       "Promise.reject().catch(proxy);"
     297             :       "Promise.resolve().then(revoked_proxy);"
     298             :       "Promise.resolve().then(bound);");
     299             : 
     300           2 :   ASSERT_EQ(4, microtask_queue()->size());
     301           1 :   Handle<Microtask> microtask1(microtask_queue()->get(0), isolate());
     302           1 :   ASSERT_TRUE(microtask1->IsPromiseFulfillReactionJobTask());
     303           3 :   EXPECT_EQ(*context2,
     304           0 :             Handle<PromiseFulfillReactionJobTask>::cast(microtask1)->context());
     305             : 
     306           1 :   Handle<Microtask> microtask2(microtask_queue()->get(1), isolate());
     307           1 :   ASSERT_TRUE(microtask2->IsPromiseRejectReactionJobTask());
     308           3 :   EXPECT_EQ(*context2,
     309           0 :             Handle<PromiseRejectReactionJobTask>::cast(microtask2)->context());
     310             : 
     311           1 :   Handle<Microtask> microtask3(microtask_queue()->get(2), isolate());
     312           1 :   ASSERT_TRUE(microtask3->IsPromiseFulfillReactionJobTask());
     313             :   // |microtask3| corresponds to a PromiseReaction for |revoked_proxy|.
     314             :   // As |revoked_proxy| doesn't have a context, the current context should be
     315             :   // used as the fallback context.
     316           3 :   EXPECT_EQ(*native_context(),
     317           0 :             Handle<PromiseFulfillReactionJobTask>::cast(microtask3)->context());
     318             : 
     319           1 :   Handle<Microtask> microtask4(microtask_queue()->get(3), isolate());
     320           1 :   ASSERT_TRUE(microtask4->IsPromiseFulfillReactionJobTask());
     321           3 :   EXPECT_EQ(*context2,
     322           0 :             Handle<PromiseFulfillReactionJobTask>::cast(microtask4)->context());
     323             : 
     324           1 :   v8_context4->DetachGlobal();
     325           1 :   v8_context3->DetachGlobal();
     326           1 :   v8_context2->DetachGlobal();
     327             : }
     328             : 
     329       15373 : TEST_F(MicrotaskQueueTest, DetachGlobal_Enqueue) {
     330           2 :   EXPECT_EQ(0, microtask_queue()->size());
     331             : 
     332             :   // Detach MicrotaskQueue from the current context.
     333           1 :   context()->DetachGlobal();
     334             : 
     335             :   // No microtask should be enqueued after DetachGlobal call.
     336           2 :   EXPECT_EQ(0, microtask_queue()->size());
     337             :   RunJS("Promise.resolve().then(()=>{})");
     338           2 :   EXPECT_EQ(0, microtask_queue()->size());
     339           1 : }
     340             : 
     341       15373 : TEST_F(MicrotaskQueueTest, DetachGlobal_Run) {
     342           2 :   EXPECT_EQ(0, microtask_queue()->size());
     343             : 
     344             :   // Enqueue microtasks to the current context.
     345             :   Handle<JSArray> ran = RunJS<JSArray>(
     346             :       "var ran = [false, false, false, false];"
     347             :       "Promise.resolve().then(() => { ran[0] = true; });"
     348             :       "Promise.reject().catch(() => { ran[1] = true; });"
     349             :       "ran");
     350             : 
     351             :   Handle<JSFunction> function =
     352             :       RunJS<JSFunction>("(function() { ran[2] = true; })");
     353             :   Handle<CallableTask> callable =
     354           1 :       factory()->NewCallableTask(function, Utils::OpenHandle(*context()));
     355           2 :   microtask_queue()->EnqueueMicrotask(*callable);
     356             : 
     357             :   // The handler should not run at this point.
     358           1 :   const int kNumExpectedTasks = 3;
     359           7 :   for (int i = 0; i < kNumExpectedTasks; ++i) {
     360           9 :     EXPECT_TRUE(
     361           0 :         Object::GetElement(isolate(), ran, i).ToHandleChecked()->IsFalse());
     362             :   }
     363           2 :   EXPECT_EQ(kNumExpectedTasks, microtask_queue()->size());
     364             : 
     365             :   // Detach MicrotaskQueue from the current context.
     366           1 :   context()->DetachGlobal();
     367             : 
     368             :   // RunMicrotasks processes pending Microtasks, but Microtasks that are
     369             :   // associated to a detached context should be cancelled and should not take
     370             :   // effect.
     371           1 :   microtask_queue()->RunMicrotasks(isolate());
     372           2 :   EXPECT_EQ(0, microtask_queue()->size());
     373           7 :   for (int i = 0; i < kNumExpectedTasks; ++i) {
     374           9 :     EXPECT_TRUE(
     375           0 :         Object::GetElement(isolate(), ran, i).ToHandleChecked()->IsFalse());
     376             :   }
     377           1 : }
     378             : 
     379       15373 : TEST_F(MicrotaskQueueTest, DetachGlobal_FinalizationGroup) {
     380             :   // Enqueue an FinalizationGroupCleanupTask.
     381             :   Handle<JSArray> ran = RunJS<JSArray>(
     382             :       "var ran = [false];"
     383             :       "var wf = new FinalizationGroup(() => { ran[0] = true; });"
     384             :       "(function() { wf.register({}, {}); })();"
     385             :       "gc();"
     386             :       "ran");
     387             : 
     388           2 :   EXPECT_TRUE(
     389           0 :       Object::GetElement(isolate(), ran, 0).ToHandleChecked()->IsFalse());
     390           2 :   EXPECT_EQ(1, microtask_queue()->size());
     391             : 
     392             :   // Detach MicrotaskQueue from the current context.
     393           1 :   context()->DetachGlobal();
     394             : 
     395           1 :   microtask_queue()->RunMicrotasks(isolate());
     396             : 
     397             :   // RunMicrotasks processes the pending Microtask, but Microtasks that are
     398             :   // associated to a detached context should be cancelled and should not take
     399             :   // effect.
     400           2 :   EXPECT_EQ(0, microtask_queue()->size());
     401           2 :   EXPECT_TRUE(
     402           0 :       Object::GetElement(isolate(), ran, 0).ToHandleChecked()->IsFalse());
     403           1 : }
     404             : 
     405             : namespace {
     406             : 
     407           3 : void DummyPromiseHook(PromiseHookType type, Local<Promise> promise,
     408           3 :                       Local<Value> parent) {}
     409             : 
     410             : }  // namespace
     411             : 
     412       15373 : TEST_F(MicrotaskQueueTest, DetachGlobal_PromiseResolveThenableJobTask) {
     413             :   // Use a PromiseHook to switch the implementation to ResolvePromise runtime,
     414             :   // instead of ResolvePromise builtin.
     415           1 :   v8_isolate()->SetPromiseHook(&DummyPromiseHook);
     416             : 
     417             :   RunJS(
     418             :       "var resolve;"
     419             :       "var promise = new Promise(r => { resolve = r; });"
     420             :       "promise.then(() => {});"
     421             :       "resolve({});");
     422             : 
     423             :   // A PromiseResolveThenableJobTask is pending in the MicrotaskQueue.
     424           2 :   EXPECT_EQ(1, microtask_queue()->size());
     425             : 
     426             :   // Detach MicrotaskQueue from the current context.
     427           1 :   context()->DetachGlobal();
     428             : 
     429             :   // RunMicrotasks processes the pending Microtask, but Microtasks that are
     430             :   // associated to a detached context should be cancelled and should not take
     431             :   // effect.
     432             :   // As PromiseResolveThenableJobTask queues another task for resolution,
     433             :   // the return value is 2 if it ran.
     434           2 :   EXPECT_EQ(1, microtask_queue()->RunMicrotasks(isolate()));
     435           2 :   EXPECT_EQ(0, microtask_queue()->size());
     436           1 : }
     437             : 
     438       15373 : TEST_F(MicrotaskQueueTest, DetachGlobal_HandlerContext) {
     439             :   // EnqueueMicrotask should use the context associated to the handler instead
     440             :   // of the current context. E.g.
     441             :   //   // At Context A.
     442             :   //   let resolved = Promise.resolve();
     443             :   //   // Call DetachGlobal on A, so that microtasks associated to A is
     444             :   //   // cancelled.
     445             :   //
     446             :   //   // At Context B.
     447             :   //   let handler = () => {
     448             :   //     console.log("here");
     449             :   //   };
     450             :   //   // The microtask to run |handler| should be associated to B instead of A,
     451             :   //   // so that handler runs even |resolved| is on the detached context A.
     452             :   //   resolved.then(handler);
     453             : 
     454           1 :   Handle<JSReceiver> results = isolate()->factory()->NewJSObjectWithNullProto();
     455             : 
     456             :   // These belong to a stale Context.
     457             :   Handle<JSPromise> stale_resolved_promise;
     458             :   Handle<JSPromise> stale_rejected_promise;
     459             :   Handle<JSReceiver> stale_handler;
     460             : 
     461           1 :   Local<v8::Context> sub_context = v8::Context::New(v8_isolate());
     462             :   {
     463             :     v8::Context::Scope scope(sub_context);
     464             :     stale_resolved_promise = RunJS<JSPromise>("Promise.resolve()");
     465             :     stale_rejected_promise = RunJS<JSPromise>("Promise.reject()");
     466             :     stale_handler = RunJS<JSReceiver>(
     467             :         "(results, label) => {"
     468             :         "  results[label] = true;"
     469             :         "}");
     470             :   }
     471             :   // DetachGlobal() cancells all microtasks associated to the context.
     472           1 :   sub_context->DetachGlobal();
     473             :   sub_context.Clear();
     474             : 
     475           1 :   SetGlobalProperty("results", Utils::ToLocal(results));
     476           1 :   SetGlobalProperty(
     477             :       "stale_resolved_promise",
     478           1 :       Utils::ToLocal(Handle<JSReceiver>::cast(stale_resolved_promise)));
     479           1 :   SetGlobalProperty(
     480             :       "stale_rejected_promise",
     481           1 :       Utils::ToLocal(Handle<JSReceiver>::cast(stale_rejected_promise)));
     482           1 :   SetGlobalProperty("stale_handler", Utils::ToLocal(stale_handler));
     483             : 
     484             :   // Set valid handlers to stale promises.
     485             :   RunJS(
     486             :       "stale_resolved_promise.then(() => {"
     487             :       "  results['stale_resolved_promise'] = true;"
     488             :       "})");
     489             :   RunJS(
     490             :       "stale_rejected_promise.catch(() => {"
     491             :       "  results['stale_rejected_promise'] = true;"
     492             :       "})");
     493           1 :   microtask_queue()->RunMicrotasks(isolate());
     494           2 :   EXPECT_TRUE(
     495             :       JSReceiver::HasProperty(results, NameFromChars("stale_resolved_promise"))
     496           0 :           .FromJust());
     497           2 :   EXPECT_TRUE(
     498             :       JSReceiver::HasProperty(results, NameFromChars("stale_rejected_promise"))
     499           0 :           .FromJust());
     500             : 
     501             :   // Set stale handlers to valid promises.
     502             :   RunJS(
     503             :       "Promise.resolve("
     504             :       "    stale_handler.bind(null, results, 'stale_handler_resolve'))");
     505             :   RunJS(
     506             :       "Promise.reject("
     507             :       "    stale_handler.bind(null, results, 'stale_handler_reject'))");
     508           1 :   microtask_queue()->RunMicrotasks(isolate());
     509           3 :   EXPECT_FALSE(
     510             :       JSReceiver::HasProperty(results, NameFromChars("stale_handler_resolve"))
     511           0 :           .FromJust());
     512           3 :   EXPECT_FALSE(
     513             :       JSReceiver::HasProperty(results, NameFromChars("stale_handler_reject"))
     514           0 :           .FromJust());
     515           1 : }
     516             : 
     517             : }  // namespace internal
     518        9222 : }  // namespace v8

Generated by: LCOV version 1.10