Home | History | Annotate | Download | only in cctest
      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 
     36 v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
     37   semaphore->Signal();
     38   return v8::Undefined();
     39 }
     40 
     41 
     42 v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
     43   CHECK(!v8::V8::IsExecutionTerminating());
     44   v8::V8::TerminateExecution();
     45   return v8::Undefined();
     46 }
     47 
     48 
     49 v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
     50   CHECK(false);
     51   return v8::Undefined();
     52 }
     53 
     54 
     55 v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
     56   CHECK(!v8::V8::IsExecutionTerminating());
     57   v8::Handle<v8::String> source =
     58       v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
     59   v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
     60   CHECK(result.IsEmpty());
     61   CHECK(v8::V8::IsExecutionTerminating());
     62   return v8::Undefined();
     63 }
     64 
     65 
     66 v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
     67   v8::TryCatch try_catch;
     68   CHECK(!v8::V8::IsExecutionTerminating());
     69   v8::Script::Compile(v8::String::New("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                                       "f()"))->Run();
     82   CHECK(try_catch.HasCaught());
     83   CHECK(try_catch.Exception()->IsNull());
     84   CHECK(try_catch.Message().IsEmpty());
     85   CHECK(!try_catch.CanContinue());
     86   CHECK(v8::V8::IsExecutionTerminating());
     87   return v8::Undefined();
     88 }
     89 
     90 
     91 v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
     92   v8::TryCatch try_catch;
     93   CHECK(!v8::V8::IsExecutionTerminating());
     94   v8::Script::Compile(v8::String::New("var term = true;"
     95                                       "while(true) {"
     96                                       "  if (term) terminate();"
     97                                       "  term = false;"
     98                                       "}"))->Run();
     99   CHECK(try_catch.HasCaught());
    100   CHECK(try_catch.Exception()->IsNull());
    101   CHECK(try_catch.Message().IsEmpty());
    102   CHECK(!try_catch.CanContinue());
    103   CHECK(v8::V8::IsExecutionTerminating());
    104   return v8::Undefined();
    105 }
    106 
    107 
    108 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
    109     v8::InvocationCallback terminate,
    110     v8::InvocationCallback doloop) {
    111   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    112   global->Set(v8::String::New("terminate"),
    113               v8::FunctionTemplate::New(terminate));
    114   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
    115   global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
    116   global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
    117   return global;
    118 }
    119 
    120 
    121 // Test that a single thread of JavaScript execution can terminate
    122 // itself.
    123 TEST(TerminateOnlyV8ThreadFromThreadItself) {
    124   v8::HandleScope scope;
    125   v8::Handle<v8::ObjectTemplate> global =
    126       CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
    127   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    128   v8::Context::Scope context_scope(context);
    129   CHECK(!v8::V8::IsExecutionTerminating());
    130   // Run a loop that will be infinite if thread termination does not work.
    131   v8::Handle<v8::String> source =
    132       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    133   v8::Script::Compile(source)->Run();
    134   // Test that we can run the code again after thread termination.
    135   CHECK(!v8::V8::IsExecutionTerminating());
    136   v8::Script::Compile(source)->Run();
    137   context.Dispose();
    138 }
    139 
    140 
    141 // Test that a single thread of JavaScript execution can terminate
    142 // itself in a loop that performs no calls.
    143 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
    144   v8::HandleScope scope;
    145   v8::Handle<v8::ObjectTemplate> global =
    146       CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
    147   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    148   v8::Context::Scope context_scope(context);
    149   CHECK(!v8::V8::IsExecutionTerminating());
    150   // Run a loop that will be infinite if thread termination does not work.
    151   v8::Handle<v8::String> source =
    152       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    153   v8::Script::Compile(source)->Run();
    154   CHECK(!v8::V8::IsExecutionTerminating());
    155   // Test that we can run the code again after thread termination.
    156   v8::Script::Compile(source)->Run();
    157   context.Dispose();
    158 }
    159 
    160 
    161 class TerminatorThread : public v8::internal::Thread {
    162  public:
    163   explicit TerminatorThread(i::Isolate* isolate)
    164       : Thread("TerminatorThread"),
    165         isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
    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.
    179 TEST(TerminateOnlyV8ThreadFromOtherThread) {
    180   semaphore = v8::internal::OS::CreateSemaphore(0);
    181   TerminatorThread thread(i::Isolate::Current());
    182   thread.Start();
    183 
    184   v8::HandleScope scope;
    185   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
    186   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    187   v8::Context::Scope context_scope(context);
    188   CHECK(!v8::V8::IsExecutionTerminating());
    189   // Run a loop that will be infinite if thread termination does not work.
    190   v8::Handle<v8::String> source =
    191       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    192   v8::Script::Compile(source)->Run();
    193 
    194   thread.Join();
    195   delete semaphore;
    196   semaphore = NULL;
    197   context.Dispose();
    198 }
    199 
    200 
    201 class LoopingThread : public v8::internal::Thread {
    202  public:
    203   LoopingThread() : Thread("LoopingThread") { }
    204   void Run() {
    205     v8::Locker locker;
    206     v8::HandleScope scope;
    207     v8_thread_id_ = v8::V8::GetCurrentThreadId();
    208     v8::Handle<v8::ObjectTemplate> global =
    209         CreateGlobalTemplate(Signal, DoLoop);
    210     v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    211     v8::Context::Scope context_scope(context);
    212     CHECK(!v8::V8::IsExecutionTerminating());
    213     // Run a loop that will be infinite if thread termination does not work.
    214     v8::Handle<v8::String> source =
    215         v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    216     v8::Script::Compile(source)->Run();
    217     context.Dispose();
    218   }
    219 
    220   int GetV8ThreadId() { return v8_thread_id_; }
    221 
    222  private:
    223   int v8_thread_id_;
    224 };
    225 
    226 
    227 // Test that multiple threads using default isolate can be terminated
    228 // from another thread when using Lockers and preemption.
    229 TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
    230   {
    231     v8::Locker locker;
    232     v8::V8::Initialize();
    233     v8::Locker::StartPreemption(1);
    234     semaphore = v8::internal::OS::CreateSemaphore(0);
    235   }
    236   const int kThreads = 2;
    237   i::List<LoopingThread*> threads(kThreads);
    238   for (int i = 0; i < kThreads; i++) {
    239     threads.Add(new LoopingThread());
    240   }
    241   for (int i = 0; i < kThreads; i++) {
    242     threads[i]->Start();
    243   }
    244   // Wait until all threads have signaled the semaphore.
    245   for (int i = 0; i < kThreads; i++) {
    246     semaphore->Wait();
    247   }
    248   {
    249     v8::Locker locker;
    250     for (int i = 0; i < kThreads; i++) {
    251       v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
    252     }
    253   }
    254   for (int i = 0; i < kThreads; i++) {
    255     threads[i]->Join();
    256     delete threads[i];
    257   }
    258 
    259   delete semaphore;
    260   semaphore = NULL;
    261 }
    262 
    263 
    264 int call_count = 0;
    265 
    266 
    267 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
    268   if (++call_count == 10) {
    269     CHECK(!v8::V8::IsExecutionTerminating());
    270     v8::V8::TerminateExecution();
    271     return v8::Undefined();
    272   }
    273   v8::Local<v8::Object> result = v8::Object::New();
    274   result->Set(v8::String::New("x"), v8::Integer::New(42));
    275   return result;
    276 }
    277 
    278 
    279 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
    280   v8::TryCatch try_catch;
    281   CHECK(!v8::V8::IsExecutionTerminating());
    282   v8::Script::Compile(v8::String::New("function f() {"
    283                                       "  try {"
    284                                       "    while(true) {"
    285                                       "      terminate_or_return_object().x;"
    286                                       "    }"
    287                                       "    fail();"
    288                                       "  } catch(e) {"
    289                                       "    fail();"
    290                                       "  }"
    291                                       "}"
    292                                       "f()"))->Run();
    293   CHECK(try_catch.HasCaught());
    294   CHECK(try_catch.Exception()->IsNull());
    295   CHECK(try_catch.Message().IsEmpty());
    296   CHECK(!try_catch.CanContinue());
    297   CHECK(v8::V8::IsExecutionTerminating());
    298   return v8::Undefined();
    299 }
    300 
    301 
    302 // Test that we correctly handle termination exceptions if they are
    303 // triggered by the creation of error objects in connection with ICs.
    304 TEST(TerminateLoadICException) {
    305   v8::HandleScope scope;
    306   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    307   global->Set(v8::String::New("terminate_or_return_object"),
    308               v8::FunctionTemplate::New(TerminateOrReturnObject));
    309   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
    310   global->Set(v8::String::New("loop"),
    311               v8::FunctionTemplate::New(LoopGetProperty));
    312 
    313   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    314   v8::Context::Scope context_scope(context);
    315   CHECK(!v8::V8::IsExecutionTerminating());
    316   // Run a loop that will be infinite if thread termination does not work.
    317   v8::Handle<v8::String> source =
    318       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    319   call_count = 0;
    320   v8::Script::Compile(source)->Run();
    321   // Test that we can run the code again after thread termination.
    322   CHECK(!v8::V8::IsExecutionTerminating());
    323   call_count = 0;
    324   v8::Script::Compile(source)->Run();
    325   context.Dispose();
    326 }
    327 
    328 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
    329   v8::TryCatch try_catch;
    330   CHECK(!v8::V8::IsExecutionTerminating());
    331   v8::Script::Compile(v8::String::New("function f() {"
    332                                       "  var term = true;"
    333                                       "  try {"
    334                                       "    while(true) {"
    335                                       "      if (term) terminate();"
    336                                       "      term = false;"
    337                                       "    }"
    338                                       "    fail();"
    339                                       "  } catch(e) {"
    340                                       "    fail();"
    341                                       "  }"
    342                                       "}"
    343                                       "f()"))->Run();
    344   CHECK(try_catch.HasCaught());
    345   CHECK(try_catch.Exception()->IsNull());
    346   CHECK(try_catch.Message().IsEmpty());
    347   CHECK(!try_catch.CanContinue());
    348   CHECK(v8::V8::IsExecutionTerminating());
    349   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
    350   return v8::Undefined();
    351 }
    352 
    353 // Test that reentry into V8 while the termination exception is still pending
    354 // (has not yet unwound the 0-level JS frame) does not crash.
    355 TEST(TerminateAndReenterFromThreadItself) {
    356   v8::HandleScope scope;
    357   v8::Handle<v8::ObjectTemplate> global =
    358       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
    359   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    360   v8::Context::Scope context_scope(context);
    361   CHECK(!v8::V8::IsExecutionTerminating());
    362   v8::Handle<v8::String> source =
    363       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    364   v8::Script::Compile(source)->Run();
    365   CHECK(!v8::V8::IsExecutionTerminating());
    366   // Check we can run JS again after termination.
    367   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
    368                                             "f()"))->Run()->IsTrue());
    369   context.Dispose();
    370 }
    371 
    372