Home | History | Annotate | Download | only in cctest
      1 // Copyright 2011 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 "src/v8.h"
     29 #include "test/cctest/cctest.h"
     30 
     31 #include "src/api.h"
     32 #include "src/debug/debug.h"
     33 #include "src/execution.h"
     34 #include "src/factory.h"
     35 #include "src/global-handles.h"
     36 #include "src/macro-assembler.h"
     37 #include "src/objects.h"
     38 #include "test/cctest/heap/utils-inl.h"
     39 
     40 using namespace v8::internal;
     41 
     42 namespace {
     43 
     44 
     45 template<typename HashMap>
     46 static void TestHashMap(Handle<HashMap> table) {
     47   Isolate* isolate = CcTest::i_isolate();
     48   Factory* factory = isolate->factory();
     49 
     50   Handle<JSObject> a = factory->NewJSArray(7);
     51   Handle<JSObject> b = factory->NewJSArray(11);
     52   table = HashMap::Put(table, a, b);
     53   CHECK_EQ(table->NumberOfElements(), 1);
     54   CHECK_EQ(table->Lookup(a), *b);
     55   // When the key does not exist in the map, Lookup returns the hole.
     56   CHECK_EQ(table->Lookup(b), CcTest::heap()->the_hole_value());
     57 
     58   // Keys still have to be valid after objects were moved.
     59   CcTest::heap()->CollectGarbage(NEW_SPACE);
     60   CHECK_EQ(table->NumberOfElements(), 1);
     61   CHECK_EQ(table->Lookup(a), *b);
     62   CHECK_EQ(table->Lookup(b), CcTest::heap()->the_hole_value());
     63 
     64   // Keys that are overwritten should not change number of elements.
     65   table = HashMap::Put(table, a, factory->NewJSArray(13));
     66   CHECK_EQ(table->NumberOfElements(), 1);
     67   CHECK_NE(table->Lookup(a), *b);
     68 
     69   // Keys that have been removed are mapped to the hole.
     70   bool was_present = false;
     71   table = HashMap::Remove(table, a, &was_present);
     72   CHECK(was_present);
     73   CHECK_EQ(table->NumberOfElements(), 0);
     74   CHECK_EQ(table->Lookup(a), CcTest::heap()->the_hole_value());
     75 
     76   // Keys should map back to their respective values and also should get
     77   // an identity hash code generated.
     78   for (int i = 0; i < 100; i++) {
     79     Handle<JSReceiver> key = factory->NewJSArray(7);
     80     Handle<JSObject> value = factory->NewJSArray(11);
     81     table = HashMap::Put(table, key, value);
     82     CHECK_EQ(table->NumberOfElements(), i + 1);
     83     CHECK_NE(table->FindEntry(key), HashMap::kNotFound);
     84     CHECK_EQ(table->Lookup(key), *value);
     85     CHECK(key->GetIdentityHash()->IsSmi());
     86   }
     87 
     88   // Keys never added to the map which already have an identity hash
     89   // code should not be found.
     90   for (int i = 0; i < 100; i++) {
     91     Handle<JSReceiver> key = factory->NewJSArray(7);
     92     CHECK(JSReceiver::GetOrCreateIdentityHash(key)->IsSmi());
     93     CHECK_EQ(table->FindEntry(key), HashMap::kNotFound);
     94     CHECK_EQ(table->Lookup(key), CcTest::heap()->the_hole_value());
     95     CHECK(key->GetIdentityHash()->IsSmi());
     96   }
     97 
     98   // Keys that don't have an identity hash should not be found and also
     99   // should not get an identity hash code generated.
    100   for (int i = 0; i < 100; i++) {
    101     Handle<JSReceiver> key = factory->NewJSArray(7);
    102     CHECK_EQ(table->Lookup(key), CcTest::heap()->the_hole_value());
    103     Object* identity_hash = key->GetIdentityHash();
    104     CHECK_EQ(identity_hash, CcTest::heap()->undefined_value());
    105   }
    106 }
    107 
    108 
    109 TEST(HashMap) {
    110   LocalContext context;
    111   v8::HandleScope scope(context->GetIsolate());
    112   Isolate* isolate = CcTest::i_isolate();
    113   TestHashMap(ObjectHashTable::New(isolate, 23));
    114 }
    115 
    116 
    117 class ObjectHashTableTest: public ObjectHashTable {
    118  public:
    119   void insert(int entry, int key, int value) {
    120     set(EntryToIndex(entry), Smi::FromInt(key));
    121     set(EntryToIndex(entry) + 1, Smi::FromInt(value));
    122   }
    123 
    124   int lookup(int key) {
    125     Handle<Object> key_obj(Smi::FromInt(key), GetIsolate());
    126     return Smi::cast(Lookup(key_obj))->value();
    127   }
    128 
    129   int capacity() {
    130     return Capacity();
    131   }
    132 };
    133 
    134 
    135 TEST(HashTableRehash) {
    136   LocalContext context;
    137   Isolate* isolate = CcTest::i_isolate();
    138   v8::HandleScope scope(context->GetIsolate());
    139   // Test almost filled table.
    140   {
    141     Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 100);
    142     ObjectHashTableTest* t = reinterpret_cast<ObjectHashTableTest*>(*table);
    143     int capacity = t->capacity();
    144     for (int i = 0; i < capacity - 1; i++) {
    145       t->insert(i, i * i, i);
    146     }
    147     t->Rehash(handle(Smi::FromInt(0), isolate));
    148     for (int i = 0; i < capacity - 1; i++) {
    149       CHECK_EQ(i, t->lookup(i * i));
    150     }
    151   }
    152   // Test half-filled table.
    153   {
    154     Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 100);
    155     ObjectHashTableTest* t = reinterpret_cast<ObjectHashTableTest*>(*table);
    156     int capacity = t->capacity();
    157     for (int i = 0; i < capacity / 2; i++) {
    158       t->insert(i, i * i, i);
    159     }
    160     t->Rehash(handle(Smi::FromInt(0), isolate));
    161     for (int i = 0; i < capacity / 2; i++) {
    162       CHECK_EQ(i, t->lookup(i * i));
    163     }
    164   }
    165 }
    166 
    167 
    168 #ifdef DEBUG
    169 template<class HashSet>
    170 static void TestHashSetCausesGC(Handle<HashSet> table) {
    171   Isolate* isolate = CcTest::i_isolate();
    172   Factory* factory = isolate->factory();
    173 
    174   Handle<JSObject> key = factory->NewJSArray(0);
    175 
    176   // Simulate a full heap so that generating an identity hash code
    177   // in subsequent calls will request GC.
    178   SimulateFullSpace(CcTest::heap()->new_space());
    179   SimulateFullSpace(CcTest::heap()->old_space());
    180 
    181   // Calling Contains() should not cause GC ever.
    182   int gc_count = isolate->heap()->gc_count();
    183   CHECK(!table->Contains(key));
    184   CHECK(gc_count == isolate->heap()->gc_count());
    185 
    186   // Calling Remove() will not cause GC in this case.
    187   bool was_present = false;
    188   table = HashSet::Remove(table, key, &was_present);
    189   CHECK(!was_present);
    190   CHECK(gc_count == isolate->heap()->gc_count());
    191 
    192   // Calling Add() should cause GC.
    193   table = HashSet::Add(table, key);
    194   CHECK(gc_count < isolate->heap()->gc_count());
    195 }
    196 #endif
    197 
    198 
    199 #ifdef DEBUG
    200 template<class HashMap>
    201 static void TestHashMapCausesGC(Handle<HashMap> table) {
    202   Isolate* isolate = CcTest::i_isolate();
    203   Factory* factory = isolate->factory();
    204 
    205   Handle<JSObject> key = factory->NewJSArray(0);
    206 
    207   // Simulate a full heap so that generating an identity hash code
    208   // in subsequent calls will request GC.
    209   SimulateFullSpace(CcTest::heap()->new_space());
    210   SimulateFullSpace(CcTest::heap()->old_space());
    211 
    212   // Calling Lookup() should not cause GC ever.
    213   CHECK(table->Lookup(key)->IsTheHole());
    214 
    215   // Calling Put() should request GC by returning a failure.
    216   int gc_count = isolate->heap()->gc_count();
    217   HashMap::Put(table, key, key);
    218   CHECK(gc_count < isolate->heap()->gc_count());
    219 }
    220 
    221 
    222 TEST(ObjectHashTableCausesGC) {
    223   i::FLAG_stress_compaction = false;
    224   LocalContext context;
    225   v8::HandleScope scope(context->GetIsolate());
    226   Isolate* isolate = CcTest::i_isolate();
    227   TestHashMapCausesGC(ObjectHashTable::New(isolate, 1));
    228 }
    229 #endif
    230 
    231 TEST(SetRequiresCopyOnCapacityChange) {
    232   LocalContext context;
    233   v8::HandleScope scope(context->GetIsolate());
    234   Isolate* isolate = CcTest::i_isolate();
    235   Handle<NameDictionary> dict = NameDictionary::New(isolate, 0, TENURED);
    236   dict->SetRequiresCopyOnCapacityChange();
    237   Handle<Name> key = isolate->factory()->InternalizeString(
    238       v8::Utils::OpenHandle(*v8_str("key")));
    239   Handle<Object> value = handle(Smi::FromInt(0), isolate);
    240   Handle<NameDictionary> new_dict =
    241       NameDictionary::Add(dict, key, value, PropertyDetails::Empty());
    242   CHECK_NE(*dict, *new_dict);
    243 }
    244 
    245 }  // namespace
    246