• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "v8.h"
29 #include "platform.h"
30 #include "cctest.h"
31 
32 
33 v8::internal::Semaphore* semaphore = NULL;
34 
35 
Signal(const v8::FunctionCallbackInfo<v8::Value> & args)36 void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
37   semaphore->Signal();
38 }
39 
40 
TerminateCurrentThread(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());
44 }
45 
46 
Fail(const v8::FunctionCallbackInfo<v8::Value> & args)47 void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
48   CHECK(false);
49 }
50 
51 
Loop(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 = v8::String::NewFromUtf8(
55       args.GetIsolate(), "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()));
59 }
60 
61 
DoLoop(const v8::FunctionCallbackInfo<v8::Value> & args)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::NewFromUtf8(args.GetIsolate(),
66                                               "function f() {"
67                                               "  var term = true;"
68                                               "  try {"
69                                               "    while(true) {"
70                                               "      if (term) terminate();"
71                                               "      term = false;"
72                                               "    }"
73                                               "    fail();"
74                                               "  } catch(e) {"
75                                               "    fail();"
76                                               "  }"
77                                               "}"
78                                               "f()"))->Run();
79   CHECK(try_catch.HasCaught());
80   CHECK(try_catch.Exception()->IsNull());
81   CHECK(try_catch.Message().IsEmpty());
82   CHECK(!try_catch.CanContinue());
83   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
84 }
85 
86 
DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value> & args)87 void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
88   v8::TryCatch try_catch;
89   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
90   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
91                                               "var term = true;"
92                                               "while(true) {"
93                                               "  if (term) terminate();"
94                                               "  term = false;"
95                                               "}"))->Run();
96   CHECK(try_catch.HasCaught());
97   CHECK(try_catch.Exception()->IsNull());
98   CHECK(try_catch.Message().IsEmpty());
99   CHECK(!try_catch.CanContinue());
100   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
101 }
102 
103 
CreateGlobalTemplate(v8::Isolate * isolate,v8::FunctionCallback terminate,v8::FunctionCallback doloop)104 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
105     v8::Isolate* isolate,
106     v8::FunctionCallback terminate,
107     v8::FunctionCallback doloop) {
108   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
109   global->Set(v8::String::NewFromUtf8(isolate, "terminate"),
110               v8::FunctionTemplate::New(terminate));
111   global->Set(v8::String::NewFromUtf8(isolate, "fail"),
112               v8::FunctionTemplate::New(Fail));
113   global->Set(v8::String::NewFromUtf8(isolate, "loop"),
114               v8::FunctionTemplate::New(Loop));
115   global->Set(v8::String::NewFromUtf8(isolate, "doloop"),
116               v8::FunctionTemplate::New(doloop));
117   return global;
118 }
119 
120 
121 // Test that a single thread of JavaScript execution can terminate
122 // itself.
TEST(TerminateOnlyV8ThreadFromThreadItself)123 TEST(TerminateOnlyV8ThreadFromThreadItself) {
124   v8::HandleScope scope(CcTest::isolate());
125   v8::Handle<v8::ObjectTemplate> global =
126       CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
127   v8::Handle<v8::Context> context =
128       v8::Context::New(CcTest::isolate(), NULL, global);
129   v8::Context::Scope context_scope(context);
130   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
131   // Run a loop that will be infinite if thread termination does not work.
132   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
133       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
134   v8::Script::Compile(source)->Run();
135   // Test that we can run the code again after thread termination.
136   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
137   v8::Script::Compile(source)->Run();
138 }
139 
140 
141 // Test that a single thread of JavaScript execution can terminate
142 // itself in a loop that performs no calls.
TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop)143 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
144   v8::HandleScope scope(CcTest::isolate());
145   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
146       CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
147   v8::Handle<v8::Context> context =
148       v8::Context::New(CcTest::isolate(), NULL, global);
149   v8::Context::Scope context_scope(context);
150   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
151   // Run a loop that will be infinite if thread termination does not work.
152   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
153       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
154   v8::Script::Compile(source)->Run();
155   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
156   // Test that we can run the code again after thread termination.
157   v8::Script::Compile(source)->Run();
158 }
159 
160 
161 class TerminatorThread : public v8::internal::Thread {
162  public:
TerminatorThread(i::Isolate * isolate)163   explicit TerminatorThread(i::Isolate* isolate)
164       : Thread("TerminatorThread"),
165         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
Run()166   void Run() {
167     semaphore->Wait();
168     CHECK(!v8::V8::IsExecutionTerminating(isolate_));
169     v8::V8::TerminateExecution(isolate_);
170   }
171 
172  private:
173   v8::Isolate* isolate_;
174 };
175 
176 
177 // Test that a single thread of JavaScript execution can be terminated
178 // from the side by another thread.
TEST(TerminateOnlyV8ThreadFromOtherThread)179 TEST(TerminateOnlyV8ThreadFromOtherThread) {
180   semaphore = new v8::internal::Semaphore(0);
181   TerminatorThread thread(CcTest::i_isolate());
182   thread.Start();
183 
184   v8::HandleScope scope(CcTest::isolate());
185   v8::Handle<v8::ObjectTemplate> global =
186       CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
187   v8::Handle<v8::Context> context =
188       v8::Context::New(CcTest::isolate(), NULL, global);
189   v8::Context::Scope context_scope(context);
190   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
191   // Run a loop that will be infinite if thread termination does not work.
192   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
193       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
194   v8::Script::Compile(source)->Run();
195 
196   thread.Join();
197   delete semaphore;
198   semaphore = NULL;
199 }
200 
201 
202 int call_count = 0;
203 
204 
TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value> & args)205 void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
206   if (++call_count == 10) {
207     CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
208     v8::V8::TerminateExecution(args.GetIsolate());
209     return;
210   }
211   v8::Local<v8::Object> result = v8::Object::New();
212   result->Set(v8::String::NewFromUtf8(args.GetIsolate(), "x"),
213               v8::Integer::New(42));
214   args.GetReturnValue().Set(result);
215 }
216 
217 
LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value> & args)218 void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
219   v8::TryCatch try_catch;
220   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
221   v8::Script::Compile(
222       v8::String::NewFromUtf8(args.GetIsolate(),
223                               "function f() {"
224                               "  try {"
225                               "    while(true) {"
226                               "      terminate_or_return_object().x;"
227                               "    }"
228                               "    fail();"
229                               "  } catch(e) {"
230                               "    fail();"
231                               "  }"
232                               "}"
233                               "f()"))->Run();
234   CHECK(try_catch.HasCaught());
235   CHECK(try_catch.Exception()->IsNull());
236   CHECK(try_catch.Message().IsEmpty());
237   CHECK(!try_catch.CanContinue());
238   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
239 }
240 
241 
242 // Test that we correctly handle termination exceptions if they are
243 // triggered by the creation of error objects in connection with ICs.
TEST(TerminateLoadICException)244 TEST(TerminateLoadICException) {
245   v8::HandleScope scope(CcTest::isolate());
246   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
247   global->Set(
248       v8::String::NewFromUtf8(CcTest::isolate(), "terminate_or_return_object"),
249       v8::FunctionTemplate::New(TerminateOrReturnObject));
250   global->Set(v8::String::NewFromUtf8(CcTest::isolate(), "fail"),
251               v8::FunctionTemplate::New(Fail));
252   global->Set(v8::String::NewFromUtf8(CcTest::isolate(), "loop"),
253               v8::FunctionTemplate::New(LoopGetProperty));
254 
255   v8::Handle<v8::Context> context =
256       v8::Context::New(CcTest::isolate(), NULL, global);
257   v8::Context::Scope context_scope(context);
258   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
259   // Run a loop that will be infinite if thread termination does not work.
260   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
261       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
262   call_count = 0;
263   v8::Script::Compile(source)->Run();
264   // Test that we can run the code again after thread termination.
265   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
266   call_count = 0;
267   v8::Script::Compile(source)->Run();
268 }
269 
270 
ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value> & args)271 void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
272   v8::TryCatch try_catch;
273   CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
274   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
275                                               "function f() {"
276                                               "  var term = true;"
277                                               "  try {"
278                                               "    while(true) {"
279                                               "      if (term) terminate();"
280                                               "      term = false;"
281                                               "    }"
282                                               "    fail();"
283                                               "  } catch(e) {"
284                                               "    fail();"
285                                               "  }"
286                                               "}"
287                                               "f()"))->Run();
288   CHECK(try_catch.HasCaught());
289   CHECK(try_catch.Exception()->IsNull());
290   CHECK(try_catch.Message().IsEmpty());
291   CHECK(!try_catch.CanContinue());
292   CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
293   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
294                                               "function f() { fail(); } f()"))
295       ->Run();
296 }
297 
298 
299 // Test that reentry into V8 while the termination exception is still pending
300 // (has not yet unwound the 0-level JS frame) does not crash.
TEST(TerminateAndReenterFromThreadItself)301 TEST(TerminateAndReenterFromThreadItself) {
302   v8::HandleScope scope(CcTest::isolate());
303   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
304       CcTest::isolate(), TerminateCurrentThread, ReenterAfterTermination);
305   v8::Handle<v8::Context> context =
306       v8::Context::New(CcTest::isolate(), NULL, global);
307   v8::Context::Scope context_scope(context);
308   CHECK(!v8::V8::IsExecutionTerminating());
309   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
310       CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
311   v8::Script::Compile(source)->Run();
312   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
313   // Check we can run JS again after termination.
314   CHECK(v8::Script::Compile(
315       v8::String::NewFromUtf8(CcTest::isolate(),
316                               "function f() { return true; }"
317                               "f()"))
318             ->Run()
319             ->IsTrue());
320 }
321 
322 
DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value> & args)323 void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
324   v8::TryCatch try_catch;
325   CHECK(!v8::V8::IsExecutionTerminating());
326   v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
327                                               "var term = true;"
328                                               "while(true) {"
329                                               "  if (term) terminate();"
330                                               "  term = false;"
331                                               "}"
332                                               "fail();"))->Run();
333   CHECK(try_catch.HasCaught());
334   CHECK(try_catch.Exception()->IsNull());
335   CHECK(try_catch.Message().IsEmpty());
336   CHECK(!try_catch.CanContinue());
337   CHECK(v8::V8::IsExecutionTerminating());
338   CHECK(try_catch.HasTerminated());
339   v8::V8::CancelTerminateExecution(CcTest::isolate());
340   CHECK(!v8::V8::IsExecutionTerminating());
341 }
342 
343 
344 // Test that a single thread of JavaScript execution can terminate
345 // itself and then resume execution.
TEST(TerminateCancelTerminateFromThreadItself)346 TEST(TerminateCancelTerminateFromThreadItself) {
347   v8::Isolate* isolate = CcTest::isolate();
348   v8::HandleScope scope(isolate);
349   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
350       isolate, TerminateCurrentThread, DoLoopCancelTerminate);
351   v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
352   v8::Context::Scope context_scope(context);
353   CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
354   v8::Handle<v8::String> source = v8::String::NewFromUtf8(
355       isolate, "try { doloop(); } catch(e) { fail(); } 'completed';");
356   // Check that execution completed with correct return value.
357   CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
358 }
359