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