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