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