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(isolate, "TerminatorThread") { }
    165   void Run() {
    166     semaphore->Wait();
    167     CHECK(!v8::V8::IsExecutionTerminating());
    168     v8::V8::TerminateExecution();
    169   }
    170 };
    171 
    172 
    173 // Test that a single thread of JavaScript execution can be terminated
    174 // from the side by another thread.
    175 TEST(TerminateOnlyV8ThreadFromOtherThread) {
    176   semaphore = v8::internal::OS::CreateSemaphore(0);
    177   TerminatorThread thread(i::Isolate::Current());
    178   thread.Start();
    179 
    180   v8::HandleScope scope;
    181   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
    182   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    183   v8::Context::Scope context_scope(context);
    184   CHECK(!v8::V8::IsExecutionTerminating());
    185   // Run a loop that will be infinite if thread termination does not work.
    186   v8::Handle<v8::String> source =
    187       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    188   v8::Script::Compile(source)->Run();
    189 
    190   thread.Join();
    191   delete semaphore;
    192   semaphore = NULL;
    193   context.Dispose();
    194 }
    195 
    196 
    197 class LoopingThread : public v8::internal::Thread {
    198  public:
    199   explicit LoopingThread(i::Isolate* isolate)
    200       : Thread(isolate, "LoopingThread") { }
    201   void Run() {
    202     v8::Locker locker;
    203     v8::HandleScope scope;
    204     v8_thread_id_ = v8::V8::GetCurrentThreadId();
    205     v8::Handle<v8::ObjectTemplate> global =
    206         CreateGlobalTemplate(Signal, DoLoop);
    207     v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    208     v8::Context::Scope context_scope(context);
    209     CHECK(!v8::V8::IsExecutionTerminating());
    210     // Run a loop that will be infinite if thread termination does not work.
    211     v8::Handle<v8::String> source =
    212         v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    213     v8::Script::Compile(source)->Run();
    214     context.Dispose();
    215   }
    216 
    217   int GetV8ThreadId() { return v8_thread_id_; }
    218 
    219  private:
    220   int v8_thread_id_;
    221 };
    222 
    223 
    224 // Test that multiple threads using V8 can be terminated from another
    225 // thread when using Lockers and preemption.
    226 TEST(TerminateMultipleV8Threads) {
    227   {
    228     v8::Locker locker;
    229     v8::V8::Initialize();
    230     v8::Locker::StartPreemption(1);
    231     semaphore = v8::internal::OS::CreateSemaphore(0);
    232   }
    233   LoopingThread thread1(i::Isolate::Current());
    234   thread1.Start();
    235   LoopingThread thread2(i::Isolate::Current());
    236   thread2.Start();
    237   // Wait until both threads have signaled the semaphore.
    238   semaphore->Wait();
    239   semaphore->Wait();
    240   {
    241     v8::Locker locker;
    242     v8::V8::TerminateExecution(thread1.GetV8ThreadId());
    243     v8::V8::TerminateExecution(thread2.GetV8ThreadId());
    244   }
    245   thread1.Join();
    246   thread2.Join();
    247 
    248   delete semaphore;
    249   semaphore = NULL;
    250 }
    251 
    252 
    253 int call_count = 0;
    254 
    255 
    256 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
    257   if (++call_count == 10) {
    258     CHECK(!v8::V8::IsExecutionTerminating());
    259     v8::V8::TerminateExecution();
    260     return v8::Undefined();
    261   }
    262   v8::Local<v8::Object> result = v8::Object::New();
    263   result->Set(v8::String::New("x"), v8::Integer::New(42));
    264   return result;
    265 }
    266 
    267 
    268 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
    269   v8::TryCatch try_catch;
    270   CHECK(!v8::V8::IsExecutionTerminating());
    271   v8::Script::Compile(v8::String::New("function f() {"
    272                                       "  try {"
    273                                       "    while(true) {"
    274                                       "      terminate_or_return_object().x;"
    275                                       "    }"
    276                                       "    fail();"
    277                                       "  } catch(e) {"
    278                                       "    fail();"
    279                                       "  }"
    280                                       "}"
    281                                       "f()"))->Run();
    282   CHECK(try_catch.HasCaught());
    283   CHECK(try_catch.Exception()->IsNull());
    284   CHECK(try_catch.Message().IsEmpty());
    285   CHECK(!try_catch.CanContinue());
    286   CHECK(v8::V8::IsExecutionTerminating());
    287   return v8::Undefined();
    288 }
    289 
    290 
    291 // Test that we correctly handle termination exceptions if they are
    292 // triggered by the creation of error objects in connection with ICs.
    293 TEST(TerminateLoadICException) {
    294   v8::HandleScope scope;
    295   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    296   global->Set(v8::String::New("terminate_or_return_object"),
    297               v8::FunctionTemplate::New(TerminateOrReturnObject));
    298   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
    299   global->Set(v8::String::New("loop"),
    300               v8::FunctionTemplate::New(LoopGetProperty));
    301 
    302   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    303   v8::Context::Scope context_scope(context);
    304   CHECK(!v8::V8::IsExecutionTerminating());
    305   // Run a loop that will be infinite if thread termination does not work.
    306   v8::Handle<v8::String> source =
    307       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    308   call_count = 0;
    309   v8::Script::Compile(source)->Run();
    310   // Test that we can run the code again after thread termination.
    311   CHECK(!v8::V8::IsExecutionTerminating());
    312   call_count = 0;
    313   v8::Script::Compile(source)->Run();
    314   context.Dispose();
    315 }
    316 
    317 v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
    318   v8::TryCatch try_catch;
    319   CHECK(!v8::V8::IsExecutionTerminating());
    320   v8::Script::Compile(v8::String::New("function f() {"
    321                                       "  var term = true;"
    322                                       "  try {"
    323                                       "    while(true) {"
    324                                       "      if (term) terminate();"
    325                                       "      term = false;"
    326                                       "    }"
    327                                       "    fail();"
    328                                       "  } catch(e) {"
    329                                       "    fail();"
    330                                       "  }"
    331                                       "}"
    332                                       "f()"))->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   v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
    339   return v8::Undefined();
    340 }
    341 
    342 // Test that reentry into V8 while the termination exception is still pending
    343 // (has not yet unwound the 0-level JS frame) does not crash.
    344 TEST(TerminateAndReenterFromThreadItself) {
    345   v8::HandleScope scope;
    346   v8::Handle<v8::ObjectTemplate> global =
    347       CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
    348   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    349   v8::Context::Scope context_scope(context);
    350   CHECK(!v8::V8::IsExecutionTerminating());
    351   v8::Handle<v8::String> source =
    352       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    353   v8::Script::Compile(source)->Run();
    354   CHECK(!v8::V8::IsExecutionTerminating());
    355   // Check we can run JS again after termination.
    356   CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
    357                                             "f()"))->Run()->IsTrue());
    358   context.Dispose();
    359 }
    360 
    361