LCOV - code coverage report
Current view: top level - test/cctest - test-thread-termination.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 461 466 98.9 %
Date: 2019-01-20 Functions: 41 50 82.0 %

          Line data    Source code
       1             : // Copyright 2009 the V8 project authors. All rights reserved.
       2             : // Redistribution and use in source and binary forms, with or without
       3             : // modification, are permitted provided that the following conditions are
       4             : // met:
       5             : //
       6             : //     * Redistributions of source code must retain the above copyright
       7             : //       notice, this list of conditions and the following disclaimer.
       8             : //     * Redistributions in binary form must reproduce the above
       9             : //       copyright notice, this list of conditions and the following
      10             : //       disclaimer in the documentation and/or other materials provided
      11             : //       with the distribution.
      12             : //     * Neither the name of Google Inc. nor the names of its
      13             : //       contributors may be used to endorse or promote products derived
      14             : //       from this software without specific prior written permission.
      15             : //
      16             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      17             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      18             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      19             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      20             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      21             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      22             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      23             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      24             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      25             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      26             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      27             : 
      28             : #include "src/api-inl.h"
      29             : #include "src/isolate.h"
      30             : #include "src/objects-inl.h"
      31             : #include "src/v8.h"
      32             : #include "test/cctest/cctest.h"
      33             : 
      34             : #include "src/base/platform/platform.h"
      35             : 
      36             : v8::base::Semaphore* semaphore = nullptr;
      37             : 
      38          15 : void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
      39          15 :   semaphore->Signal();
      40          15 : }
      41             : 
      42             : 
      43         140 : void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
      44          70 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      45          70 :   args.GetIsolate()->TerminateExecution();
      46          70 : }
      47             : 
      48           0 : void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) { UNREACHABLE(); }
      49             : 
      50         135 : void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
      51          45 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      52             :   v8::MaybeLocal<v8::Value> result =
      53             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      54          45 :                  "try { doloop(); fail(); } catch(e) { fail(); }");
      55          45 :   CHECK(result.IsEmpty());
      56          45 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
      57          45 : }
      58             : 
      59             : 
      60          60 : void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
      61          15 :   v8::TryCatch try_catch(args.GetIsolate());
      62          15 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      63             :   v8::MaybeLocal<v8::Value> result =
      64             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      65             :                  "function f() {"
      66             :                  "  var term = true;"
      67             :                  "  try {"
      68             :                  "    while(true) {"
      69             :                  "      if (term) terminate();"
      70             :                  "      term = false;"
      71             :                  "    }"
      72             :                  "    fail();"
      73             :                  "  } catch(e) {"
      74             :                  "    fail();"
      75             :                  "  }"
      76             :                  "}"
      77          15 :                  "f()");
      78          15 :   CHECK(result.IsEmpty());
      79          15 :   CHECK(try_catch.HasCaught());
      80          30 :   CHECK(try_catch.Exception()->IsNull());
      81          30 :   CHECK(try_catch.Message().IsEmpty());
      82          15 :   CHECK(!try_catch.CanContinue());
      83          15 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
      84          15 : }
      85             : 
      86             : 
      87          60 : void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
      88          15 :   v8::TryCatch try_catch(args.GetIsolate());
      89          15 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      90             :   v8::MaybeLocal<v8::Value> result =
      91             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      92             :                  "var term = true;"
      93             :                  "while(true) {"
      94             :                  "  if (term) terminate();"
      95             :                  "  term = false;"
      96          15 :                  "}");
      97          15 :   CHECK(result.IsEmpty());
      98          15 :   CHECK(try_catch.HasCaught());
      99          30 :   CHECK(try_catch.Exception()->IsNull());
     100          30 :   CHECK(try_catch.Message().IsEmpty());
     101          15 :   CHECK(!try_catch.CanContinue());
     102          15 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
     103          15 : }
     104             : 
     105             : 
     106          75 : v8::Local<v8::ObjectTemplate> CreateGlobalTemplate(
     107             :     v8::Isolate* isolate, v8::FunctionCallback terminate,
     108             :     v8::FunctionCallback doloop) {
     109          75 :   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
     110             :   global->Set(v8_str("terminate"),
     111         225 :               v8::FunctionTemplate::New(isolate, terminate));
     112         225 :   global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
     113         225 :   global->Set(v8_str("loop"), v8::FunctionTemplate::New(isolate, Loop));
     114         225 :   global->Set(v8_str("doloop"), v8::FunctionTemplate::New(isolate, doloop));
     115          75 :   return global;
     116             : }
     117             : 
     118             : 
     119             : // Test that a single thread of JavaScript execution can terminate
     120             : // itself.
     121       28342 : TEST(TerminateOnlyV8ThreadFromThreadItself) {
     122           5 :   v8::HandleScope scope(CcTest::isolate());
     123             :   v8::Local<v8::ObjectTemplate> global =
     124           5 :       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
     125             :   v8::Local<v8::Context> context =
     126           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     127             :   v8::Context::Scope context_scope(context);
     128           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     129             :   // Run a loop that will be infinite if thread termination does not work.
     130             :   v8::MaybeLocal<v8::Value> result =
     131             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     132           5 :                  "try { loop(); fail(); } catch(e) { fail(); }");
     133           5 :   CHECK(result.IsEmpty());
     134             :   // Test that we can run the code again after thread termination.
     135           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     136             :   result = CompileRun(CcTest::isolate()->GetCurrentContext(),
     137           5 :                       "try { loop(); fail(); } catch(e) { fail(); }");
     138          10 :   CHECK(result.IsEmpty());
     139           5 : }
     140             : 
     141             : 
     142             : // Test that a single thread of JavaScript execution can terminate
     143             : // itself in a loop that performs no calls.
     144       28342 : TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
     145           5 :   v8::HandleScope scope(CcTest::isolate());
     146             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     147           5 :       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
     148             :   v8::Local<v8::Context> context =
     149           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     150             :   v8::Context::Scope context_scope(context);
     151           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     152             :   // Run a loop that will be infinite if thread termination does not work.
     153             :   static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
     154             :   v8::MaybeLocal<v8::Value> result =
     155           5 :       CompileRun(CcTest::isolate()->GetCurrentContext(), source);
     156           5 :   CHECK(result.IsEmpty());
     157           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     158             :   // Test that we can run the code again after thread termination.
     159           5 :   result = CompileRun(CcTest::isolate()->GetCurrentContext(), source);
     160          10 :   CHECK(result.IsEmpty());
     161           5 : }
     162             : 
     163             : 
     164          15 : class TerminatorThread : public v8::base::Thread {
     165             :  public:
     166             :   explicit TerminatorThread(i::Isolate* isolate)
     167             :       : Thread(Options("TerminatorThread")),
     168          15 :         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {}
     169          15 :   void Run() override {
     170          15 :     semaphore->Wait();
     171          15 :     CHECK(!isolate_->IsExecutionTerminating());
     172          15 :     isolate_->TerminateExecution();
     173          15 :   }
     174             : 
     175             :  private:
     176             :   v8::Isolate* isolate_;
     177             : };
     178             : 
     179             : 
     180             : // Test that a single thread of JavaScript execution can be terminated
     181             : // from the side by another thread.
     182       28342 : TEST(TerminateOnlyV8ThreadFromOtherThread) {
     183           5 :   semaphore = new v8::base::Semaphore(0);
     184             :   TerminatorThread thread(CcTest::i_isolate());
     185           5 :   thread.Start();
     186             : 
     187          10 :   v8::HandleScope scope(CcTest::isolate());
     188             :   v8::Local<v8::ObjectTemplate> global =
     189           5 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     190             :   v8::Local<v8::Context> context =
     191           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     192             :   v8::Context::Scope context_scope(context);
     193           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     194             :   // Run a loop that will be infinite if thread termination does not work.
     195             :   v8::MaybeLocal<v8::Value> result =
     196             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     197           5 :                  "try { loop(); fail(); } catch(e) { fail(); }");
     198           5 :   CHECK(result.IsEmpty());
     199           5 :   thread.Join();
     200           5 :   delete semaphore;
     201           5 :   semaphore = nullptr;
     202           5 : }
     203             : 
     204             : // Test that execution can be terminated from within JSON.stringify.
     205       28342 : TEST(TerminateJsonStringify) {
     206           5 :   semaphore = new v8::base::Semaphore(0);
     207             :   TerminatorThread thread(CcTest::i_isolate());
     208           5 :   thread.Start();
     209             : 
     210          10 :   v8::HandleScope scope(CcTest::isolate());
     211             :   v8::Local<v8::ObjectTemplate> global =
     212           5 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     213             :   v8::Local<v8::Context> context =
     214           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     215             :   v8::Context::Scope context_scope(context);
     216           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     217             :   v8::MaybeLocal<v8::Value> result =
     218             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     219             :                  "var x = [];"
     220             :                  "x[2**31]=1;"
     221             :                  "terminate();"
     222             :                  "JSON.stringify(x);"
     223           5 :                  "fail();");
     224           5 :   CHECK(result.IsEmpty());
     225           5 :   thread.Join();
     226           5 :   delete semaphore;
     227           5 :   semaphore = nullptr;
     228           5 : }
     229             : 
     230             : int call_count = 0;
     231             : 
     232             : 
     233         480 : void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
     234         100 :   if (++call_count == 10) {
     235          10 :     CHECK(!args.GetIsolate()->IsExecutionTerminating());
     236          10 :     args.GetIsolate()->TerminateExecution();
     237         110 :     return;
     238             :   }
     239          90 :   v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
     240             :   v8::Maybe<bool> val =
     241             :       result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"),
     242         360 :                   v8::Integer::New(args.GetIsolate(), 42));
     243          90 :   CHECK(val.FromJust());
     244             :   args.GetReturnValue().Set(result);
     245             : }
     246             : 
     247             : 
     248          40 : void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
     249          10 :   v8::TryCatch try_catch(args.GetIsolate());
     250          10 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     251             :   v8::MaybeLocal<v8::Value> result =
     252             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
     253             :                  "function f() {"
     254             :                  "  try {"
     255             :                  "    while(true) {"
     256             :                  "      terminate_or_return_object().x;"
     257             :                  "    }"
     258             :                  "    fail();"
     259             :                  "  } catch(e) {"
     260             :                  "    (function() {})();"  // trigger stack check.
     261             :                  "    fail();"
     262             :                  "  }"
     263             :                  "}"
     264          10 :                  "f()");
     265          10 :   CHECK(result.IsEmpty());
     266          10 :   CHECK(try_catch.HasCaught());
     267          20 :   CHECK(try_catch.Exception()->IsNull());
     268          20 :   CHECK(try_catch.Message().IsEmpty());
     269          10 :   CHECK(!try_catch.CanContinue());
     270          10 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
     271          10 : }
     272             : 
     273             : 
     274             : // Test that we correctly handle termination exceptions if they are
     275             : // triggered by the creation of error objects in connection with ICs.
     276       28342 : TEST(TerminateLoadICException) {
     277           5 :   v8::Isolate* isolate = CcTest::isolate();
     278           5 :   v8::HandleScope scope(isolate);
     279           5 :   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
     280             :   global->Set(v8_str("terminate_or_return_object"),
     281          15 :               v8::FunctionTemplate::New(isolate, TerminateOrReturnObject));
     282          15 :   global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
     283             :   global->Set(v8_str("loop"),
     284          15 :               v8::FunctionTemplate::New(isolate, LoopGetProperty));
     285             : 
     286           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     287             :   v8::Context::Scope context_scope(context);
     288           5 :   CHECK(!isolate->IsExecutionTerminating());
     289             :   // Run a loop that will be infinite if thread termination does not work.
     290             :   static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
     291           5 :   call_count = 0;
     292             :   v8::MaybeLocal<v8::Value> result =
     293           5 :       CompileRun(isolate->GetCurrentContext(), source);
     294           5 :   CHECK(result.IsEmpty());
     295             :   // Test that we can run the code again after thread termination.
     296           5 :   CHECK(!isolate->IsExecutionTerminating());
     297           5 :   call_count = 0;
     298           5 :   result = CompileRun(isolate->GetCurrentContext(), source);
     299          10 :   CHECK(result.IsEmpty());
     300           5 : }
     301             : 
     302             : 
     303       28337 : v8::Persistent<v8::String> reenter_script_1;
     304       28337 : v8::Persistent<v8::String> reenter_script_2;
     305             : 
     306          20 : void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
     307          10 :   v8::TryCatch try_catch(args.GetIsolate());
     308             :   v8::Isolate* isolate = args.GetIsolate();
     309          10 :   CHECK(!isolate->IsExecutionTerminating());
     310             :   v8::Local<v8::String> script =
     311          10 :       v8::Local<v8::String>::New(isolate, reenter_script_1);
     312          10 :   v8::MaybeLocal<v8::Value> result = CompileRun(script);
     313          10 :   CHECK(result.IsEmpty());
     314          10 :   CHECK(try_catch.HasCaught());
     315          20 :   CHECK(try_catch.Exception()->IsNull());
     316          20 :   CHECK(try_catch.Message().IsEmpty());
     317          10 :   CHECK(!try_catch.CanContinue());
     318          10 :   CHECK(try_catch.HasTerminated());
     319          10 :   CHECK(isolate->IsExecutionTerminating());
     320          10 :   script = v8::Local<v8::String>::New(isolate, reenter_script_2);
     321             :   v8::MaybeLocal<v8::Script> compiled_script =
     322          10 :       v8::Script::Compile(isolate->GetCurrentContext(), script);
     323          10 :   CHECK(compiled_script.IsEmpty());
     324          10 : }
     325             : 
     326             : 
     327             : // Test that reentry into V8 while the termination exception is still pending
     328             : // (has not yet unwound the 0-level JS frame) does not crash.
     329       28342 : TEST(TerminateAndReenterFromThreadItself) {
     330           5 :   v8::Isolate* isolate = CcTest::isolate();
     331           5 :   v8::HandleScope scope(isolate);
     332             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     333           5 :       isolate, TerminateCurrentThread, ReenterAfterTermination);
     334           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     335             :   v8::Context::Scope context_scope(context);
     336           5 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     337             :   // Create script strings upfront as it won't work when terminating.
     338             :   reenter_script_1.Reset(isolate, v8_str(
     339             :                                       "function f() {"
     340             :                                       "  var term = true;"
     341             :                                       "  try {"
     342             :                                       "    while(true) {"
     343             :                                       "      if (term) terminate();"
     344             :                                       "      term = false;"
     345             :                                       "    }"
     346             :                                       "    fail();"
     347             :                                       "  } catch(e) {"
     348             :                                       "    fail();"
     349             :                                       "  }"
     350             :                                       "}"
     351          10 :                                       "f()"));
     352          10 :   reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()"));
     353             :   CompileRun("try { loop(); fail(); } catch(e) { fail(); }");
     354           5 :   CHECK(!isolate->IsExecutionTerminating());
     355             :   // Check we can run JS again after termination.
     356           5 :   CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
     357             :   reenter_script_1.Reset();
     358           5 :   reenter_script_2.Reset();
     359           5 : }
     360             : 
     361       28342 : TEST(TerminateAndReenterFromThreadItselfWithOuterTryCatch) {
     362           5 :   v8::Isolate* isolate = CcTest::isolate();
     363           5 :   v8::HandleScope scope(isolate);
     364             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     365           5 :       isolate, TerminateCurrentThread, ReenterAfterTermination);
     366           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     367             :   v8::Context::Scope context_scope(context);
     368           5 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     369             :   // Create script strings upfront as it won't work when terminating.
     370             :   reenter_script_1.Reset(isolate, v8_str("function f() {"
     371             :                                          "  var term = true;"
     372             :                                          "  try {"
     373             :                                          "    while(true) {"
     374             :                                          "      if (term) terminate();"
     375             :                                          "      term = false;"
     376             :                                          "    }"
     377             :                                          "    fail();"
     378             :                                          "  } catch(e) {"
     379             :                                          "    fail();"
     380             :                                          "  }"
     381             :                                          "}"
     382          10 :                                          "f()"));
     383          10 :   reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()"));
     384             :   {
     385           5 :     v8::TryCatch try_catch(isolate);
     386             :     CompileRun("try { loop(); fail(); } catch(e) { fail(); }");
     387           5 :     CHECK(try_catch.HasCaught());
     388          10 :     CHECK(try_catch.Exception()->IsNull());
     389          10 :     CHECK(try_catch.Message().IsEmpty());
     390           5 :     CHECK(!try_catch.CanContinue());
     391           5 :     CHECK(try_catch.HasTerminated());
     392           5 :     CHECK(isolate->IsExecutionTerminating());
     393             :   }
     394           5 :   CHECK(!isolate->IsExecutionTerminating());
     395             :   // Check we can run JS again after termination.
     396           5 :   CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
     397             :   reenter_script_1.Reset();
     398           5 :   reenter_script_2.Reset();
     399           5 : }
     400             : 
     401          10 : void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
     402           5 :   v8::TryCatch try_catch(args.GetIsolate());
     403           5 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     404             :   v8::MaybeLocal<v8::Value> result =
     405             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
     406             :                  "var term = true;"
     407             :                  "while(true) {"
     408             :                  "  if (term) terminate();"
     409             :                  "  term = false;"
     410             :                  "}"
     411           5 :                  "fail();");
     412           5 :   CHECK(result.IsEmpty());
     413           5 :   CHECK(try_catch.HasCaught());
     414          10 :   CHECK(try_catch.Exception()->IsNull());
     415          10 :   CHECK(try_catch.Message().IsEmpty());
     416           5 :   CHECK(!try_catch.CanContinue());
     417           5 :   CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
     418           5 :   CHECK(try_catch.HasTerminated());
     419           5 :   CcTest::isolate()->CancelTerminateExecution();
     420           5 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     421           5 : }
     422             : 
     423             : 
     424             : // Test that a single thread of JavaScript execution can terminate
     425             : // itself and then resume execution.
     426       28342 : TEST(TerminateCancelTerminateFromThreadItself) {
     427           5 :   v8::Isolate* isolate = CcTest::isolate();
     428           5 :   v8::HandleScope scope(isolate);
     429             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     430           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     431           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     432             :   v8::Context::Scope context_scope(context);
     433           5 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     434             :   // Check that execution completed with correct return value.
     435             :   v8::Local<v8::Value> result =
     436             :       CompileRun(isolate->GetCurrentContext(),
     437           5 :                  "try { doloop(); } catch(e) { fail(); } 'completed';")
     438           5 :           .ToLocalChecked();
     439          15 :   CHECK(result->Equals(isolate->GetCurrentContext(), v8_str("completed"))
     440           5 :             .FromJust());
     441           5 : }
     442             : 
     443             : 
     444           0 : void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
     445           0 :   UNREACHABLE();
     446             : }
     447             : 
     448             : 
     449           5 : void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) {
     450             :   v8::Isolate* isolate = info.GetIsolate();
     451           5 :   v8::HandleScope scope(isolate);
     452             :   // Enqueue another should-not-run task to ensure we clean out the queue
     453             :   // when we terminate.
     454             :   isolate->EnqueueMicrotask(
     455          10 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
     456          10 :           .ToLocalChecked());
     457             :   CompileRun("terminate(); while (true) { }");
     458           5 :   CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
     459           5 : }
     460             : 
     461             : 
     462       28342 : TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
     463           5 :   semaphore = new v8::base::Semaphore(0);
     464             :   TerminatorThread thread(CcTest::i_isolate());
     465           5 :   thread.Start();
     466             : 
     467           5 :   v8::Isolate* isolate = CcTest::isolate();
     468           5 :   isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
     469          10 :   v8::HandleScope scope(isolate);
     470             :   v8::Local<v8::ObjectTemplate> global =
     471           5 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     472             :   v8::Local<v8::Context> context =
     473           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     474             :   v8::Context::Scope context_scope(context);
     475             :   isolate->EnqueueMicrotask(
     476          10 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever)
     477          10 :           .ToLocalChecked());
     478             :   // The second task should never be run because we bail out if we're
     479             :   // terminating.
     480             :   isolate->EnqueueMicrotask(
     481          10 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
     482          10 :           .ToLocalChecked());
     483           5 :   isolate->RunMicrotasks();
     484             : 
     485           5 :   isolate->CancelTerminateExecution();
     486           5 :   isolate->RunMicrotasks();  // should not run MicrotaskShouldNotRun
     487             : 
     488           5 :   thread.Join();
     489           5 :   delete semaphore;
     490           5 :   semaphore = nullptr;
     491           5 : }
     492             : 
     493             : 
     494             : static int callback_counter = 0;
     495             : 
     496             : 
     497          10 : static void CounterCallback(v8::Isolate* isolate, void* data) {
     498          10 :   callback_counter++;
     499          10 : }
     500             : 
     501             : 
     502       28342 : TEST(PostponeTerminateException) {
     503           5 :   v8::Isolate* isolate = CcTest::isolate();
     504           5 :   v8::HandleScope scope(isolate);
     505             :   v8::Local<v8::ObjectTemplate> global =
     506           5 :       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
     507             :   v8::Local<v8::Context> context =
     508           5 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     509             :   v8::Context::Scope context_scope(context);
     510             : 
     511          10 :   v8::TryCatch try_catch(isolate);
     512             :   static const char* terminate_and_loop =
     513             :       "terminate(); for (var i = 0; i < 10000; i++);";
     514             : 
     515             :   { // Postpone terminate execution interrupts.
     516             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     517             :                                   i::StackGuard::TERMINATE_EXECUTION);
     518             : 
     519             :     // API interrupts should still be triggered.
     520           5 :     CcTest::isolate()->RequestInterrupt(&CounterCallback, nullptr);
     521           5 :     CHECK_EQ(0, callback_counter);
     522           5 :     CompileRun(terminate_and_loop);
     523           5 :     CHECK(!try_catch.HasTerminated());
     524           5 :     CHECK_EQ(1, callback_counter);
     525             : 
     526             :     { // Postpone API interrupts as well.
     527             :       i::PostponeInterruptsScope p2(CcTest::i_isolate(),
     528             :                                     i::StackGuard::API_INTERRUPT);
     529             : 
     530             :       // None of the two interrupts should trigger.
     531           5 :       CcTest::isolate()->RequestInterrupt(&CounterCallback, nullptr);
     532           5 :       CompileRun(terminate_and_loop);
     533           5 :       CHECK(!try_catch.HasTerminated());
     534           5 :       CHECK_EQ(1, callback_counter);
     535             :     }
     536             : 
     537             :     // Now the previously requested API interrupt should trigger.
     538           5 :     CompileRun(terminate_and_loop);
     539           5 :     CHECK(!try_catch.HasTerminated());
     540           5 :     CHECK_EQ(2, callback_counter);
     541             :   }
     542             : 
     543             :   // Now the previously requested terminate execution interrupt should trigger.
     544             :   CompileRun("for (var i = 0; i < 10000; i++);");
     545           5 :   CHECK(try_catch.HasTerminated());
     546          10 :   CHECK_EQ(2, callback_counter);
     547           5 : }
     548             : 
     549          65 : static void AssertTerminatedCodeRun(v8::Isolate* isolate) {
     550          65 :   v8::TryCatch try_catch(isolate);
     551             :   CompileRun("for (var i = 0; i < 10000; i++);");
     552          65 :   CHECK(try_catch.HasTerminated());
     553          65 : }
     554             : 
     555          90 : static void AssertFinishedCodeRun(v8::Isolate* isolate) {
     556          90 :   v8::TryCatch try_catch(isolate);
     557             :   CompileRun("for (var i = 0; i < 10000; i++);");
     558          90 :   CHECK(!try_catch.HasTerminated());
     559          90 : }
     560             : 
     561       28342 : TEST(SafeForTerminateException) {
     562           5 :   v8::Isolate* isolate = CcTest::isolate();
     563           5 :   v8::HandleScope scope(isolate);
     564           5 :   v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
     565             :   v8::Context::Scope context_scope(context);
     566             : 
     567             :   {  // Checks safe for termination scope.
     568             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     569             :                                   i::StackGuard::TERMINATE_EXECUTION);
     570           5 :     isolate->TerminateExecution();
     571           5 :     AssertFinishedCodeRun(isolate);
     572             :     {
     573             :       i::SafeForInterruptsScope p2(CcTest::i_isolate(),
     574             :                                    i::StackGuard::TERMINATE_EXECUTION);
     575           5 :       AssertTerminatedCodeRun(isolate);
     576           5 :       AssertFinishedCodeRun(isolate);
     577           5 :       isolate->TerminateExecution();
     578             :     }
     579           5 :     AssertFinishedCodeRun(isolate);
     580           5 :     isolate->CancelTerminateExecution();
     581             :   }
     582             : 
     583           5 :   isolate->TerminateExecution();
     584             :   {  // no scope -> postpone
     585             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     586             :                                   i::StackGuard::TERMINATE_EXECUTION);
     587           5 :     AssertFinishedCodeRun(isolate);
     588             :     {  // postpone -> postpone
     589             :       i::PostponeInterruptsScope p2(CcTest::i_isolate(),
     590             :                                     i::StackGuard::TERMINATE_EXECUTION);
     591           5 :       AssertFinishedCodeRun(isolate);
     592             : 
     593             :       {  // postpone -> safe
     594             :         i::SafeForInterruptsScope p3(CcTest::i_isolate(),
     595             :                                      i::StackGuard::TERMINATE_EXECUTION);
     596           5 :         AssertTerminatedCodeRun(isolate);
     597           5 :         isolate->TerminateExecution();
     598             : 
     599             :         {  // safe -> safe
     600             :           i::SafeForInterruptsScope p4(CcTest::i_isolate(),
     601             :                                        i::StackGuard::TERMINATE_EXECUTION);
     602           5 :           AssertTerminatedCodeRun(isolate);
     603           5 :           isolate->TerminateExecution();
     604             : 
     605             :           {  // safe -> postpone
     606             :             i::PostponeInterruptsScope p5(CcTest::i_isolate(),
     607             :                                           i::StackGuard::TERMINATE_EXECUTION);
     608           5 :             AssertFinishedCodeRun(isolate);
     609             :           }  // postpone -> safe
     610             : 
     611           5 :           AssertTerminatedCodeRun(isolate);
     612           5 :           isolate->TerminateExecution();
     613             :         }  // safe -> safe
     614             : 
     615           5 :         AssertTerminatedCodeRun(isolate);
     616           5 :         isolate->TerminateExecution();
     617             :       }  // safe -> postpone
     618             : 
     619           5 :       AssertFinishedCodeRun(isolate);
     620             :     }  // postpone -> postpone
     621             : 
     622           5 :     AssertFinishedCodeRun(isolate);
     623             :   }  // postpone -> no scope
     624           5 :   AssertTerminatedCodeRun(isolate);
     625             : 
     626           5 :   isolate->TerminateExecution();
     627             :   {  // no scope -> safe
     628             :     i::SafeForInterruptsScope p1(CcTest::i_isolate(),
     629             :                                  i::StackGuard::TERMINATE_EXECUTION);
     630           5 :     AssertTerminatedCodeRun(isolate);
     631             :   }  // safe -> no scope
     632           5 :   AssertFinishedCodeRun(isolate);
     633             : 
     634             :   {  // no scope -> postpone
     635             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     636             :                                   i::StackGuard::TERMINATE_EXECUTION);
     637           5 :     isolate->TerminateExecution();
     638             :     {  // postpone -> safe
     639             :       i::SafeForInterruptsScope p2(CcTest::i_isolate(),
     640             :                                    i::StackGuard::TERMINATE_EXECUTION);
     641           5 :       AssertTerminatedCodeRun(isolate);
     642             :     }  // safe -> postpone
     643             :   }    // postpone -> no scope
     644           5 :   AssertFinishedCodeRun(isolate);
     645             : 
     646             :   {  // no scope -> postpone
     647             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     648             :                                   i::StackGuard::TERMINATE_EXECUTION);
     649             :     {  // postpone -> safe
     650             :       i::SafeForInterruptsScope p2(CcTest::i_isolate(),
     651             :                                    i::StackGuard::TERMINATE_EXECUTION);
     652             :       {  // safe -> postpone
     653             :         i::PostponeInterruptsScope p3(CcTest::i_isolate(),
     654             :                                       i::StackGuard::TERMINATE_EXECUTION);
     655           5 :         isolate->TerminateExecution();
     656             :       }  // postpone -> safe
     657           5 :       AssertTerminatedCodeRun(isolate);
     658             :     }  // safe -> postpone
     659           5 :   }    // postpone -> no scope
     660           5 : }
     661             : 
     662          10 : void RequestTermianteAndCallAPI(
     663          20 :     const v8::FunctionCallbackInfo<v8::Value>& args) {
     664          10 :   args.GetIsolate()->TerminateExecution();
     665          10 :   AssertFinishedCodeRun(args.GetIsolate());
     666          10 : }
     667             : 
     668       28342 : UNINITIALIZED_TEST(IsolateSafeForTerminationMode) {
     669             :   v8::Isolate::CreateParams create_params;
     670           5 :   create_params.only_terminate_in_safe_scope = true;
     671           5 :   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
     672           5 :   v8::Isolate* isolate = v8::Isolate::New(create_params);
     673             :   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
     674             :   {
     675             :     v8::Isolate::Scope isolate_scope(isolate);
     676          10 :     v8::HandleScope handle_scope(isolate);
     677           5 :     v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
     678             :     global->Set(v8_str("terminateAndCallAPI"),
     679          15 :                 v8::FunctionTemplate::New(isolate, RequestTermianteAndCallAPI));
     680           5 :     v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     681             :     v8::Context::Scope context_scope(context);
     682             : 
     683             :     // Should postpone termination without safe scope.
     684           5 :     isolate->TerminateExecution();
     685           5 :     AssertFinishedCodeRun(isolate);
     686             :     {
     687           5 :       v8::Isolate::SafeForTerminationScope safe_scope(isolate);
     688           5 :       AssertTerminatedCodeRun(isolate);
     689             :     }
     690           5 :     AssertFinishedCodeRun(isolate);
     691             : 
     692             :     {
     693           5 :       isolate->TerminateExecution();
     694           5 :       AssertFinishedCodeRun(isolate);
     695             :       i::PostponeInterruptsScope p1(i_isolate,
     696             :                                     i::StackGuard::TERMINATE_EXECUTION);
     697             :       {
     698             :         // SafeForTermination overrides postpone.
     699           5 :         v8::Isolate::SafeForTerminationScope safe_scope(isolate);
     700           5 :         AssertTerminatedCodeRun(isolate);
     701             :       }
     702           5 :       AssertFinishedCodeRun(isolate);
     703             :     }
     704             : 
     705             :     {
     706           5 :       v8::Isolate::SafeForTerminationScope safe_scope(isolate);
     707             :       // Request terminate and call API recursively.
     708             :       CompileRun("terminateAndCallAPI()");
     709           5 :       AssertTerminatedCodeRun(isolate);
     710             :     }
     711             : 
     712             :     {
     713             :       i::PostponeInterruptsScope p1(i_isolate,
     714             :                                     i::StackGuard::TERMINATE_EXECUTION);
     715             :       // Request terminate and call API recursively.
     716             :       CompileRun("terminateAndCallAPI()");
     717           5 :       AssertFinishedCodeRun(isolate);
     718             :     }
     719           5 :     AssertFinishedCodeRun(isolate);
     720             :     {
     721           5 :       v8::Isolate::SafeForTerminationScope safe_scope(isolate);
     722           5 :       AssertTerminatedCodeRun(isolate);
     723             :     }
     724             :   }
     725           5 :   isolate->Dispose();
     726           5 : }
     727             : 
     728       28342 : TEST(ErrorObjectAfterTermination) {
     729           5 :   v8::Isolate* isolate = CcTest::isolate();
     730           5 :   v8::HandleScope scope(isolate);
     731           5 :   v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
     732             :   v8::Context::Scope context_scope(context);
     733           5 :   isolate->TerminateExecution();
     734           5 :   v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
     735          10 :   CHECK(error->IsNativeError());
     736           5 : }
     737             : 
     738             : 
     739          10 : void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
     740           5 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     741           5 :   v8::Local<v8::Object> global = CcTest::global();
     742             :   v8::Local<v8::Function> loop = v8::Local<v8::Function>::Cast(
     743          15 :       global->Get(CcTest::isolate()->GetCurrentContext(), v8_str("loop"))
     744           5 :           .ToLocalChecked());
     745           5 :   i::MaybeHandle<i::Object> exception;
     746             :   i::MaybeHandle<i::Object> result =
     747             :       i::Execution::TryCall(CcTest::i_isolate(), v8::Utils::OpenHandle((*loop)),
     748             :                             v8::Utils::OpenHandle((*global)), 0, nullptr,
     749           5 :                             i::Execution::MessageHandling::kReport, &exception);
     750           5 :   CHECK(result.is_null());
     751             :   // TryCall ignores terminate execution, but rerequests the interrupt.
     752           5 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     753           5 :   CHECK(CompileRun("1 + 1;").IsEmpty());
     754           5 : }
     755             : 
     756             : 
     757       28342 : TEST(TerminationInInnerTryCall) {
     758           5 :   v8::Isolate* isolate = CcTest::isolate();
     759           5 :   v8::HandleScope scope(isolate);
     760             :   v8::Local<v8::ObjectTemplate> global_template = CreateGlobalTemplate(
     761           5 :       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
     762             :   global_template->Set(
     763             :       v8_str("inner_try_call_terminate"),
     764          15 :       v8::FunctionTemplate::New(isolate, InnerTryCallTerminate));
     765             :   v8::Local<v8::Context> context =
     766           5 :       v8::Context::New(CcTest::isolate(), nullptr, global_template);
     767             :   v8::Context::Scope context_scope(context);
     768             :   {
     769           5 :     v8::TryCatch try_catch(isolate);
     770             :     CompileRun("inner_try_call_terminate()");
     771           5 :     CHECK(try_catch.HasTerminated());
     772             :   }
     773             :   v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
     774          10 :       v8::Isolate::GetCurrent()->GetCurrentContext());
     775           5 :   CHECK_EQ(4, result.FromJust());
     776          10 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     777           5 : }
     778             : 
     779             : 
     780       28342 : TEST(TerminateAndTryCall) {
     781           5 :   i::FLAG_allow_natives_syntax = true;
     782           5 :   v8::Isolate* isolate = CcTest::isolate();
     783           5 :   v8::HandleScope scope(isolate);
     784             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     785           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     786           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     787             :   v8::Context::Scope context_scope(context);
     788           5 :   CHECK(!isolate->IsExecutionTerminating());
     789             :   {
     790           5 :     v8::TryCatch try_catch(isolate);
     791           5 :     CHECK(!isolate->IsExecutionTerminating());
     792             :     // Terminate execution has been triggered inside TryCall, but re-requested
     793             :     // to trigger later.
     794           5 :     CHECK(CompileRun("terminate(); reference_error();").IsEmpty());
     795           5 :     CHECK(try_catch.HasCaught());
     796           5 :     CHECK(!isolate->IsExecutionTerminating());
     797             :     v8::Local<v8::Value> value =
     798             :         CcTest::global()
     799          15 :             ->Get(isolate->GetCurrentContext(), v8_str("terminate"))
     800           5 :             .ToLocalChecked();
     801           5 :     CHECK(value->IsFunction());
     802             :     // The first stack check after terminate has been re-requested fails.
     803           5 :     CHECK(CompileRun("1 + 1").IsEmpty());
     804           5 :     CHECK(isolate->IsExecutionTerminating());
     805             :   }
     806             :   // V8 then recovers.
     807             :   v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
     808          10 :       v8::Isolate::GetCurrent()->GetCurrentContext());
     809           5 :   CHECK_EQ(4, result.FromJust());
     810          10 :   CHECK(!isolate->IsExecutionTerminating());
     811           5 : }
     812             : 
     813           0 : class ConsoleImpl : public v8::debug::ConsoleDelegate {
     814             :  private:
     815           5 :   void Log(const v8::debug::ConsoleCallArguments& args,
     816             :            const v8::debug::ConsoleContext&) override {
     817             :     CompileRun("1 + 1");
     818           5 :   }
     819             : };
     820             : 
     821       28342 : TEST(TerminateConsole) {
     822           5 :   i::FLAG_allow_natives_syntax = true;
     823           5 :   v8::Isolate* isolate = CcTest::isolate();
     824           5 :   ConsoleImpl console;
     825           5 :   v8::debug::SetConsoleDelegate(isolate, &console);
     826          10 :   v8::HandleScope scope(isolate);
     827             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     828           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     829           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     830             :   v8::Context::Scope context_scope(context);
     831           5 :   CHECK(!isolate->IsExecutionTerminating());
     832          10 :   v8::TryCatch try_catch(isolate);
     833           5 :   CHECK(!isolate->IsExecutionTerminating());
     834           5 :   CHECK(CompileRun("terminate(); console.log(); fail();").IsEmpty());
     835           5 :   CHECK(try_catch.HasCaught());
     836           5 :   CHECK(isolate->IsExecutionTerminating());
     837           5 : }
     838             : 
     839           5 : class TerminatorSleeperThread : public v8::base::Thread {
     840             :  public:
     841             :   explicit TerminatorSleeperThread(v8::Isolate* isolate, int sleep_ms)
     842             :       : Thread(Options("TerminatorSlepperThread")),
     843             :         isolate_(isolate),
     844           5 :         sleep_ms_(sleep_ms) {}
     845           5 :   void Run() override {
     846          10 :     v8::base::OS::Sleep(v8::base::TimeDelta::FromMilliseconds(sleep_ms_));
     847           5 :     CHECK(!isolate_->IsExecutionTerminating());
     848           5 :     isolate_->TerminateExecution();
     849           5 :   }
     850             : 
     851             :  private:
     852             :   v8::Isolate* isolate_;
     853             :   int sleep_ms_;
     854             : };
     855             : 
     856       28342 : TEST(TerminateRegExp) {
     857             : // regexp interpreter does not support preemption.
     858             : #ifndef V8_INTERPRETED_REGEXP
     859           5 :   i::FLAG_allow_natives_syntax = true;
     860           5 :   v8::Isolate* isolate = CcTest::isolate();
     861           5 :   v8::HandleScope scope(isolate);
     862             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     863           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     864           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     865             :   v8::Context::Scope context_scope(context);
     866           5 :   CHECK(!isolate->IsExecutionTerminating());
     867          10 :   v8::TryCatch try_catch(isolate);
     868           5 :   CHECK(!isolate->IsExecutionTerminating());
     869           5 :   CHECK(!CompileRun("var re = /(x+)+y$/; re.test('x');").IsEmpty());
     870             :   TerminatorSleeperThread terminator(isolate, 100);
     871           5 :   terminator.Start();
     872           5 :   CHECK(CompileRun("re.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); fail();")
     873             :             .IsEmpty());
     874           5 :   CHECK(try_catch.HasCaught());
     875          10 :   CHECK(isolate->IsExecutionTerminating());
     876             : #endif  // V8_INTERPRETED_REGEXP
     877           5 : }
     878             : 
     879       28342 : TEST(TerminateInMicrotask) {
     880           5 :   v8::Isolate* isolate = CcTest::isolate();
     881             :   v8::Locker locker(isolate);
     882           5 :   isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
     883          10 :   v8::HandleScope scope(isolate);
     884             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     885           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     886           5 :   v8::Local<v8::Context> context1 = v8::Context::New(isolate, nullptr, global);
     887           5 :   v8::Local<v8::Context> context2 = v8::Context::New(isolate, nullptr, global);
     888          10 :   v8::TryCatch try_catch(isolate);
     889             :   {
     890             :     v8::Context::Scope context_scope(context1);
     891           5 :     CHECK(!isolate->IsExecutionTerminating());
     892           5 :     CHECK(!CompileRun("Promise.resolve().then(function() {"
     893             :                       "terminate(); loop(); fail();})")
     894             :                .IsEmpty());
     895           5 :     CHECK(!try_catch.HasCaught());
     896             :   }
     897             :   {
     898             :     v8::Context::Scope context_scope(context2);
     899          10 :     CHECK(context2 == isolate->GetCurrentContext());
     900          10 :     CHECK(context2 == isolate->GetEnteredOrMicrotaskContext());
     901           5 :     CHECK(!isolate->IsExecutionTerminating());
     902           5 :     isolate->RunMicrotasks();
     903          10 :     CHECK(context2 == isolate->GetCurrentContext());
     904          10 :     CHECK(context2 == isolate->GetEnteredOrMicrotaskContext());
     905           5 :     CHECK(try_catch.HasCaught());
     906           5 :     CHECK(try_catch.HasTerminated());
     907             :   }
     908          10 :   CHECK(!isolate->IsExecutionTerminating());
     909           5 : }
     910             : 
     911           5 : void TerminationMicrotask(void* data) {
     912           5 :   CcTest::isolate()->TerminateExecution();
     913             :   CompileRun("");
     914           5 : }
     915             : 
     916           0 : void UnreachableMicrotask(void* data) { UNREACHABLE(); }
     917             : 
     918       28342 : TEST(TerminateInApiMicrotask) {
     919           5 :   v8::Isolate* isolate = CcTest::isolate();
     920             :   v8::Locker locker(isolate);
     921           5 :   isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
     922          10 :   v8::HandleScope scope(isolate);
     923             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     924           5 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     925           5 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     926          10 :   v8::TryCatch try_catch(isolate);
     927             :   {
     928             :     v8::Context::Scope context_scope(context);
     929           5 :     CHECK(!isolate->IsExecutionTerminating());
     930           5 :     isolate->EnqueueMicrotask(TerminationMicrotask);
     931           5 :     isolate->EnqueueMicrotask(UnreachableMicrotask);
     932           5 :     isolate->RunMicrotasks();
     933           5 :     CHECK(try_catch.HasCaught());
     934           5 :     CHECK(try_catch.HasTerminated());
     935             :   }
     936          10 :   CHECK(!isolate->IsExecutionTerminating());
     937       85016 : }

Generated by: LCOV version 1.10