LCOV - code coverage report
Current view: top level - test/cctest - test-thread-termination.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 465 469 99.1 %
Date: 2019-04-17 Functions: 45 54 83.3 %

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

Generated by: LCOV version 1.10