LCOV - code coverage report
Current view: top level - test/cctest - test-thread-termination.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 304 309 98.4 %
Date: 2017-10-20 Functions: 30 36 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.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          18 : void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
      39          18 :   semaphore->Signal();
      40          18 : }
      41             : 
      42             : 
      43         144 : void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
      44          72 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      45          72 :   args.GetIsolate()->TerminateExecution();
      46          72 : }
      47             : 
      48             : 
      49           0 : void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
      50           0 :   CHECK(false);
      51             : }
      52             : 
      53             : 
      54         126 : void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
      55          42 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      56             :   v8::MaybeLocal<v8::Value> result =
      57             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      58          42 :                  "try { doloop(); fail(); } catch(e) { fail(); }");
      59          42 :   CHECK(result.IsEmpty());
      60          42 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
      61          42 : }
      62             : 
      63             : 
      64          72 : void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
      65          18 :   v8::TryCatch try_catch(args.GetIsolate());
      66          18 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      67             :   v8::MaybeLocal<v8::Value> result =
      68             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      69             :                  "function f() {"
      70             :                  "  var term = true;"
      71             :                  "  try {"
      72             :                  "    while(true) {"
      73             :                  "      if (term) terminate();"
      74             :                  "      term = false;"
      75             :                  "    }"
      76             :                  "    fail();"
      77             :                  "  } catch(e) {"
      78             :                  "    fail();"
      79             :                  "  }"
      80             :                  "}"
      81          18 :                  "f()");
      82          18 :   CHECK(result.IsEmpty());
      83          18 :   CHECK(try_catch.HasCaught());
      84          36 :   CHECK(try_catch.Exception()->IsNull());
      85          36 :   CHECK(try_catch.Message().IsEmpty());
      86          18 :   CHECK(!try_catch.CanContinue());
      87          18 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
      88          18 : }
      89             : 
      90             : 
      91          72 : void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
      92          18 :   v8::TryCatch try_catch(args.GetIsolate());
      93          18 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
      94             :   v8::MaybeLocal<v8::Value> result =
      95             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
      96             :                  "var term = true;"
      97             :                  "while(true) {"
      98             :                  "  if (term) terminate();"
      99             :                  "  term = false;"
     100          18 :                  "}");
     101          18 :   CHECK(result.IsEmpty());
     102          18 :   CHECK(try_catch.HasCaught());
     103          36 :   CHECK(try_catch.Exception()->IsNull());
     104          36 :   CHECK(try_catch.Message().IsEmpty());
     105          18 :   CHECK(!try_catch.CanContinue());
     106          18 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
     107          18 : }
     108             : 
     109             : 
     110          66 : v8::Local<v8::ObjectTemplate> CreateGlobalTemplate(
     111             :     v8::Isolate* isolate, v8::FunctionCallback terminate,
     112             :     v8::FunctionCallback doloop) {
     113          66 :   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
     114             :   global->Set(v8_str("terminate"),
     115         198 :               v8::FunctionTemplate::New(isolate, terminate));
     116         198 :   global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
     117         198 :   global->Set(v8_str("loop"), v8::FunctionTemplate::New(isolate, Loop));
     118         198 :   global->Set(v8_str("doloop"), v8::FunctionTemplate::New(isolate, doloop));
     119          66 :   return global;
     120             : }
     121             : 
     122             : 
     123             : // Test that a single thread of JavaScript execution can terminate
     124             : // itself.
     125       23724 : TEST(TerminateOnlyV8ThreadFromThreadItself) {
     126           6 :   v8::HandleScope scope(CcTest::isolate());
     127             :   v8::Local<v8::ObjectTemplate> global =
     128           6 :       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
     129             :   v8::Local<v8::Context> context =
     130           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     131             :   v8::Context::Scope context_scope(context);
     132           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     133             :   // Run a loop that will be infinite if thread termination does not work.
     134             :   v8::MaybeLocal<v8::Value> result =
     135             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     136           6 :                  "try { loop(); fail(); } catch(e) { fail(); }");
     137           6 :   CHECK(result.IsEmpty());
     138             :   // Test that we can run the code again after thread termination.
     139           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     140             :   result = CompileRun(CcTest::isolate()->GetCurrentContext(),
     141           6 :                       "try { loop(); fail(); } catch(e) { fail(); }");
     142          12 :   CHECK(result.IsEmpty());
     143           6 : }
     144             : 
     145             : 
     146             : // Test that a single thread of JavaScript execution can terminate
     147             : // itself in a loop that performs no calls.
     148       23724 : TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
     149           6 :   v8::HandleScope scope(CcTest::isolate());
     150             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     151           6 :       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
     152             :   v8::Local<v8::Context> context =
     153           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     154             :   v8::Context::Scope context_scope(context);
     155           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     156             :   // Run a loop that will be infinite if thread termination does not work.
     157             :   static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
     158             :   v8::MaybeLocal<v8::Value> result =
     159           6 :       CompileRun(CcTest::isolate()->GetCurrentContext(), source);
     160           6 :   CHECK(result.IsEmpty());
     161           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     162             :   // Test that we can run the code again after thread termination.
     163           6 :   result = CompileRun(CcTest::isolate()->GetCurrentContext(), source);
     164          12 :   CHECK(result.IsEmpty());
     165           6 : }
     166             : 
     167             : 
     168          18 : class TerminatorThread : public v8::base::Thread {
     169             :  public:
     170             :   explicit TerminatorThread(i::Isolate* isolate)
     171             :       : Thread(Options("TerminatorThread")),
     172          18 :         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {}
     173          18 :   void Run() {
     174          18 :     semaphore->Wait();
     175          18 :     CHECK(!isolate_->IsExecutionTerminating());
     176          18 :     isolate_->TerminateExecution();
     177          18 :   }
     178             : 
     179             :  private:
     180             :   v8::Isolate* isolate_;
     181             : };
     182             : 
     183             : 
     184             : // Test that a single thread of JavaScript execution can be terminated
     185             : // from the side by another thread.
     186       23724 : TEST(TerminateOnlyV8ThreadFromOtherThread) {
     187           6 :   semaphore = new v8::base::Semaphore(0);
     188             :   TerminatorThread thread(CcTest::i_isolate());
     189           6 :   thread.Start();
     190             : 
     191          12 :   v8::HandleScope scope(CcTest::isolate());
     192             :   v8::Local<v8::ObjectTemplate> global =
     193           6 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     194             :   v8::Local<v8::Context> context =
     195           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     196             :   v8::Context::Scope context_scope(context);
     197           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     198             :   // Run a loop that will be infinite if thread termination does not work.
     199             :   v8::MaybeLocal<v8::Value> result =
     200             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     201           6 :                  "try { loop(); fail(); } catch(e) { fail(); }");
     202           6 :   CHECK(result.IsEmpty());
     203           6 :   thread.Join();
     204           6 :   delete semaphore;
     205           6 :   semaphore = nullptr;
     206           6 : }
     207             : 
     208             : // Test that execution can be terminated from within JSON.stringify.
     209       23724 : TEST(TerminateJsonStringify) {
     210           6 :   semaphore = new v8::base::Semaphore(0);
     211             :   TerminatorThread thread(CcTest::i_isolate());
     212           6 :   thread.Start();
     213             : 
     214          12 :   v8::HandleScope scope(CcTest::isolate());
     215             :   v8::Local<v8::ObjectTemplate> global =
     216           6 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     217             :   v8::Local<v8::Context> context =
     218           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     219             :   v8::Context::Scope context_scope(context);
     220           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     221             :   v8::MaybeLocal<v8::Value> result =
     222             :       CompileRun(CcTest::isolate()->GetCurrentContext(),
     223             :                  "var x = [];"
     224             :                  "x[2**31]=1;"
     225             :                  "terminate();"
     226             :                  "JSON.stringify(x);"
     227           6 :                  "fail();");
     228           6 :   CHECK(result.IsEmpty());
     229           6 :   thread.Join();
     230           6 :   delete semaphore;
     231           6 :   semaphore = nullptr;
     232           6 : }
     233             : 
     234             : int call_count = 0;
     235             : 
     236             : 
     237         576 : void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
     238         120 :   if (++call_count == 10) {
     239          12 :     CHECK(!args.GetIsolate()->IsExecutionTerminating());
     240          12 :     args.GetIsolate()->TerminateExecution();
     241         132 :     return;
     242             :   }
     243         108 :   v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
     244             :   v8::Maybe<bool> val =
     245             :       result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"),
     246         432 :                   v8::Integer::New(args.GetIsolate(), 42));
     247         108 :   CHECK(val.FromJust());
     248             :   args.GetReturnValue().Set(result);
     249             : }
     250             : 
     251             : 
     252          48 : void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
     253          12 :   v8::TryCatch try_catch(args.GetIsolate());
     254          12 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     255             :   v8::MaybeLocal<v8::Value> result =
     256             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
     257             :                  "function f() {"
     258             :                  "  try {"
     259             :                  "    while(true) {"
     260             :                  "      terminate_or_return_object().x;"
     261             :                  "    }"
     262             :                  "    fail();"
     263             :                  "  } catch(e) {"
     264             :                  "    (function() {})();"  // trigger stack check.
     265             :                  "    fail();"
     266             :                  "  }"
     267             :                  "}"
     268          12 :                  "f()");
     269          12 :   CHECK(result.IsEmpty());
     270          12 :   CHECK(try_catch.HasCaught());
     271          24 :   CHECK(try_catch.Exception()->IsNull());
     272          24 :   CHECK(try_catch.Message().IsEmpty());
     273          12 :   CHECK(!try_catch.CanContinue());
     274          12 :   CHECK(args.GetIsolate()->IsExecutionTerminating());
     275          12 : }
     276             : 
     277             : 
     278             : // Test that we correctly handle termination exceptions if they are
     279             : // triggered by the creation of error objects in connection with ICs.
     280       23724 : TEST(TerminateLoadICException) {
     281           6 :   v8::Isolate* isolate = CcTest::isolate();
     282           6 :   v8::HandleScope scope(isolate);
     283           6 :   v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
     284             :   global->Set(v8_str("terminate_or_return_object"),
     285          18 :               v8::FunctionTemplate::New(isolate, TerminateOrReturnObject));
     286          18 :   global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
     287             :   global->Set(v8_str("loop"),
     288          18 :               v8::FunctionTemplate::New(isolate, LoopGetProperty));
     289             : 
     290           6 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     291             :   v8::Context::Scope context_scope(context);
     292           6 :   CHECK(!isolate->IsExecutionTerminating());
     293             :   // Run a loop that will be infinite if thread termination does not work.
     294             :   static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
     295           6 :   call_count = 0;
     296             :   v8::MaybeLocal<v8::Value> result =
     297           6 :       CompileRun(isolate->GetCurrentContext(), source);
     298           6 :   CHECK(result.IsEmpty());
     299             :   // Test that we can run the code again after thread termination.
     300           6 :   CHECK(!isolate->IsExecutionTerminating());
     301           6 :   call_count = 0;
     302           6 :   result = CompileRun(isolate->GetCurrentContext(), source);
     303          12 :   CHECK(result.IsEmpty());
     304           6 : }
     305             : 
     306             : 
     307       23718 : v8::Persistent<v8::String> reenter_script_1;
     308       23718 : v8::Persistent<v8::String> reenter_script_2;
     309             : 
     310          12 : void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
     311           6 :   v8::TryCatch try_catch(args.GetIsolate());
     312             :   v8::Isolate* isolate = args.GetIsolate();
     313           6 :   CHECK(!isolate->IsExecutionTerminating());
     314             :   v8::Local<v8::String> script =
     315           6 :       v8::Local<v8::String>::New(isolate, reenter_script_1);
     316           6 :   v8::MaybeLocal<v8::Value> result = CompileRun(script);
     317           6 :   CHECK(result.IsEmpty());
     318           6 :   CHECK(try_catch.HasCaught());
     319          12 :   CHECK(try_catch.Exception()->IsNull());
     320          12 :   CHECK(try_catch.Message().IsEmpty());
     321           6 :   CHECK(!try_catch.CanContinue());
     322           6 :   CHECK(isolate->IsExecutionTerminating());
     323           6 :   script = v8::Local<v8::String>::New(isolate, reenter_script_2);
     324             :   v8::MaybeLocal<v8::Script> compiled_script =
     325           6 :       v8::Script::Compile(isolate->GetCurrentContext(), script);
     326           6 :   CHECK(compiled_script.IsEmpty());
     327           6 : }
     328             : 
     329             : 
     330             : // Test that reentry into V8 while the termination exception is still pending
     331             : // (has not yet unwound the 0-level JS frame) does not crash.
     332       23724 : TEST(TerminateAndReenterFromThreadItself) {
     333           6 :   v8::Isolate* isolate = CcTest::isolate();
     334           6 :   v8::HandleScope scope(isolate);
     335             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     336           6 :       isolate, TerminateCurrentThread, ReenterAfterTermination);
     337           6 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     338             :   v8::Context::Scope context_scope(context);
     339           6 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     340             :   // Create script strings upfront as it won't work when terminating.
     341             :   reenter_script_1.Reset(isolate, v8_str(
     342             :                                       "function f() {"
     343             :                                       "  var term = true;"
     344             :                                       "  try {"
     345             :                                       "    while(true) {"
     346             :                                       "      if (term) terminate();"
     347             :                                       "      term = false;"
     348             :                                       "    }"
     349             :                                       "    fail();"
     350             :                                       "  } catch(e) {"
     351             :                                       "    fail();"
     352             :                                       "  }"
     353             :                                       "}"
     354          12 :                                       "f()"));
     355          12 :   reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()"));
     356             :   CompileRun("try { loop(); fail(); } catch(e) { fail(); }");
     357           6 :   CHECK(!isolate->IsExecutionTerminating());
     358             :   // Check we can run JS again after termination.
     359           6 :   CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
     360             :   reenter_script_1.Reset();
     361           6 :   reenter_script_2.Reset();
     362           6 : }
     363             : 
     364             : 
     365          12 : void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
     366           6 :   v8::TryCatch try_catch(args.GetIsolate());
     367           6 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     368             :   v8::MaybeLocal<v8::Value> result =
     369             :       CompileRun(args.GetIsolate()->GetCurrentContext(),
     370             :                  "var term = true;"
     371             :                  "while(true) {"
     372             :                  "  if (term) terminate();"
     373             :                  "  term = false;"
     374             :                  "}"
     375           6 :                  "fail();");
     376           6 :   CHECK(result.IsEmpty());
     377           6 :   CHECK(try_catch.HasCaught());
     378          12 :   CHECK(try_catch.Exception()->IsNull());
     379          12 :   CHECK(try_catch.Message().IsEmpty());
     380           6 :   CHECK(!try_catch.CanContinue());
     381           6 :   CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
     382           6 :   CHECK(try_catch.HasTerminated());
     383           6 :   CcTest::isolate()->CancelTerminateExecution();
     384           6 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     385           6 : }
     386             : 
     387             : 
     388             : // Test that a single thread of JavaScript execution can terminate
     389             : // itself and then resume execution.
     390       23724 : TEST(TerminateCancelTerminateFromThreadItself) {
     391           6 :   v8::Isolate* isolate = CcTest::isolate();
     392           6 :   v8::HandleScope scope(isolate);
     393             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     394           6 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     395           6 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     396             :   v8::Context::Scope context_scope(context);
     397           6 :   CHECK(!CcTest::isolate()->IsExecutionTerminating());
     398             :   // Check that execution completed with correct return value.
     399             :   v8::Local<v8::Value> result =
     400             :       CompileRun(isolate->GetCurrentContext(),
     401           6 :                  "try { doloop(); } catch(e) { fail(); } 'completed';")
     402           6 :           .ToLocalChecked();
     403          18 :   CHECK(result->Equals(isolate->GetCurrentContext(), v8_str("completed"))
     404           6 :             .FromJust());
     405           6 : }
     406             : 
     407             : 
     408           0 : void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
     409           0 :   CHECK(false);
     410             : }
     411             : 
     412             : 
     413           6 : void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) {
     414             :   v8::Isolate* isolate = info.GetIsolate();
     415           6 :   v8::HandleScope scope(isolate);
     416             :   // Enqueue another should-not-run task to ensure we clean out the queue
     417             :   // when we terminate.
     418             :   isolate->EnqueueMicrotask(
     419          12 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
     420          12 :           .ToLocalChecked());
     421             :   CompileRun("terminate(); while (true) { }");
     422           6 :   CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
     423           6 : }
     424             : 
     425             : 
     426       23724 : TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
     427           6 :   semaphore = new v8::base::Semaphore(0);
     428             :   TerminatorThread thread(CcTest::i_isolate());
     429           6 :   thread.Start();
     430             : 
     431           6 :   v8::Isolate* isolate = CcTest::isolate();
     432           6 :   isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
     433          12 :   v8::HandleScope scope(isolate);
     434             :   v8::Local<v8::ObjectTemplate> global =
     435           6 :       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
     436             :   v8::Local<v8::Context> context =
     437           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     438             :   v8::Context::Scope context_scope(context);
     439             :   isolate->EnqueueMicrotask(
     440          12 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever)
     441          12 :           .ToLocalChecked());
     442             :   // The second task should never be run because we bail out if we're
     443             :   // terminating.
     444             :   isolate->EnqueueMicrotask(
     445          12 :       v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
     446          12 :           .ToLocalChecked());
     447           6 :   isolate->RunMicrotasks();
     448             : 
     449           6 :   isolate->CancelTerminateExecution();
     450           6 :   isolate->RunMicrotasks();  // should not run MicrotaskShouldNotRun
     451             : 
     452           6 :   thread.Join();
     453           6 :   delete semaphore;
     454           6 :   semaphore = nullptr;
     455           6 : }
     456             : 
     457             : 
     458             : static int callback_counter = 0;
     459             : 
     460             : 
     461          12 : static void CounterCallback(v8::Isolate* isolate, void* data) {
     462          12 :   callback_counter++;
     463          12 : }
     464             : 
     465             : 
     466       23724 : TEST(PostponeTerminateException) {
     467           6 :   v8::Isolate* isolate = CcTest::isolate();
     468           6 :   v8::HandleScope scope(isolate);
     469             :   v8::Local<v8::ObjectTemplate> global =
     470           6 :       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
     471             :   v8::Local<v8::Context> context =
     472           6 :       v8::Context::New(CcTest::isolate(), nullptr, global);
     473             :   v8::Context::Scope context_scope(context);
     474             : 
     475          12 :   v8::TryCatch try_catch(isolate);
     476             :   static const char* terminate_and_loop =
     477             :       "terminate(); for (var i = 0; i < 10000; i++);";
     478             : 
     479             :   { // Postpone terminate execution interrupts.
     480             :     i::PostponeInterruptsScope p1(CcTest::i_isolate(),
     481             :                                   i::StackGuard::TERMINATE_EXECUTION);
     482             : 
     483             :     // API interrupts should still be triggered.
     484           6 :     CcTest::isolate()->RequestInterrupt(&CounterCallback, nullptr);
     485           6 :     CHECK_EQ(0, callback_counter);
     486           6 :     CompileRun(terminate_and_loop);
     487           6 :     CHECK(!try_catch.HasTerminated());
     488           6 :     CHECK_EQ(1, callback_counter);
     489             : 
     490             :     { // Postpone API interrupts as well.
     491             :       i::PostponeInterruptsScope p2(CcTest::i_isolate(),
     492             :                                     i::StackGuard::API_INTERRUPT);
     493             : 
     494             :       // None of the two interrupts should trigger.
     495           6 :       CcTest::isolate()->RequestInterrupt(&CounterCallback, nullptr);
     496           6 :       CompileRun(terminate_and_loop);
     497           6 :       CHECK(!try_catch.HasTerminated());
     498           6 :       CHECK_EQ(1, callback_counter);
     499             :     }
     500             : 
     501             :     // Now the previously requested API interrupt should trigger.
     502           6 :     CompileRun(terminate_and_loop);
     503           6 :     CHECK(!try_catch.HasTerminated());
     504           6 :     CHECK_EQ(2, callback_counter);
     505             :   }
     506             : 
     507             :   // Now the previously requested terminate execution interrupt should trigger.
     508             :   CompileRun("for (var i = 0; i < 10000; i++);");
     509           6 :   CHECK(try_catch.HasTerminated());
     510          12 :   CHECK_EQ(2, callback_counter);
     511           6 : }
     512             : 
     513             : 
     514       23724 : TEST(ErrorObjectAfterTermination) {
     515           6 :   v8::Isolate* isolate = CcTest::isolate();
     516           6 :   v8::HandleScope scope(isolate);
     517           6 :   v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
     518             :   v8::Context::Scope context_scope(context);
     519           6 :   isolate->TerminateExecution();
     520           6 :   v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
     521          12 :   CHECK(error->IsNativeError());
     522           6 : }
     523             : 
     524             : 
     525          12 : void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
     526           6 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     527           6 :   v8::Local<v8::Object> global = CcTest::global();
     528             :   v8::Local<v8::Function> loop = v8::Local<v8::Function>::Cast(
     529          18 :       global->Get(CcTest::isolate()->GetCurrentContext(), v8_str("loop"))
     530           6 :           .ToLocalChecked());
     531             :   i::MaybeHandle<i::Object> exception;
     532             :   i::MaybeHandle<i::Object> result =
     533             :       i::Execution::TryCall(CcTest::i_isolate(), v8::Utils::OpenHandle((*loop)),
     534             :                             v8::Utils::OpenHandle((*global)), 0, nullptr,
     535           6 :                             i::Execution::MessageHandling::kReport, &exception);
     536           6 :   CHECK(result.is_null());
     537             :   // TryCall ignores terminate execution, but rerequests the interrupt.
     538           6 :   CHECK(!args.GetIsolate()->IsExecutionTerminating());
     539           6 :   CHECK(CompileRun("1 + 1;").IsEmpty());
     540           6 : }
     541             : 
     542             : 
     543       23724 : TEST(TerminationInInnerTryCall) {
     544           6 :   v8::Isolate* isolate = CcTest::isolate();
     545           6 :   v8::HandleScope scope(isolate);
     546             :   v8::Local<v8::ObjectTemplate> global_template = CreateGlobalTemplate(
     547           6 :       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
     548             :   global_template->Set(
     549             :       v8_str("inner_try_call_terminate"),
     550          18 :       v8::FunctionTemplate::New(isolate, InnerTryCallTerminate));
     551             :   v8::Local<v8::Context> context =
     552           6 :       v8::Context::New(CcTest::isolate(), nullptr, global_template);
     553             :   v8::Context::Scope context_scope(context);
     554             :   {
     555           6 :     v8::TryCatch try_catch(isolate);
     556             :     CompileRun("inner_try_call_terminate()");
     557           6 :     CHECK(try_catch.HasTerminated());
     558             :   }
     559             :   v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
     560          12 :       v8::Isolate::GetCurrent()->GetCurrentContext());
     561           6 :   CHECK_EQ(4, result.FromJust());
     562          12 :   CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
     563           6 : }
     564             : 
     565             : 
     566       23724 : TEST(TerminateAndTryCall) {
     567           6 :   i::FLAG_allow_natives_syntax = true;
     568           6 :   v8::Isolate* isolate = CcTest::isolate();
     569           6 :   v8::HandleScope scope(isolate);
     570             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     571           6 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     572           6 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     573             :   v8::Context::Scope context_scope(context);
     574           6 :   CHECK(!isolate->IsExecutionTerminating());
     575          12 :   v8::TryCatch try_catch(isolate);
     576           6 :   CHECK(!isolate->IsExecutionTerminating());
     577             :   // Terminate execution has been triggered inside TryCall, but re-requested
     578             :   // to trigger later.
     579           6 :   CHECK(CompileRun("terminate(); reference_error();").IsEmpty());
     580           6 :   CHECK(try_catch.HasCaught());
     581           6 :   CHECK(!isolate->IsExecutionTerminating());
     582             :   v8::Local<v8::Value> value =
     583             :       CcTest::global()
     584          18 :           ->Get(isolate->GetCurrentContext(), v8_str("terminate"))
     585           6 :           .ToLocalChecked();
     586           6 :   CHECK(value->IsFunction());
     587             :   // The first stack check after terminate has been re-requested fails.
     588           6 :   CHECK(CompileRun("1 + 1").IsEmpty());
     589           6 :   CHECK(!isolate->IsExecutionTerminating());
     590             :   // V8 then recovers.
     591             :   v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
     592          12 :       v8::Isolate::GetCurrent()->GetCurrentContext());
     593           6 :   CHECK_EQ(4, result.FromJust());
     594          12 :   CHECK(!isolate->IsExecutionTerminating());
     595           6 : }
     596             : 
     597           0 : class ConsoleImpl : public v8::debug::ConsoleDelegate {
     598             :  private:
     599           6 :   void Log(const v8::debug::ConsoleCallArguments& args,
     600             :            const v8::debug::ConsoleContext&) override {
     601             :     CompileRun("1 + 1");
     602           6 :   }
     603             : };
     604             : 
     605       23724 : TEST(TerminateConsole) {
     606           6 :   i::FLAG_allow_natives_syntax = true;
     607           6 :   v8::Isolate* isolate = CcTest::isolate();
     608           6 :   ConsoleImpl console;
     609           6 :   v8::debug::SetConsoleDelegate(isolate, &console);
     610          12 :   v8::HandleScope scope(isolate);
     611             :   v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
     612           6 :       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
     613           6 :   v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
     614             :   v8::Context::Scope context_scope(context);
     615           6 :   CHECK(!isolate->IsExecutionTerminating());
     616          12 :   v8::TryCatch try_catch(isolate);
     617           6 :   CHECK(!isolate->IsExecutionTerminating());
     618           6 :   CHECK(CompileRun("terminate(); console.log(); fail();").IsEmpty());
     619           6 :   CHECK(try_catch.HasCaught());
     620           6 :   CHECK(!isolate->IsExecutionTerminating());
     621       71160 : }

Generated by: LCOV version 1.10