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 208 int call_count = 0; 209 210 211 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) { 212 if (++call_count == 10) { 213 CHECK(!args.GetIsolate()->IsExecutionTerminating()); 214 args.GetIsolate()->TerminateExecution(); 215 return; 216 } 217 v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate()); 218 v8::Maybe<bool> val = 219 result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"), 220 v8::Integer::New(args.GetIsolate(), 42)); 221 CHECK(val.FromJust()); 222 args.GetReturnValue().Set(result); 223 } 224 225 226 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) { 227 v8::TryCatch try_catch(args.GetIsolate()); 228 CHECK(!args.GetIsolate()->IsExecutionTerminating()); 229 v8::MaybeLocal<v8::Value> result = 230 CompileRun(args.GetIsolate()->GetCurrentContext(), 231 "function f() {" 232 " try {" 233 " while(true) {" 234 " terminate_or_return_object().x;" 235 " }" 236 " fail();" 237 " } catch(e) {" 238 " (function() {})();" // trigger stack check. 239 " fail();" 240 " }" 241 "}" 242 "f()"); 243 CHECK(result.IsEmpty()); 244 CHECK(try_catch.HasCaught()); 245 CHECK(try_catch.Exception()->IsNull()); 246 CHECK(try_catch.Message().IsEmpty()); 247 CHECK(!try_catch.CanContinue()); 248 CHECK(args.GetIsolate()->IsExecutionTerminating()); 249 } 250 251 252 // Test that we correctly handle termination exceptions if they are 253 // triggered by the creation of error objects in connection with ICs. 254 TEST(TerminateLoadICException) { 255 v8::Isolate* isolate = CcTest::isolate(); 256 v8::HandleScope scope(isolate); 257 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); 258 global->Set(v8_str("terminate_or_return_object"), 259 v8::FunctionTemplate::New(isolate, TerminateOrReturnObject)); 260 global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail)); 261 global->Set(v8_str("loop"), 262 v8::FunctionTemplate::New(isolate, LoopGetProperty)); 263 264 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); 265 v8::Context::Scope context_scope(context); 266 CHECK(!isolate->IsExecutionTerminating()); 267 // Run a loop that will be infinite if thread termination does not work. 268 static const char* source = "try { loop(); fail(); } catch(e) { fail(); }"; 269 call_count = 0; 270 v8::MaybeLocal<v8::Value> result = 271 CompileRun(isolate->GetCurrentContext(), source); 272 CHECK(result.IsEmpty()); 273 // Test that we can run the code again after thread termination. 274 CHECK(!isolate->IsExecutionTerminating()); 275 call_count = 0; 276 result = CompileRun(isolate->GetCurrentContext(), source); 277 CHECK(result.IsEmpty()); 278 } 279 280 281 v8::Persistent<v8::String> reenter_script_1; 282 v8::Persistent<v8::String> reenter_script_2; 283 284 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) { 285 v8::TryCatch try_catch(args.GetIsolate()); 286 v8::Isolate* isolate = args.GetIsolate(); 287 CHECK(!isolate->IsExecutionTerminating()); 288 v8::Local<v8::String> script = 289 v8::Local<v8::String>::New(isolate, reenter_script_1); 290 v8::MaybeLocal<v8::Value> result = CompileRun(script); 291 CHECK(result.IsEmpty()); 292 CHECK(try_catch.HasCaught()); 293 CHECK(try_catch.Exception()->IsNull()); 294 CHECK(try_catch.Message().IsEmpty()); 295 CHECK(!try_catch.CanContinue()); 296 CHECK(isolate->IsExecutionTerminating()); 297 script = v8::Local<v8::String>::New(isolate, reenter_script_2); 298 v8::MaybeLocal<v8::Script> compiled_script = 299 v8::Script::Compile(isolate->GetCurrentContext(), script); 300 CHECK(compiled_script.IsEmpty()); 301 } 302 303 304 // Test that reentry into V8 while the termination exception is still pending 305 // (has not yet unwound the 0-level JS frame) does not crash. 306 TEST(TerminateAndReenterFromThreadItself) { 307 v8::Isolate* isolate = CcTest::isolate(); 308 v8::HandleScope scope(isolate); 309 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( 310 isolate, TerminateCurrentThread, ReenterAfterTermination); 311 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); 312 v8::Context::Scope context_scope(context); 313 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); 314 // Create script strings upfront as it won't work when terminating. 315 reenter_script_1.Reset(isolate, v8_str( 316 "function f() {" 317 " var term = true;" 318 " try {" 319 " while(true) {" 320 " if (term) terminate();" 321 " term = false;" 322 " }" 323 " fail();" 324 " } catch(e) {" 325 " fail();" 326 " }" 327 "}" 328 "f()")); 329 reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()")); 330 CompileRun("try { loop(); fail(); } catch(e) { fail(); }"); 331 CHECK(!isolate->IsExecutionTerminating()); 332 // Check we can run JS again after termination. 333 CHECK(CompileRun("function f() { return true; } f()")->IsTrue()); 334 reenter_script_1.Reset(); 335 reenter_script_2.Reset(); 336 } 337 338 339 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { 340 v8::TryCatch try_catch(args.GetIsolate()); 341 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); 342 v8::MaybeLocal<v8::Value> result = 343 CompileRun(args.GetIsolate()->GetCurrentContext(), 344 "var term = true;" 345 "while(true) {" 346 " if (term) terminate();" 347 " term = false;" 348 "}" 349 "fail();"); 350 CHECK(result.IsEmpty()); 351 CHECK(try_catch.HasCaught()); 352 CHECK(try_catch.Exception()->IsNull()); 353 CHECK(try_catch.Message().IsEmpty()); 354 CHECK(!try_catch.CanContinue()); 355 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating()); 356 CHECK(try_catch.HasTerminated()); 357 CcTest::isolate()->CancelTerminateExecution(); 358 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); 359 } 360 361 362 // Test that a single thread of JavaScript execution can terminate 363 // itself and then resume execution. 364 TEST(TerminateCancelTerminateFromThreadItself) { 365 v8::Isolate* isolate = CcTest::isolate(); 366 v8::HandleScope scope(isolate); 367 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( 368 isolate, TerminateCurrentThread, DoLoopCancelTerminate); 369 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); 370 v8::Context::Scope context_scope(context); 371 CHECK(!CcTest::isolate()->IsExecutionTerminating()); 372 // Check that execution completed with correct return value. 373 v8::Local<v8::Value> result = 374 CompileRun(isolate->GetCurrentContext(), 375 "try { doloop(); } catch(e) { fail(); } 'completed';") 376 .ToLocalChecked(); 377 CHECK(result->Equals(isolate->GetCurrentContext(), v8_str("completed")) 378 .FromJust()); 379 } 380 381 382 void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) { 383 CHECK(false); 384 } 385 386 387 void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) { 388 v8::Isolate* isolate = info.GetIsolate(); 389 v8::HandleScope scope(isolate); 390 // Enqueue another should-not-run task to ensure we clean out the queue 391 // when we terminate. 392 isolate->EnqueueMicrotask( 393 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun) 394 .ToLocalChecked()); 395 CompileRun("terminate(); while (true) { }"); 396 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating()); 397 } 398 399 400 TEST(TerminateFromOtherThreadWhileMicrotaskRunning) { 401 semaphore = new v8::base::Semaphore(0); 402 TerminatorThread thread(CcTest::i_isolate()); 403 thread.Start(); 404 405 v8::Isolate* isolate = CcTest::isolate(); 406 isolate->SetAutorunMicrotasks(false); 407 v8::HandleScope scope(isolate); 408 v8::Local<v8::ObjectTemplate> global = 409 CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop); 410 v8::Local<v8::Context> context = 411 v8::Context::New(CcTest::isolate(), NULL, global); 412 v8::Context::Scope context_scope(context); 413 isolate->EnqueueMicrotask( 414 v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever) 415 .ToLocalChecked()); 416 // The second task should never be run because we bail out if we're 417 // terminating. 418 isolate->EnqueueMicrotask( 419 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun) 420 .ToLocalChecked()); 421 isolate->RunMicrotasks(); 422 423 isolate->CancelTerminateExecution(); 424 isolate->RunMicrotasks(); // should not run MicrotaskShouldNotRun 425 426 thread.Join(); 427 delete semaphore; 428 semaphore = NULL; 429 } 430 431 432 static int callback_counter = 0; 433 434 435 static void CounterCallback(v8::Isolate* isolate, void* data) { 436 callback_counter++; 437 } 438 439 440 TEST(PostponeTerminateException) { 441 v8::Isolate* isolate = CcTest::isolate(); 442 v8::HandleScope scope(isolate); 443 v8::Local<v8::ObjectTemplate> global = 444 CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop); 445 v8::Local<v8::Context> context = 446 v8::Context::New(CcTest::isolate(), NULL, global); 447 v8::Context::Scope context_scope(context); 448 449 v8::TryCatch try_catch(isolate); 450 static const char* terminate_and_loop = 451 "terminate(); for (var i = 0; i < 10000; i++);"; 452 453 { // Postpone terminate execution interrupts. 454 i::PostponeInterruptsScope p1(CcTest::i_isolate(), 455 i::StackGuard::TERMINATE_EXECUTION); 456 457 // API interrupts should still be triggered. 458 CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL); 459 CHECK_EQ(0, callback_counter); 460 CompileRun(terminate_and_loop); 461 CHECK(!try_catch.HasTerminated()); 462 CHECK_EQ(1, callback_counter); 463 464 { // Postpone API interrupts as well. 465 i::PostponeInterruptsScope p2(CcTest::i_isolate(), 466 i::StackGuard::API_INTERRUPT); 467 468 // None of the two interrupts should trigger. 469 CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL); 470 CompileRun(terminate_and_loop); 471 CHECK(!try_catch.HasTerminated()); 472 CHECK_EQ(1, callback_counter); 473 } 474 475 // Now the previously requested API interrupt should trigger. 476 CompileRun(terminate_and_loop); 477 CHECK(!try_catch.HasTerminated()); 478 CHECK_EQ(2, callback_counter); 479 } 480 481 // Now the previously requested terminate execution interrupt should trigger. 482 CompileRun("for (var i = 0; i < 10000; i++);"); 483 CHECK(try_catch.HasTerminated()); 484 CHECK_EQ(2, callback_counter); 485 } 486 487 488 TEST(ErrorObjectAfterTermination) { 489 v8::Isolate* isolate = CcTest::isolate(); 490 v8::HandleScope scope(isolate); 491 v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate()); 492 v8::Context::Scope context_scope(context); 493 isolate->TerminateExecution(); 494 v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error")); 495 // TODO(yangguo): crbug/403509. Check for empty handle instead. 496 CHECK(error->IsUndefined()); 497 } 498 499 500 void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { 501 CHECK(!args.GetIsolate()->IsExecutionTerminating()); 502 v8::Local<v8::Object> global = CcTest::global(); 503 v8::Local<v8::Function> loop = v8::Local<v8::Function>::Cast( 504 global->Get(CcTest::isolate()->GetCurrentContext(), v8_str("loop")) 505 .ToLocalChecked()); 506 i::MaybeHandle<i::Object> result = 507 i::Execution::TryCall(CcTest::i_isolate(), v8::Utils::OpenHandle((*loop)), 508 v8::Utils::OpenHandle((*global)), 0, NULL, NULL); 509 CHECK(result.is_null()); 510 // TryCall ignores terminate execution, but rerequests the interrupt. 511 CHECK(!args.GetIsolate()->IsExecutionTerminating()); 512 CHECK(CompileRun("1 + 1;").IsEmpty()); 513 } 514 515 516 TEST(TerminationInInnerTryCall) { 517 v8::Isolate* isolate = CcTest::isolate(); 518 v8::HandleScope scope(isolate); 519 v8::Local<v8::ObjectTemplate> global_template = CreateGlobalTemplate( 520 CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall); 521 global_template->Set( 522 v8_str("inner_try_call_terminate"), 523 v8::FunctionTemplate::New(isolate, InnerTryCallTerminate)); 524 v8::Local<v8::Context> context = 525 v8::Context::New(CcTest::isolate(), NULL, global_template); 526 v8::Context::Scope context_scope(context); 527 { 528 v8::TryCatch try_catch(isolate); 529 CompileRun("inner_try_call_terminate()"); 530 CHECK(try_catch.HasTerminated()); 531 } 532 v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value( 533 v8::Isolate::GetCurrent()->GetCurrentContext()); 534 CHECK_EQ(4, result.FromJust()); 535 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); 536 } 537 538 539 TEST(TerminateAndTryCall) { 540 i::FLAG_allow_natives_syntax = true; 541 v8::Isolate* isolate = CcTest::isolate(); 542 v8::HandleScope scope(isolate); 543 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( 544 isolate, TerminateCurrentThread, DoLoopCancelTerminate); 545 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); 546 v8::Context::Scope context_scope(context); 547 CHECK(!isolate->IsExecutionTerminating()); 548 v8::TryCatch try_catch(isolate); 549 CHECK(!isolate->IsExecutionTerminating()); 550 // Terminate execution has been triggered inside TryCall, but re-requested 551 // to trigger later. 552 CHECK(CompileRun("terminate(); reference_error();").IsEmpty()); 553 CHECK(try_catch.HasCaught()); 554 CHECK(!isolate->IsExecutionTerminating()); 555 v8::Local<v8::Value> value = 556 CcTest::global() 557 ->Get(isolate->GetCurrentContext(), v8_str("terminate")) 558 .ToLocalChecked(); 559 CHECK(value->IsFunction()); 560 // The first stack check after terminate has been re-requested fails. 561 CHECK(CompileRun("1 + 1").IsEmpty()); 562 CHECK(!isolate->IsExecutionTerminating()); 563 // V8 then recovers. 564 v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value( 565 v8::Isolate::GetCurrent()->GetCurrentContext()); 566 CHECK_EQ(4, result.FromJust()); 567 CHECK(!isolate->IsExecutionTerminating()); 568 } 569