LCOV - code coverage report
Current view: top level - test/unittests - microtask-queue-unittest.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 216 237 91.1 %
Date: 2019-04-17 Functions: 49 65 75.4 %

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

Generated by: LCOV version 1.10