• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Signal(const v8::FunctionCallbackInfo<v8::Value> & args)37 void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
38   semaphore->Signal();
39 }
40 
41 
TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value> & args)42 void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
43   CHECK(!args.GetIsolate()->IsExecutionTerminating());
44   args.GetIsolate()->TerminateExecution();
45 }
46 
47 
Fail(const v8::FunctionCallbackInfo<v8::Value> & args)48 void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
49   CHECK(false);
50 }
51 
52 
Loop(const v8::FunctionCallbackInfo<v8::Value> & args)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 
DoLoop(const v8::FunctionCallbackInfo<v8::Value> & args)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 
DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value> & args)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 
CreateGlobalTemplate(v8::Isolate * isolate,v8::FunctionCallback terminate,v8::FunctionCallback doloop)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.
TEST(TerminateOnlyV8ThreadFromThreadItself)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.
TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop)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:
TerminatorThread(i::Isolate * isolate)169   explicit TerminatorThread(i::Isolate* isolate)
170       : Thread(Options("TerminatorThread")),
171         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {}
Run()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.
TEST(TerminateOnlyV8ThreadFromOtherThread)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 
TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value> & args)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 
LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value> & args)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.
TEST(TerminateLoadICException)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 
ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value> & args)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.
TEST(TerminateAndReenterFromThreadItself)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 
DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)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.
TEST(TerminateCancelTerminateFromThreadItself)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 
MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value> & info)382 void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
383   CHECK(false);
384 }
385 
386 
MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value> & info)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 
TEST(TerminateFromOtherThreadWhileMicrotaskRunning)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 
CounterCallback(v8::Isolate * isolate,void * data)435 static void CounterCallback(v8::Isolate* isolate, void* data) {
436   callback_counter++;
437 }
438 
439 
TEST(PostponeTerminateException)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 
TEST(ErrorObjectAfterTermination)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 
InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)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 
TEST(TerminationInInnerTryCall)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 
TEST(TerminateAndTryCall)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