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   v8::V8::TerminateExecution();
     44   return v8::Undefined();
     45 }
     46 
     47 
     48 v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
     49   CHECK(false);
     50   return v8::Undefined();
     51 }
     52 
     53 
     54 v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
     55   v8::Handle<v8::String> source =
     56       v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
     57   v8::Script::Compile(source)->Run();
     58   return v8::Undefined();
     59 }
     60 
     61 
     62 v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
     63   v8::TryCatch try_catch;
     64   v8::Script::Compile(v8::String::New("function f() {"
     65                                       "  var term = true;"
     66                                       "  try {"
     67                                       "    while(true) {"
     68                                       "      if (term) terminate();"
     69                                       "      term = false;"
     70                                       "    }"
     71                                       "    fail();"
     72                                       "  } catch(e) {"
     73                                       "    fail();"
     74                                       "  }"
     75                                       "}"
     76                                       "f()"))->Run();
     77   CHECK(try_catch.HasCaught());
     78   CHECK(try_catch.Exception()->IsNull());
     79   CHECK(try_catch.Message().IsEmpty());
     80   CHECK(!try_catch.CanContinue());
     81   return v8::Undefined();
     82 }
     83 
     84 
     85 v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
     86   v8::TryCatch try_catch;
     87   v8::Script::Compile(v8::String::New("var term = true;"
     88                                       "while(true) {"
     89                                       "  if (term) terminate();"
     90                                       "  term = false;"
     91                                       "}"))->Run();
     92   CHECK(try_catch.HasCaught());
     93   CHECK(try_catch.Exception()->IsNull());
     94   CHECK(try_catch.Message().IsEmpty());
     95   CHECK(!try_catch.CanContinue());
     96   return v8::Undefined();
     97 }
     98 
     99 
    100 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
    101     v8::InvocationCallback terminate,
    102     v8::InvocationCallback doloop) {
    103   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    104   global->Set(v8::String::New("terminate"),
    105               v8::FunctionTemplate::New(terminate));
    106   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
    107   global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
    108   global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
    109   return global;
    110 }
    111 
    112 
    113 // Test that a single thread of JavaScript execution can terminate
    114 // itself.
    115 TEST(TerminateOnlyV8ThreadFromThreadItself) {
    116   v8::HandleScope scope;
    117   v8::Handle<v8::ObjectTemplate> global =
    118       CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
    119   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    120   v8::Context::Scope context_scope(context);
    121   // Run a loop that will be infinite if thread termination does not work.
    122   v8::Handle<v8::String> source =
    123       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    124   v8::Script::Compile(source)->Run();
    125   // Test that we can run the code again after thread termination.
    126   v8::Script::Compile(source)->Run();
    127   context.Dispose();
    128 }
    129 
    130 
    131 // Test that a single thread of JavaScript execution can terminate
    132 // itself in a loop that performs no calls.
    133 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
    134   v8::HandleScope scope;
    135   v8::Handle<v8::ObjectTemplate> global =
    136       CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
    137   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    138   v8::Context::Scope context_scope(context);
    139   // Run a loop that will be infinite if thread termination does not work.
    140   v8::Handle<v8::String> source =
    141       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    142   v8::Script::Compile(source)->Run();
    143   // Test that we can run the code again after thread termination.
    144   v8::Script::Compile(source)->Run();
    145   context.Dispose();
    146 }
    147 
    148 
    149 class TerminatorThread : public v8::internal::Thread {
    150   void Run() {
    151     semaphore->Wait();
    152     v8::V8::TerminateExecution();
    153   }
    154 };
    155 
    156 
    157 // Test that a single thread of JavaScript execution can be terminated
    158 // from the side by another thread.
    159 TEST(TerminateOnlyV8ThreadFromOtherThread) {
    160   semaphore = v8::internal::OS::CreateSemaphore(0);
    161   TerminatorThread thread;
    162   thread.Start();
    163 
    164   v8::HandleScope scope;
    165   v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
    166   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    167   v8::Context::Scope context_scope(context);
    168   // Run a loop that will be infinite if thread termination does not work.
    169   v8::Handle<v8::String> source =
    170       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    171   v8::Script::Compile(source)->Run();
    172 
    173   thread.Join();
    174   delete semaphore;
    175   semaphore = NULL;
    176   context.Dispose();
    177 }
    178 
    179 
    180 class LoopingThread : public v8::internal::Thread {
    181  public:
    182   void Run() {
    183     v8::Locker locker;
    184     v8::HandleScope scope;
    185     v8_thread_id_ = v8::V8::GetCurrentThreadId();
    186     v8::Handle<v8::ObjectTemplate> global =
    187         CreateGlobalTemplate(Signal, DoLoop);
    188     v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    189     v8::Context::Scope context_scope(context);
    190     // Run a loop that will be infinite if thread termination does not work.
    191     v8::Handle<v8::String> source =
    192         v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    193     v8::Script::Compile(source)->Run();
    194     context.Dispose();
    195   }
    196 
    197   int GetV8ThreadId() { return v8_thread_id_; }
    198 
    199  private:
    200   int v8_thread_id_;
    201 };
    202 
    203 
    204 // Test that multiple threads using V8 can be terminated from another
    205 // thread when using Lockers and preemption.
    206 TEST(TerminateMultipleV8Threads) {
    207   {
    208     v8::Locker locker;
    209     v8::V8::Initialize();
    210     v8::Locker::StartPreemption(1);
    211     semaphore = v8::internal::OS::CreateSemaphore(0);
    212   }
    213   LoopingThread thread1;
    214   thread1.Start();
    215   LoopingThread thread2;
    216   thread2.Start();
    217   // Wait until both threads have signaled the semaphore.
    218   semaphore->Wait();
    219   semaphore->Wait();
    220   {
    221     v8::Locker locker;
    222     v8::V8::TerminateExecution(thread1.GetV8ThreadId());
    223     v8::V8::TerminateExecution(thread2.GetV8ThreadId());
    224   }
    225   thread1.Join();
    226   thread2.Join();
    227 
    228   delete semaphore;
    229   semaphore = NULL;
    230 }
    231 
    232 
    233 int call_count = 0;
    234 
    235 
    236 v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
    237   if (++call_count == 10) {
    238     v8::V8::TerminateExecution();
    239     return v8::Undefined();
    240   }
    241   v8::Local<v8::Object> result = v8::Object::New();
    242   result->Set(v8::String::New("x"), v8::Integer::New(42));
    243   return result;
    244 }
    245 
    246 
    247 v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
    248   v8::TryCatch try_catch;
    249   v8::Script::Compile(v8::String::New("function f() {"
    250                                       "  try {"
    251                                       "    while(true) {"
    252                                       "      terminate_or_return_object().x;"
    253                                       "    }"
    254                                       "    fail();"
    255                                       "  } catch(e) {"
    256                                       "    fail();"
    257                                       "  }"
    258                                       "}"
    259                                       "f()"))->Run();
    260   CHECK(try_catch.HasCaught());
    261   CHECK(try_catch.Exception()->IsNull());
    262   CHECK(try_catch.Message().IsEmpty());
    263   CHECK(!try_catch.CanContinue());
    264   return v8::Undefined();
    265 }
    266 
    267 
    268 // Test that we correctly handle termination exceptions if they are
    269 // triggered by the creation of error objects in connection with ICs.
    270 TEST(TerminateLoadICException) {
    271   v8::HandleScope scope;
    272   v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
    273   global->Set(v8::String::New("terminate_or_return_object"),
    274               v8::FunctionTemplate::New(TerminateOrReturnObject));
    275   global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
    276   global->Set(v8::String::New("loop"),
    277               v8::FunctionTemplate::New(LoopGetProperty));
    278 
    279   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
    280   v8::Context::Scope context_scope(context);
    281   // Run a loop that will be infinite if thread termination does not work.
    282   v8::Handle<v8::String> source =
    283       v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
    284   call_count = 0;
    285   v8::Script::Compile(source)->Run();
    286   // Test that we can run the code again after thread termination.
    287   call_count = 0;
    288   v8::Script::Compile(source)->Run();
    289   context.Dispose();
    290 }
    291