Home | History | Annotate | Download | only in cctest
      1 // Copyright 2007-2008 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 <stdlib.h>
     29 
     30 #include "v8.h"
     31 
     32 #include "heap.h"
     33 #include "cctest.h"
     34 
     35 using namespace v8;
     36 
     37 
     38 enum Expectations {
     39   EXPECT_RESULT,
     40   EXPECT_EXCEPTION
     41 };
     42 
     43 
     44 // A DeclarationContext holds a reference to a v8::Context and keeps
     45 // track of various declaration related counters to make it easier to
     46 // track if global declarations in the presence of interceptors behave
     47 // the right way.
     48 class DeclarationContext {
     49  public:
     50   DeclarationContext();
     51 
     52   virtual ~DeclarationContext() {
     53     if (is_initialized_) {
     54       context_->Exit();
     55       context_.Dispose();
     56     }
     57   }
     58 
     59   void Check(const char* source,
     60              int get, int set, int has,
     61              Expectations expectations,
     62              v8::Handle<Value> value = Local<Value>());
     63 
     64   int get_count() const { return get_count_; }
     65   int set_count() const { return set_count_; }
     66   int query_count() const { return query_count_; }
     67 
     68  protected:
     69   virtual v8::Handle<Value> Get(Local<String> key);
     70   virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
     71   virtual v8::Handle<Integer> Query(Local<String> key);
     72 
     73   void InitializeIfNeeded();
     74 
     75   // Get the holder for the interceptor. Default to the instance template
     76   // but may be overwritten.
     77   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
     78     return function->InstanceTemplate();
     79   }
     80 
     81   // The handlers are called as static functions that forward
     82   // to the instance specific virtual methods.
     83   static v8::Handle<Value> HandleGet(Local<String> key,
     84                                      const AccessorInfo& info);
     85   static v8::Handle<Value> HandleSet(Local<String> key,
     86                                      Local<Value> value,
     87                                      const AccessorInfo& info);
     88   static v8::Handle<Integer> HandleQuery(Local<String> key,
     89                                          const AccessorInfo& info);
     90 
     91  private:
     92   bool is_initialized_;
     93   Persistent<Context> context_;
     94   Local<String> property_;
     95 
     96   int get_count_;
     97   int set_count_;
     98   int query_count_;
     99 
    100   static DeclarationContext* GetInstance(const AccessorInfo& info);
    101 };
    102 
    103 
    104 DeclarationContext::DeclarationContext()
    105     : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) {
    106   // Do nothing.
    107 }
    108 
    109 
    110 void DeclarationContext::InitializeIfNeeded() {
    111   if (is_initialized_) return;
    112   HandleScope scope;
    113   Local<FunctionTemplate> function = FunctionTemplate::New();
    114   Local<Value> data = External::New(this);
    115   GetHolder(function)->SetNamedPropertyHandler(&HandleGet,
    116                                                &HandleSet,
    117                                                &HandleQuery,
    118                                                0, 0,
    119                                                data);
    120   context_ = Context::New(0, function->InstanceTemplate(), Local<Value>());
    121   context_->Enter();
    122   is_initialized_ = true;
    123 }
    124 
    125 
    126 void DeclarationContext::Check(const char* source,
    127                                int get, int set, int query,
    128                                Expectations expectations,
    129                                v8::Handle<Value> value) {
    130   InitializeIfNeeded();
    131   // A retry after a GC may pollute the counts, so perform gc now
    132   // to avoid that.
    133   HEAP->CollectGarbage(v8::internal::NEW_SPACE);
    134   HandleScope scope;
    135   TryCatch catcher;
    136   catcher.SetVerbose(true);
    137   Local<Value> result = Script::Compile(String::New(source))->Run();
    138   CHECK_EQ(get, get_count());
    139   CHECK_EQ(set, set_count());
    140   CHECK_EQ(query, query_count());
    141   if (expectations == EXPECT_RESULT) {
    142     CHECK(!catcher.HasCaught());
    143     if (!value.IsEmpty()) {
    144       CHECK_EQ(value, result);
    145     }
    146   } else {
    147     CHECK(expectations == EXPECT_EXCEPTION);
    148     CHECK(catcher.HasCaught());
    149     if (!value.IsEmpty()) {
    150       CHECK_EQ(value, catcher.Exception());
    151     }
    152   }
    153 }
    154 
    155 
    156 v8::Handle<Value> DeclarationContext::HandleGet(Local<String> key,
    157                                                 const AccessorInfo& info) {
    158   DeclarationContext* context = GetInstance(info);
    159   context->get_count_++;
    160   return context->Get(key);
    161 }
    162 
    163 
    164 v8::Handle<Value> DeclarationContext::HandleSet(Local<String> key,
    165                                                 Local<Value> value,
    166                                                 const AccessorInfo& info) {
    167   DeclarationContext* context = GetInstance(info);
    168   context->set_count_++;
    169   return context->Set(key, value);
    170 }
    171 
    172 
    173 v8::Handle<Integer> DeclarationContext::HandleQuery(Local<String> key,
    174                                                     const AccessorInfo& info) {
    175   DeclarationContext* context = GetInstance(info);
    176   context->query_count_++;
    177   return context->Query(key);
    178 }
    179 
    180 
    181 DeclarationContext* DeclarationContext::GetInstance(const AccessorInfo& info) {
    182   return static_cast<DeclarationContext*>(External::Unwrap(info.Data()));
    183 }
    184 
    185 
    186 v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
    187   return v8::Handle<Value>();
    188 }
    189 
    190 
    191 v8::Handle<Value> DeclarationContext::Set(Local<String> key,
    192                                           Local<Value> value) {
    193   return v8::Handle<Value>();
    194 }
    195 
    196 
    197 v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
    198   return v8::Handle<Integer>();
    199 }
    200 
    201 
    202 // Test global declaration of a property the interceptor doesn't know
    203 // about and doesn't handle.
    204 TEST(Unknown) {
    205   HandleScope scope;
    206 
    207   { DeclarationContext context;
    208     context.Check("var x; x",
    209                   1,  // access
    210                   1,  // declaration
    211                   2,  // declaration + initialization
    212                   EXPECT_RESULT, Undefined());
    213   }
    214 
    215   { DeclarationContext context;
    216     context.Check("var x = 0; x",
    217                   1,  // access
    218                   2,  // declaration + initialization
    219                   2,  // declaration + initialization
    220                   EXPECT_RESULT, Number::New(0));
    221   }
    222 
    223   { DeclarationContext context;
    224     context.Check("function x() { }; x",
    225                   1,  // access
    226                   0,
    227                   0,
    228                   EXPECT_RESULT);
    229   }
    230 
    231   { DeclarationContext context;
    232     context.Check("const x; x",
    233                   1,  // access
    234                   2,  // declaration + initialization
    235                   1,  // declaration
    236                   EXPECT_RESULT, Undefined());
    237   }
    238 
    239   { DeclarationContext context;
    240     context.Check("const x = 0; x",
    241                   1,  // access
    242                   2,  // declaration + initialization
    243                   1,  // declaration
    244                   EXPECT_RESULT, Undefined());  // SB 0 - BUG 1213579
    245   }
    246 }
    247 
    248 
    249 
    250 class PresentPropertyContext: public DeclarationContext {
    251  protected:
    252   virtual v8::Handle<Integer> Query(Local<String> key) {
    253     return Integer::New(v8::None);
    254   }
    255 };
    256 
    257 
    258 
    259 TEST(Present) {
    260   HandleScope scope;
    261 
    262   { PresentPropertyContext context;
    263     context.Check("var x; x",
    264                   1,  // access
    265                   0,
    266                   2,  // declaration + initialization
    267                   EXPECT_EXCEPTION);  // x is not defined!
    268   }
    269 
    270   { PresentPropertyContext context;
    271     context.Check("var x = 0; x",
    272                   1,  // access
    273                   1,  // initialization
    274                   2,  // declaration + initialization
    275                   EXPECT_RESULT, Number::New(0));
    276   }
    277 
    278   { PresentPropertyContext context;
    279     context.Check("function x() { }; x",
    280                   1,  // access
    281                   0,
    282                   0,
    283                   EXPECT_RESULT);
    284   }
    285 
    286   { PresentPropertyContext context;
    287     context.Check("const x; x",
    288                   1,  // access
    289                   1,  // initialization
    290                   1,  // (re-)declaration
    291                   EXPECT_RESULT, Undefined());
    292   }
    293 
    294   { PresentPropertyContext context;
    295     context.Check("const x = 0; x",
    296                   1,  // access
    297                   1,  // initialization
    298                   1,  // (re-)declaration
    299                   EXPECT_RESULT, Number::New(0));
    300   }
    301 }
    302 
    303 
    304 
    305 class AbsentPropertyContext: public DeclarationContext {
    306  protected:
    307   virtual v8::Handle<Integer> Query(Local<String> key) {
    308     return v8::Handle<Integer>();
    309   }
    310 };
    311 
    312 
    313 TEST(Absent) {
    314   HandleScope scope;
    315 
    316   { AbsentPropertyContext context;
    317     context.Check("var x; x",
    318                   1,  // access
    319                   1,  // declaration
    320                   2,  // declaration + initialization
    321                   EXPECT_RESULT, Undefined());
    322   }
    323 
    324   { AbsentPropertyContext context;
    325     context.Check("var x = 0; x",
    326                   1,  // access
    327                   2,  // declaration + initialization
    328                   2,  // declaration + initialization
    329                   EXPECT_RESULT, Number::New(0));
    330   }
    331 
    332   { AbsentPropertyContext context;
    333     context.Check("function x() { }; x",
    334                   1,  // access
    335                   0,
    336                   0,
    337                   EXPECT_RESULT);
    338   }
    339 
    340   { AbsentPropertyContext context;
    341     context.Check("const x; x",
    342                   1,  // access
    343                   2,  // declaration + initialization
    344                   1,  // declaration
    345                   EXPECT_RESULT, Undefined());
    346   }
    347 
    348   { AbsentPropertyContext context;
    349     context.Check("const x = 0; x",
    350                   1,  // access
    351                   2,  // declaration + initialization
    352                   1,  // declaration
    353                   EXPECT_RESULT, Undefined());  // SB 0 - BUG 1213579
    354   }
    355 
    356   { AbsentPropertyContext context;
    357     context.Check("if (false) { var x = 0 }; x",
    358                   1,  // access
    359                   1,  // declaration
    360                   1,  // declaration + initialization
    361                   EXPECT_RESULT, Undefined());
    362   }
    363 }
    364 
    365 
    366 
    367 class AppearingPropertyContext: public DeclarationContext {
    368  public:
    369   enum State {
    370     DECLARE,
    371     INITIALIZE_IF_ASSIGN,
    372     UNKNOWN
    373   };
    374 
    375   AppearingPropertyContext() : state_(DECLARE) { }
    376 
    377  protected:
    378   virtual v8::Handle<Integer> Query(Local<String> key) {
    379     switch (state_) {
    380       case DECLARE:
    381         // Force declaration by returning that the
    382         // property is absent.
    383         state_ = INITIALIZE_IF_ASSIGN;
    384         return Handle<Integer>();
    385       case INITIALIZE_IF_ASSIGN:
    386         // Return that the property is present so we only get the
    387         // setter called when initializing with a value.
    388         state_ = UNKNOWN;
    389         return Integer::New(v8::None);
    390       default:
    391         CHECK(state_ == UNKNOWN);
    392         break;
    393     }
    394     // Do the lookup in the object.
    395     return v8::Handle<Integer>();
    396   }
    397 
    398  private:
    399   State state_;
    400 };
    401 
    402 
    403 TEST(Appearing) {
    404   HandleScope scope;
    405 
    406   { AppearingPropertyContext context;
    407     context.Check("var x; x",
    408                   1,  // access
    409                   1,  // declaration
    410                   2,  // declaration + initialization
    411                   EXPECT_RESULT, Undefined());
    412   }
    413 
    414   { AppearingPropertyContext context;
    415     context.Check("var x = 0; x",
    416                   1,  // access
    417                   2,  // declaration + initialization
    418                   2,  // declaration + initialization
    419                   EXPECT_RESULT, Number::New(0));
    420   }
    421 
    422   { AppearingPropertyContext context;
    423     context.Check("function x() { }; x",
    424                   1,  // access
    425                   0,
    426                   0,
    427                   EXPECT_RESULT);
    428   }
    429 
    430   { AppearingPropertyContext context;
    431     context.Check("const x; x",
    432                   1,  // access
    433                   2,  // declaration + initialization
    434                   1,  // declaration
    435                   EXPECT_RESULT, Undefined());
    436   }
    437 
    438   { AppearingPropertyContext context;
    439     context.Check("const x = 0; x",
    440                   1,  // access
    441                   2,  // declaration + initialization
    442                   1,  // declaration
    443                   EXPECT_RESULT, Undefined());
    444                   // Result is undefined because declaration succeeded but
    445                   // initialization to 0 failed (due to context behavior).
    446   }
    447 }
    448 
    449 
    450 
    451 class ReappearingPropertyContext: public DeclarationContext {
    452  public:
    453   enum State {
    454     DECLARE,
    455     DONT_DECLARE,
    456     INITIALIZE,
    457     UNKNOWN
    458   };
    459 
    460   ReappearingPropertyContext() : state_(DECLARE) { }
    461 
    462  protected:
    463   virtual v8::Handle<Integer> Query(Local<String> key) {
    464     switch (state_) {
    465       case DECLARE:
    466         // Force the first declaration by returning that
    467         // the property is absent.
    468         state_ = DONT_DECLARE;
    469         return Handle<Integer>();
    470       case DONT_DECLARE:
    471         // Ignore the second declaration by returning
    472         // that the property is already there.
    473         state_ = INITIALIZE;
    474         return Integer::New(v8::None);
    475       case INITIALIZE:
    476         // Force an initialization by returning that
    477         // the property is absent. This will make sure
    478         // that the setter is called and it will not
    479         // lead to redeclaration conflicts (yet).
    480         state_ = UNKNOWN;
    481         return Handle<Integer>();
    482       default:
    483         CHECK(state_ == UNKNOWN);
    484         break;
    485     }
    486     // Do the lookup in the object.
    487     return Handle<Integer>();
    488   }
    489 
    490  private:
    491   State state_;
    492 };
    493 
    494 
    495 TEST(Reappearing) {
    496   HandleScope scope;
    497 
    498   { ReappearingPropertyContext context;
    499     context.Check("const x; var x = 0",
    500                   0,
    501                   3,  // const declaration+initialization, var initialization
    502                   3,  // 2 x declaration + var initialization
    503                   EXPECT_RESULT, Undefined());
    504   }
    505 }
    506 
    507 
    508 
    509 class ExistsInPrototypeContext: public DeclarationContext {
    510  protected:
    511   virtual v8::Handle<Integer> Query(Local<String> key) {
    512     // Let it seem that the property exists in the prototype object.
    513     return Integer::New(v8::None);
    514   }
    515 
    516   // Use the prototype as the holder for the interceptors.
    517   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
    518     return function->PrototypeTemplate();
    519   }
    520 };
    521 
    522 
    523 TEST(ExistsInPrototype) {
    524   HandleScope scope;
    525 
    526   // Sanity check to make sure that the holder of the interceptor
    527   // really is the prototype object.
    528   { ExistsInPrototypeContext context;
    529     context.Check("this.x = 87; this.x",
    530                   0,
    531                   0,
    532                   0,
    533                   EXPECT_RESULT, Number::New(87));
    534   }
    535 
    536   { ExistsInPrototypeContext context;
    537     context.Check("var x; x",
    538                   1,  // get
    539                   0,
    540                   1,  // declaration
    541                   EXPECT_EXCEPTION);
    542   }
    543 
    544   { ExistsInPrototypeContext context;
    545     context.Check("var x = 0; x",
    546                   0,
    547                   0,
    548                   1,  // declaration
    549                   EXPECT_RESULT, Number::New(0));
    550   }
    551 
    552   { ExistsInPrototypeContext context;
    553     context.Check("const x; x",
    554                   0,
    555                   0,
    556                   1,  // declaration
    557                   EXPECT_RESULT, Undefined());
    558   }
    559 
    560   { ExistsInPrototypeContext context;
    561     context.Check("const x = 0; x",
    562                   0,
    563                   0,
    564                   1,  // declaration
    565                   EXPECT_RESULT, Number::New(0));
    566   }
    567 }
    568 
    569 
    570 
    571 class AbsentInPrototypeContext: public DeclarationContext {
    572  protected:
    573   virtual v8::Handle<Integer> Query(Local<String> key) {
    574     // Let it seem that the property is absent in the prototype object.
    575     return Handle<Integer>();
    576   }
    577 
    578   // Use the prototype as the holder for the interceptors.
    579   virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
    580     return function->PrototypeTemplate();
    581   }
    582 };
    583 
    584 
    585 TEST(AbsentInPrototype) {
    586   HandleScope scope;
    587 
    588   { AbsentInPrototypeContext context;
    589     context.Check("if (false) { var x = 0; }; x",
    590                   0,
    591                   0,
    592                   1,  // declaration
    593                   EXPECT_RESULT, Undefined());
    594   }
    595 }
    596