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