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
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.
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.
33 v8::internal::Semaphore* semaphore = NULL;
36 void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
41 void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
42 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
43 v8::V8::TerminateExecution(args.GetIsolate());
47 void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
52 void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
53 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
54 v8::Handle<v8::String> source =
55 v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
56 v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
57 CHECK(result.IsEmpty());
58 CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
62 void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
63 v8::TryCatch try_catch;
64 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
65 v8::Script::Compile(v8::String::New("function f() {"
69 " if (term) terminate();"
78 CHECK(try_catch.HasCaught());
79 CHECK(try_catch.Exception()->IsNull());
80 CHECK(try_catch.Message().IsEmpty());
81 CHECK(!try_catch.CanContinue());
82 CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
86 void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
87 v8::TryCatch try_catch;
88 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
89 v8::Script::Compile(v8::String::New("var term = true;"
91 " if (term) terminate();"
94 CHECK(try_catch.HasCaught());
95 CHECK(try_catch.Exception()->IsNull());
96 CHECK(try_catch.Message().IsEmpty());
97 CHECK(!try_catch.CanContinue());
98 CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
102 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
103 v8::FunctionCallback terminate,
104 v8::FunctionCallback doloop) {
105 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
106 global->Set(v8::String::New("terminate"),
107 v8::FunctionTemplate::New(terminate));
108 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
109 global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
110 global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
115 // Test that a single thread of JavaScript execution can terminate
117 TEST(TerminateOnlyV8ThreadFromThreadItself) {
118 v8::HandleScope scope(CcTest::isolate());
119 v8::Handle<v8::ObjectTemplate> global =
120 CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
121 v8::Handle<v8::Context> context =
122 v8::Context::New(CcTest::isolate(), NULL, global);
123 v8::Context::Scope context_scope(context);
124 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
125 // Run a loop that will be infinite if thread termination does not work.
126 v8::Handle<v8::String> source =
127 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
128 v8::Script::Compile(source)->Run();
129 // Test that we can run the code again after thread termination.
130 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
131 v8::Script::Compile(source)->Run();
135 // Test that a single thread of JavaScript execution can terminate
136 // itself in a loop that performs no calls.
137 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
138 v8::HandleScope scope(CcTest::isolate());
139 v8::Handle<v8::ObjectTemplate> global =
140 CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
141 v8::Handle<v8::Context> context =
142 v8::Context::New(CcTest::isolate(), NULL, global);
143 v8::Context::Scope context_scope(context);
144 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
145 // Run a loop that will be infinite if thread termination does not work.
146 v8::Handle<v8::String> source =
147 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
148 v8::Script::Compile(source)->Run();
149 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
150 // Test that we can run the code again after thread termination.
151 v8::Script::Compile(source)->Run();
155 class TerminatorThread : public v8::internal::Thread {
157 explicit TerminatorThread(i::Isolate* isolate)
158 : Thread("TerminatorThread"),
159 isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
162 CHECK(!v8::V8::IsExecutionTerminating(isolate_));
163 v8::V8::TerminateExecution(isolate_);
167 v8::Isolate* isolate_;
171 // Test that a single thread of JavaScript execution can be terminated
172 // from the side by another thread.
173 TEST(TerminateOnlyV8ThreadFromOtherThread) {
174 semaphore = new v8::internal::Semaphore(0);
175 TerminatorThread thread(CcTest::i_isolate());
178 v8::HandleScope scope(CcTest::isolate());
179 v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
180 v8::Handle<v8::Context> context =
181 v8::Context::New(CcTest::isolate(), NULL, global);
182 v8::Context::Scope context_scope(context);
183 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
184 // Run a loop that will be infinite if thread termination does not work.
185 v8::Handle<v8::String> source =
186 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
187 v8::Script::Compile(source)->Run();
198 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
199 if (++call_count == 10) {
200 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
201 v8::V8::TerminateExecution(args.GetIsolate());
204 v8::Local<v8::Object> result = v8::Object::New();
205 result->Set(v8::String::New("x"), v8::Integer::New(42));
206 args.GetReturnValue().Set(result);
210 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
211 v8::TryCatch try_catch;
212 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
213 v8::Script::Compile(v8::String::New("function f() {"
216 " terminate_or_return_object().x;"
224 CHECK(try_catch.HasCaught());
225 CHECK(try_catch.Exception()->IsNull());
226 CHECK(try_catch.Message().IsEmpty());
227 CHECK(!try_catch.CanContinue());
228 CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
232 // Test that we correctly handle termination exceptions if they are
233 // triggered by the creation of error objects in connection with ICs.
234 TEST(TerminateLoadICException) {
235 v8::HandleScope scope(CcTest::isolate());
236 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
237 global->Set(v8::String::New("terminate_or_return_object"),
238 v8::FunctionTemplate::New(TerminateOrReturnObject));
239 global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
240 global->Set(v8::String::New("loop"),
241 v8::FunctionTemplate::New(LoopGetProperty));
243 v8::Handle<v8::Context> context =
244 v8::Context::New(CcTest::isolate(), NULL, global);
245 v8::Context::Scope context_scope(context);
246 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
247 // Run a loop that will be infinite if thread termination does not work.
248 v8::Handle<v8::String> source =
249 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
251 v8::Script::Compile(source)->Run();
252 // Test that we can run the code again after thread termination.
253 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
255 v8::Script::Compile(source)->Run();
259 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
260 v8::TryCatch try_catch;
261 CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
262 v8::Script::Compile(v8::String::New("function f() {"
266 " if (term) terminate();"
275 CHECK(try_catch.HasCaught());
276 CHECK(try_catch.Exception()->IsNull());
277 CHECK(try_catch.Message().IsEmpty());
278 CHECK(!try_catch.CanContinue());
279 CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
280 v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
284 // Test that reentry into V8 while the termination exception is still pending
285 // (has not yet unwound the 0-level JS frame) does not crash.
286 TEST(TerminateAndReenterFromThreadItself) {
287 v8::HandleScope scope(CcTest::isolate());
288 v8::Handle<v8::ObjectTemplate> global =
289 CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
290 v8::Handle<v8::Context> context =
291 v8::Context::New(CcTest::isolate(), NULL, global);
292 v8::Context::Scope context_scope(context);
293 CHECK(!v8::V8::IsExecutionTerminating());
294 v8::Handle<v8::String> source =
295 v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
296 v8::Script::Compile(source)->Run();
297 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
298 // Check we can run JS again after termination.
299 CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
300 "f()"))->Run()->IsTrue());
304 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
305 v8::TryCatch try_catch;
306 CHECK(!v8::V8::IsExecutionTerminating());
307 v8::Script::Compile(v8::String::New("var term = true;"
309 " if (term) terminate();"
313 CHECK(try_catch.HasCaught());
314 CHECK(try_catch.Exception()->IsNull());
315 CHECK(try_catch.Message().IsEmpty());
316 CHECK(!try_catch.CanContinue());
317 CHECK(v8::V8::IsExecutionTerminating());
318 CHECK(try_catch.HasTerminated());
319 v8::V8::CancelTerminateExecution(CcTest::isolate());
320 CHECK(!v8::V8::IsExecutionTerminating());
324 // Test that a single thread of JavaScript execution can terminate
325 // itself and then resume execution.
326 TEST(TerminateCancelTerminateFromThreadItself) {
327 v8::Isolate* isolate = CcTest::isolate();
328 v8::HandleScope scope(isolate);
329 v8::Handle<v8::ObjectTemplate> global =
330 CreateGlobalTemplate(TerminateCurrentThread, DoLoopCancelTerminate);
331 v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
332 v8::Context::Scope context_scope(context);
333 CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
334 v8::Handle<v8::String> source =
335 v8::String::New("try { doloop(); } catch(e) { fail(); } 'completed';");
336 // Check that execution completed with correct return value.
337 CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));