blob: 9d3cbf61960a2a0db75d4b1fb1dac3b1fcedd3f2 [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
Ben Murdoch61f157c2016-09-16 13:49:30 +0100207// Test that execution can be terminated from within JSON.stringify.
208TEST(TerminateJsonStringify) {
209 semaphore = new v8::base::Semaphore(0);
210 TerminatorThread thread(CcTest::i_isolate());
211 thread.Start();
212
213 v8::HandleScope scope(CcTest::isolate());
214 v8::Local<v8::ObjectTemplate> global =
215 CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
216 v8::Local<v8::Context> context =
217 v8::Context::New(CcTest::isolate(), NULL, global);
218 v8::Context::Scope context_scope(context);
219 CHECK(!CcTest::isolate()->IsExecutionTerminating());
220 v8::MaybeLocal<v8::Value> result =
221 CompileRun(CcTest::isolate()->GetCurrentContext(),
222 "var x = [];"
223 "x[2**31]=1;"
224 "terminate();"
225 "JSON.stringify(x);"
226 "fail();");
227 CHECK(result.IsEmpty());
228 thread.Join();
229 delete semaphore;
230 semaphore = NULL;
231}
Steve Blocka7e24c12009-10-30 11:49:00 +0000232
233int call_count = 0;
234
235
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000236void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000237 if (++call_count == 10) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000238 CHECK(!args.GetIsolate()->IsExecutionTerminating());
239 args.GetIsolate()->TerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000240 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000241 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000242 v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000243 v8::Maybe<bool> val =
244 result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"),
245 v8::Integer::New(args.GetIsolate(), 42));
246 CHECK(val.FromJust());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000247 args.GetReturnValue().Set(result);
Steve Blocka7e24c12009-10-30 11:49:00 +0000248}
249
250
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000251void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000252 v8::TryCatch try_catch(args.GetIsolate());
253 CHECK(!args.GetIsolate()->IsExecutionTerminating());
254 v8::MaybeLocal<v8::Value> result =
255 CompileRun(args.GetIsolate()->GetCurrentContext(),
256 "function f() {"
257 " try {"
258 " while(true) {"
259 " terminate_or_return_object().x;"
260 " }"
261 " fail();"
262 " } catch(e) {"
263 " (function() {})();" // trigger stack check.
264 " fail();"
265 " }"
266 "}"
267 "f()");
268 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000269 CHECK(try_catch.HasCaught());
270 CHECK(try_catch.Exception()->IsNull());
271 CHECK(try_catch.Message().IsEmpty());
272 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000273 CHECK(args.GetIsolate()->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000274}
275
276
277// Test that we correctly handle termination exceptions if they are
278// triggered by the creation of error objects in connection with ICs.
279TEST(TerminateLoadICException) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000280 v8::Isolate* isolate = CcTest::isolate();
281 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000282 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
283 global->Set(v8_str("terminate_or_return_object"),
284 v8::FunctionTemplate::New(isolate, TerminateOrReturnObject));
285 global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail));
286 global->Set(v8_str("loop"),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000287 v8::FunctionTemplate::New(isolate, LoopGetProperty));
Steve Blocka7e24c12009-10-30 11:49:00 +0000288
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000289 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Steve Blocka7e24c12009-10-30 11:49:00 +0000290 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000291 CHECK(!isolate->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000292 // Run a loop that will be infinite if thread termination does not work.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000293 static const char* source = "try { loop(); fail(); } catch(e) { fail(); }";
Steve Blocka7e24c12009-10-30 11:49:00 +0000294 call_count = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000295 v8::MaybeLocal<v8::Value> result =
296 CompileRun(isolate->GetCurrentContext(), source);
297 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000298 // Test that we can run the code again after thread termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000299 CHECK(!isolate->IsExecutionTerminating());
Steve Blocka7e24c12009-10-30 11:49:00 +0000300 call_count = 0;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000301 result = CompileRun(isolate->GetCurrentContext(), source);
302 CHECK(result.IsEmpty());
Steve Blocka7e24c12009-10-30 11:49:00 +0000303}
Leon Clarkef7060e22010-06-03 12:02:55 +0100304
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000305
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000306v8::Persistent<v8::String> reenter_script_1;
307v8::Persistent<v8::String> reenter_script_2;
308
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000309void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000310 v8::TryCatch try_catch(args.GetIsolate());
311 v8::Isolate* isolate = args.GetIsolate();
312 CHECK(!isolate->IsExecutionTerminating());
313 v8::Local<v8::String> script =
314 v8::Local<v8::String>::New(isolate, reenter_script_1);
315 v8::MaybeLocal<v8::Value> result = CompileRun(script);
316 CHECK(result.IsEmpty());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000317 CHECK(try_catch.HasCaught());
318 CHECK(try_catch.Exception()->IsNull());
319 CHECK(try_catch.Message().IsEmpty());
320 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000321 CHECK(isolate->IsExecutionTerminating());
322 script = v8::Local<v8::String>::New(isolate, reenter_script_2);
323 v8::MaybeLocal<v8::Script> compiled_script =
324 v8::Script::Compile(isolate->GetCurrentContext(), script);
325 CHECK(compiled_script.IsEmpty());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000326}
327
328
329// Test that reentry into V8 while the termination exception is still pending
330// (has not yet unwound the 0-level JS frame) does not crash.
331TEST(TerminateAndReenterFromThreadItself) {
332 v8::Isolate* isolate = CcTest::isolate();
333 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000334 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000335 isolate, TerminateCurrentThread, ReenterAfterTermination);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000336 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000337 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000338 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
339 // Create script strings upfront as it won't work when terminating.
340 reenter_script_1.Reset(isolate, v8_str(
341 "function f() {"
342 " var term = true;"
343 " try {"
344 " while(true) {"
345 " if (term) terminate();"
346 " term = false;"
347 " }"
348 " fail();"
349 " } catch(e) {"
350 " fail();"
351 " }"
352 "}"
353 "f()"));
354 reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()"));
355 CompileRun("try { loop(); fail(); } catch(e) { fail(); }");
356 CHECK(!isolate->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000357 // Check we can run JS again after termination.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000358 CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
359 reenter_script_1.Reset();
360 reenter_script_2.Reset();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000361}
362
363
364void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000365 v8::TryCatch try_catch(args.GetIsolate());
366 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
367 v8::MaybeLocal<v8::Value> result =
368 CompileRun(args.GetIsolate()->GetCurrentContext(),
369 "var term = true;"
370 "while(true) {"
371 " if (term) terminate();"
372 " term = false;"
373 "}"
374 "fail();");
375 CHECK(result.IsEmpty());
Leon Clarkef7060e22010-06-03 12:02:55 +0100376 CHECK(try_catch.HasCaught());
377 CHECK(try_catch.Exception()->IsNull());
378 CHECK(try_catch.Message().IsEmpty());
379 CHECK(!try_catch.CanContinue());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000380 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000381 CHECK(try_catch.HasTerminated());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000382 CcTest::isolate()->CancelTerminateExecution();
383 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
Leon Clarkef7060e22010-06-03 12:02:55 +0100384}
385
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000386
387// Test that a single thread of JavaScript execution can terminate
388// itself and then resume execution.
389TEST(TerminateCancelTerminateFromThreadItself) {
390 v8::Isolate* isolate = CcTest::isolate();
391 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000392 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000393 isolate, TerminateCurrentThread, DoLoopCancelTerminate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000394 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
Leon Clarkef7060e22010-06-03 12:02:55 +0100395 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000396 CHECK(!CcTest::isolate()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000397 // Check that execution completed with correct return value.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000398 v8::Local<v8::Value> result =
399 CompileRun(isolate->GetCurrentContext(),
400 "try { doloop(); } catch(e) { fail(); } 'completed';")
401 .ToLocalChecked();
402 CHECK(result->Equals(isolate->GetCurrentContext(), v8_str("completed"))
403 .FromJust());
Leon Clarkef7060e22010-06-03 12:02:55 +0100404}
405
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000406
407void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
408 CHECK(false);
409}
410
411
412void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) {
413 v8::Isolate* isolate = info.GetIsolate();
414 v8::HandleScope scope(isolate);
415 // Enqueue another should-not-run task to ensure we clean out the queue
416 // when we terminate.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000417 isolate->EnqueueMicrotask(
418 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
419 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000420 CompileRun("terminate(); while (true) { }");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000421 CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000422}
423
424
425TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
426 semaphore = new v8::base::Semaphore(0);
427 TerminatorThread thread(CcTest::i_isolate());
428 thread.Start();
429
430 v8::Isolate* isolate = CcTest::isolate();
Ben Murdochda12d292016-06-02 14:46:10 +0100431 isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000432 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000433 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000434 CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000435 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000436 v8::Context::New(CcTest::isolate(), NULL, global);
437 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000438 isolate->EnqueueMicrotask(
439 v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever)
440 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000441 // The second task should never be run because we bail out if we're
442 // terminating.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000443 isolate->EnqueueMicrotask(
444 v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun)
445 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000446 isolate->RunMicrotasks();
447
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000448 isolate->CancelTerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000449 isolate->RunMicrotasks(); // should not run MicrotaskShouldNotRun
450
451 thread.Join();
452 delete semaphore;
453 semaphore = NULL;
454}
455
456
457static int callback_counter = 0;
458
459
460static void CounterCallback(v8::Isolate* isolate, void* data) {
461 callback_counter++;
462}
463
464
465TEST(PostponeTerminateException) {
466 v8::Isolate* isolate = CcTest::isolate();
467 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000468 v8::Local<v8::ObjectTemplate> global =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000469 CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000470 v8::Local<v8::Context> context =
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000471 v8::Context::New(CcTest::isolate(), NULL, global);
472 v8::Context::Scope context_scope(context);
473
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000474 v8::TryCatch try_catch(isolate);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000475 static const char* terminate_and_loop =
476 "terminate(); for (var i = 0; i < 10000; i++);";
477
478 { // Postpone terminate execution interrupts.
479 i::PostponeInterruptsScope p1(CcTest::i_isolate(),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000480 i::StackGuard::TERMINATE_EXECUTION);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000481
482 // API interrupts should still be triggered.
483 CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
484 CHECK_EQ(0, callback_counter);
485 CompileRun(terminate_and_loop);
486 CHECK(!try_catch.HasTerminated());
487 CHECK_EQ(1, callback_counter);
488
489 { // Postpone API interrupts as well.
490 i::PostponeInterruptsScope p2(CcTest::i_isolate(),
491 i::StackGuard::API_INTERRUPT);
492
493 // None of the two interrupts should trigger.
494 CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
495 CompileRun(terminate_and_loop);
496 CHECK(!try_catch.HasTerminated());
497 CHECK_EQ(1, callback_counter);
498 }
499
500 // Now the previously requested API interrupt should trigger.
501 CompileRun(terminate_and_loop);
502 CHECK(!try_catch.HasTerminated());
503 CHECK_EQ(2, callback_counter);
504 }
505
506 // Now the previously requested terminate execution interrupt should trigger.
507 CompileRun("for (var i = 0; i < 10000; i++);");
508 CHECK(try_catch.HasTerminated());
509 CHECK_EQ(2, callback_counter);
510}
511
512
513TEST(ErrorObjectAfterTermination) {
514 v8::Isolate* isolate = CcTest::isolate();
515 v8::HandleScope scope(isolate);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000516 v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000517 v8::Context::Scope context_scope(context);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000518 isolate->TerminateExecution();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000519 v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
520 // TODO(yangguo): crbug/403509. Check for empty handle instead.
521 CHECK(error->IsUndefined());
522}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000523
524
525void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
526 CHECK(!args.GetIsolate()->IsExecutionTerminating());
527 v8::Local<v8::Object> global = CcTest::global();
528 v8::Local<v8::Function> loop = v8::Local<v8::Function>::Cast(
529 global->Get(CcTest::isolate()->GetCurrentContext(), v8_str("loop"))
530 .ToLocalChecked());
531 i::MaybeHandle<i::Object> result =
532 i::Execution::TryCall(CcTest::i_isolate(), v8::Utils::OpenHandle((*loop)),
533 v8::Utils::OpenHandle((*global)), 0, NULL, NULL);
534 CHECK(result.is_null());
535 // TryCall ignores terminate execution, but rerequests the interrupt.
536 CHECK(!args.GetIsolate()->IsExecutionTerminating());
537 CHECK(CompileRun("1 + 1;").IsEmpty());
538}
539
540
541TEST(TerminationInInnerTryCall) {
542 v8::Isolate* isolate = CcTest::isolate();
543 v8::HandleScope scope(isolate);
544 v8::Local<v8::ObjectTemplate> global_template = CreateGlobalTemplate(
545 CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
546 global_template->Set(
547 v8_str("inner_try_call_terminate"),
548 v8::FunctionTemplate::New(isolate, InnerTryCallTerminate));
549 v8::Local<v8::Context> context =
550 v8::Context::New(CcTest::isolate(), NULL, global_template);
551 v8::Context::Scope context_scope(context);
552 {
553 v8::TryCatch try_catch(isolate);
554 CompileRun("inner_try_call_terminate()");
555 CHECK(try_catch.HasTerminated());
556 }
557 v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
558 v8::Isolate::GetCurrent()->GetCurrentContext());
559 CHECK_EQ(4, result.FromJust());
560 CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating());
561}
562
563
564TEST(TerminateAndTryCall) {
565 i::FLAG_allow_natives_syntax = true;
566 v8::Isolate* isolate = CcTest::isolate();
567 v8::HandleScope scope(isolate);
568 v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate(
569 isolate, TerminateCurrentThread, DoLoopCancelTerminate);
570 v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global);
571 v8::Context::Scope context_scope(context);
572 CHECK(!isolate->IsExecutionTerminating());
573 v8::TryCatch try_catch(isolate);
574 CHECK(!isolate->IsExecutionTerminating());
575 // Terminate execution has been triggered inside TryCall, but re-requested
576 // to trigger later.
577 CHECK(CompileRun("terminate(); reference_error();").IsEmpty());
578 CHECK(try_catch.HasCaught());
579 CHECK(!isolate->IsExecutionTerminating());
580 v8::Local<v8::Value> value =
581 CcTest::global()
582 ->Get(isolate->GetCurrentContext(), v8_str("terminate"))
583 .ToLocalChecked();
584 CHECK(value->IsFunction());
585 // The first stack check after terminate has been re-requested fails.
586 CHECK(CompileRun("1 + 1").IsEmpty());
587 CHECK(!isolate->IsExecutionTerminating());
588 // V8 then recovers.
589 v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value(
590 v8::Isolate::GetCurrent()->GetCurrentContext());
591 CHECK_EQ(4, result.FromJust());
592 CHECK(!isolate->IsExecutionTerminating());
593}