blob: 85dfd13b602fa9abb0294f0772cdbc98bed1a6c3 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +000028#include "src/v8.h"
29#include "test/cctest/cctest.h"
30
31#include "src/base/platform/platform.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000032
33
Ben Murdochb8a8cc12014-11-26 15:28:44 +000034v8::base::Semaphore* semaphore = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +000035
36
Ben Murdochb8a8cc12014-11-26 15:28:44 +000037void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +000038 semaphore->Signal();
Steve Blocka7e24c12009-10-30 11:49:00 +000039}
40
41
Ben Murdochb8a8cc12014-11-26 15:28:44 +000042void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000043 CHECK(!args.GetIsolate()->IsExecutionTerminating());
44 args.GetIsolate()->TerminateExecution();
Steve Blocka7e24c12009-10-30 11:49:00 +000045}
46
47
Ben Murdochb8a8cc12014-11-26 15:28:44 +000048void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +000049 CHECK(false);
Steve Blocka7e24c12009-10-30 11:49:00 +000050}
51
52
Ben Murdochb8a8cc12014-11-26 15:28:44 +000053void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000054 CHECK(!args.GetIsolate()->IsExecutionTerminating());
55 v8::MaybeLocal<v8::Value> result =
56 CompileRun(args.GetIsolate()->GetCurrentContext(),
57 "try { doloop(); fail(); } catch(e) { fail(); }");
Steve Block6ded16b2010-05-10 14:33:55 +010058 CHECK(result.IsEmpty());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000059 CHECK(args.GetIsolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +000060}
61
62
Ben Murdochb8a8cc12014-11-26 15:28:44 +000063void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000064 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());
Steve Blocka7e24c12009-10-30 11:49:00 +000082 CHECK(try_catch.HasCaught());
83 CHECK(try_catch.Exception()->IsNull());
84 CHECK(try_catch.Message().IsEmpty());
85 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000086 CHECK(args.GetIsolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +000087}
88
89
Ben Murdochb8a8cc12014-11-26 15:28:44 +000090void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000091 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());
Steve Blockd0582a62009-12-15 09:54:21 +0000101 CHECK(try_catch.HasCaught());
102 CHECK(try_catch.Exception()->IsNull());
103 CHECK(try_catch.Message().IsEmpty());
104 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000105 CHECK(args.GetIsolate()->IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +0000106}
107
108
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109v8::Local<v8::ObjectTemplate> CreateGlobalTemplate(
110 v8::Isolate* isolate, v8::FunctionCallback terminate,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000111 v8::FunctionCallback doloop) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000112 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
113 global->Set(v8_str("terminate"),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000114 v8::FunctionTemplate::New(isolate, terminate));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000115 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));
Steve Blocka7e24c12009-10-30 11:49:00 +0000118 return global;
119}
120
121
122// Test that a single thread of JavaScript execution can terminate
123// itself.
124TEST(TerminateOnlyV8ThreadFromThreadItself) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000125 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000126 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000127 CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000128 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000129 v8::Context::New(CcTest::isolate(), NULL, global);
Steve Blockd0582a62009-12-15 09:54:21 +0000130 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000131 CHECK(!CcTest::isolate()->IsExecutionTerminating());
Steve Blockd0582a62009-12-15 09:54:21 +0000132 // Run a loop that will be infinite if thread termination does not work.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000133 v8::MaybeLocal<v8::Value> result =
134 CompileRun(CcTest::isolate()->GetCurrentContext(),
135 "try { loop(); fail(); } catch(e) { fail(); }");
136 CHECK(result.IsEmpty());
Steve Blockd0582a62009-12-15 09:54:21 +0000137 // Test that we can run the code again after thread termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000138 CHECK(!CcTest::isolate()->IsExecutionTerminating());
139 result = CompileRun(CcTest::isolate()->GetCurrentContext(),
140 "try { loop(); fail(); } catch(e) { fail(); }");
141 CHECK(result.IsEmpty());
Steve Blockd0582a62009-12-15 09:54:21 +0000142}
143
144
145// Test that a single thread of JavaScript execution can terminate
146// itself in a loop that performs no calls.
147TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000148 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000149 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000150 CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000151 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000152 v8::Context::New(CcTest::isolate(), NULL, global);
Steve Blocka7e24c12009-10-30 11:49:00 +0000153 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000154 CHECK(!CcTest::isolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000155 // Run a loop that will be infinite if thread termination does not work.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000156 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());
Steve Blocka7e24c12009-10-30 11:49:00 +0000161 // Test that we can run the code again after thread termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000162 result = CompileRun(CcTest::isolate()->GetCurrentContext(), source);
163 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000164}
165
166
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000167class TerminatorThread : public v8::base::Thread {
Steve Block44f0eee2011-05-26 01:26:41 +0100168 public:
169 explicit TerminatorThread(i::Isolate* isolate)
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000170 : Thread(Options("TerminatorThread")),
171 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {}
Steve Blocka7e24c12009-10-30 11:49:00 +0000172 void Run() {
173 semaphore->Wait();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000174 CHECK(!isolate_->IsExecutionTerminating());
175 isolate_->TerminateExecution();
Steve Blocka7e24c12009-10-30 11:49:00 +0000176 }
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000177
178 private:
179 v8::Isolate* isolate_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000180};
181
182
183// Test that a single thread of JavaScript execution can be terminated
184// from the side by another thread.
185TEST(TerminateOnlyV8ThreadFromOtherThread) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000186 semaphore = new v8::base::Semaphore(0);
187 TerminatorThread thread(CcTest::i_isolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000188 thread.Start();
189
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000190 v8::HandleScope scope(CcTest::isolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000191 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000192 CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000193 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000194 v8::Context::New(CcTest::isolate(), NULL, global);
Steve Blocka7e24c12009-10-30 11:49:00 +0000195 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000196 CHECK(!CcTest::isolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000197 // Run a loop that will be infinite if thread termination does not work.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000198 v8::MaybeLocal<v8::Value> result =
199 CompileRun(CcTest::isolate()->GetCurrentContext(),
200 "try { loop(); fail(); } catch(e) { fail(); }");
201 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000202 thread.Join();
203 delete semaphore;
204 semaphore = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000205}
206
207
208int call_count = 0;
209
210
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000211void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000212 if (++call_count == 10) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000213 CHECK(!args.GetIsolate()->IsExecutionTerminating());
214 args.GetIsolate()->TerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000215 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000216 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000217 v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000218 v8::Maybe<bool> val =
219 result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"),
220 v8::Integer::New(args.GetIsolate(), 42));
221 CHECK(val.FromJust());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000222 args.GetReturnValue().Set(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000223}
224
225
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000226void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000227 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());
Steve Blocka7e24c12009-10-30 11:49:00 +0000244 CHECK(try_catch.HasCaught());
245 CHECK(try_catch.Exception()->IsNull());
246 CHECK(try_catch.Message().IsEmpty());
247 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000248 CHECK(args.GetIsolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000249}
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.
254TEST(TerminateLoadICException) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000255 v8::Isolate* isolate = CcTest::isolate();
256 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000257 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"),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000262 v8::FunctionTemplate::New(isolate, LoopGetProperty));
Steve Blocka7e24c12009-10-30 11:49:00 +0000263
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000264 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Steve Blocka7e24c12009-10-30 11:49:00 +0000265 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000266 CHECK(!isolate->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000267 // Run a loop that will be infinite if thread termination does not work.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000268 static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
Steve Blocka7e24c12009-10-30 11:49:00 +0000269 call_count = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000270 v8::MaybeLocal<v8::Value> result =
271 CompileRun(isolate->GetCurrentContext(), source);
272 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000273 // Test that we can run the code again after thread termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000274 CHECK(!isolate->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000275 call_count = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000276 result = CompileRun(isolate->GetCurrentContext(), source);
277 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000278}
Leon Clarkef7060e22010-06-03 12:02:55 +0100279
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000280
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000281v8::Persistent<v8::String> reenter_script_1;
282v8::Persistent<v8::String> reenter_script_2;
283
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000284void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000285 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());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000292 CHECK(try_catch.HasCaught());
293 CHECK(try_catch.Exception()->IsNull());
294 CHECK(try_catch.Message().IsEmpty());
295 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000296 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());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000301}
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.
306TEST(TerminateAndReenterFromThreadItself) {
307 v8::Isolate* isolate = CcTest::isolate();
308 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000309 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000310 isolate, TerminateCurrentThread, ReenterAfterTermination);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000311 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000312 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000313 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());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000332 // Check we can run JS again after termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000333 CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
334 reenter_script_1.Reset();
335 reenter_script_2.Reset();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000336}
337
338
339void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000340 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());
Leon Clarkef7060e22010-06-03 12:02:55 +0100351 CHECK(try_catch.HasCaught());
352 CHECK(try_catch.Exception()->IsNull());
353 CHECK(try_catch.Message().IsEmpty());
354 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000355 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000356 CHECK(try_catch.HasTerminated());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000357 CcTest::isolate()->CancelTerminateExecution();
358 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
Leon Clarkef7060e22010-06-03 12:02:55 +0100359}
360
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000361
362// Test that a single thread of JavaScript execution can terminate
363// itself and then resume execution.
364TEST(TerminateCancelTerminateFromThreadItself) {
365 v8::Isolate* isolate = CcTest::isolate();
366 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000367 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000368 isolate, TerminateCurrentThread, DoLoopCancelTerminate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000369 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Leon Clarkef7060e22010-06-03 12:02:55 +0100370 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000371 CHECK(!CcTest::isolate()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000372 // Check that execution completed with correct return value.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000373 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());
Leon Clarkef7060e22010-06-03 12:02:55 +0100379}
380
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000381
382void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
383 CHECK(false);
384}
385
386
387void 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.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000392 isolate->EnqueueMicrotask(
393 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
394 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000395 CompileRun("terminate(); while (true) { }");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000396 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000397}
398
399
400TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
401 semaphore = new v8::base::Semaphore(0);
402 TerminatorThread thread(CcTest::i_isolate());
403 thread.Start();
404
405 v8::Isolate* isolate = CcTest::isolate();
Ben Murdochda12d292016-06-02 14:46:10 +0100406 isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000407 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000408 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000409 CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000410 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000411 v8::Context::New(CcTest::isolate(), NULL, global);
412 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000413 isolate->EnqueueMicrotask(
414 v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever)
415 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000416 // The second task should never be run because we bail out if we're
417 // terminating.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000418 isolate->EnqueueMicrotask(
419 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
420 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000421 isolate->RunMicrotasks();
422
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000423 isolate->CancelTerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000424 isolate->RunMicrotasks(); // should not run MicrotaskShouldNotRun
425
426 thread.Join();
427 delete semaphore;
428 semaphore = NULL;
429}
430
431
432static int callback_counter = 0;
433
434
435static void CounterCallback(v8::Isolate* isolate, void* data) {
436 callback_counter++;
437}
438
439
440TEST(PostponeTerminateException) {
441 v8::Isolate* isolate = CcTest::isolate();
442 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000443 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000444 CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000445 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000446 v8::Context::New(CcTest::isolate(), NULL, global);
447 v8::Context::Scope context_scope(context);
448
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000449 v8::TryCatch try_catch(isolate);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000450 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(),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000455 i::StackGuard::TERMINATE_EXECUTION);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000456
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
488TEST(ErrorObjectAfterTermination) {
489 v8::Isolate* isolate = CcTest::isolate();
490 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000491 v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000492 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000493 isolate->TerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000494 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}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000498
499
500void 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
516TEST(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
539TEST(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}