• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <utility>
29 
30 #include "src/v8.h"
31 
32 #include "src/global-handles.h"
33 #include "test/cctest/cctest.h"
34 #include "test/cctest/heap/heap-utils.h"
35 
36 using namespace v8::internal;
37 
GetIsolateFrom(LocalContext * context)38 static Isolate* GetIsolateFrom(LocalContext* context) {
39   return reinterpret_cast<Isolate*>((*context)->GetIsolate());
40 }
41 
42 
AllocateJSWeakSet(Isolate * isolate)43 static Handle<JSWeakSet> AllocateJSWeakSet(Isolate* isolate) {
44   Factory* factory = isolate->factory();
45   Handle<Map> map = factory->NewMap(JS_WEAK_SET_TYPE, JSWeakSet::kSize);
46   Handle<JSObject> weakset_obj = factory->NewJSObjectFromMap(map);
47   Handle<JSWeakSet> weakset(JSWeakSet::cast(*weakset_obj));
48   // Do not leak handles for the hash table, it would make entries strong.
49   {
50     HandleScope scope(isolate);
51     Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 1);
52     weakset->set_table(*table);
53   }
54   return weakset;
55 }
56 
57 static int NumberOfWeakCalls = 0;
WeakPointerCallback(const v8::WeakCallbackInfo<void> & data)58 static void WeakPointerCallback(const v8::WeakCallbackInfo<void>& data) {
59   std::pair<v8::Persistent<v8::Value>*, int>* p =
60       reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>(
61           data.GetParameter());
62   CHECK_EQ(1234, p->second);
63   NumberOfWeakCalls++;
64   p->first->Reset();
65 }
66 
67 
TEST(WeakSet_Weakness)68 TEST(WeakSet_Weakness) {
69   FLAG_incremental_marking = false;
70   LocalContext context;
71   Isolate* isolate = GetIsolateFrom(&context);
72   Factory* factory = isolate->factory();
73   Heap* heap = isolate->heap();
74   HandleScope scope(isolate);
75   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
76   GlobalHandles* global_handles = isolate->global_handles();
77 
78   // Keep global reference to the key.
79   Handle<Object> key;
80   {
81     HandleScope scope(isolate);
82     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
83     Handle<JSObject> object = factory->NewJSObjectFromMap(map);
84     key = global_handles->Create(*object);
85   }
86   CHECK(!global_handles->IsWeak(key.location()));
87 
88   // Put entry into weak set.
89   {
90     HandleScope scope(isolate);
91     Handle<Smi> smi(Smi::FromInt(23), isolate);
92     int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
93     JSWeakCollection::Set(weakset, key, smi, hash);
94   }
95   CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
96 
97   // Force a full GC.
98   heap->CollectAllGarbage(false);
99   CHECK_EQ(0, NumberOfWeakCalls);
100   CHECK_EQ(1, ObjectHashTable::cast(weakset->table())->NumberOfElements());
101   CHECK_EQ(
102       0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
103 
104   // Make the global reference to the key weak.
105   {
106     HandleScope scope(isolate);
107     std::pair<Handle<Object>*, int> handle_and_id(&key, 1234);
108     GlobalHandles::MakeWeak(
109         key.location(), reinterpret_cast<void*>(&handle_and_id),
110         &WeakPointerCallback, v8::WeakCallbackType::kParameter);
111   }
112   CHECK(global_handles->IsWeak(key.location()));
113 
114   heap->CollectAllGarbage(false);
115   CHECK_EQ(1, NumberOfWeakCalls);
116   CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements());
117   CHECK_EQ(
118       1, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
119 }
120 
121 
TEST(WeakSet_Shrinking)122 TEST(WeakSet_Shrinking) {
123   LocalContext context;
124   Isolate* isolate = GetIsolateFrom(&context);
125   Factory* factory = isolate->factory();
126   Heap* heap = isolate->heap();
127   HandleScope scope(isolate);
128   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
129 
130   // Check initial capacity.
131   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity());
132 
133   // Fill up weak set to trigger capacity change.
134   {
135     HandleScope scope(isolate);
136     Handle<Map> map = factory->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
137     for (int i = 0; i < 32; i++) {
138       Handle<JSObject> object = factory->NewJSObjectFromMap(map);
139       Handle<Smi> smi(Smi::FromInt(i), isolate);
140       int32_t hash = Object::GetOrCreateHash(isolate, object)->value();
141       JSWeakCollection::Set(weakset, object, smi, hash);
142     }
143   }
144 
145   // Check increased capacity.
146   CHECK_EQ(128, ObjectHashTable::cast(weakset->table())->Capacity());
147 
148   // Force a full GC.
149   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->NumberOfElements());
150   CHECK_EQ(
151       0, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
152   heap->CollectAllGarbage(false);
153   CHECK_EQ(0, ObjectHashTable::cast(weakset->table())->NumberOfElements());
154   CHECK_EQ(
155       32, ObjectHashTable::cast(weakset->table())->NumberOfDeletedElements());
156 
157   // Check shrunk capacity.
158   CHECK_EQ(32, ObjectHashTable::cast(weakset->table())->Capacity());
159 }
160 
161 
162 // Test that weak set values on an evacuation candidate which are not reachable
163 // by other paths are correctly recorded in the slots buffer.
TEST(WeakSet_Regress2060a)164 TEST(WeakSet_Regress2060a) {
165   if (i::FLAG_never_compact) return;
166   FLAG_always_compact = true;
167   LocalContext context;
168   Isolate* isolate = GetIsolateFrom(&context);
169   Factory* factory = isolate->factory();
170   Heap* heap = isolate->heap();
171   HandleScope scope(isolate);
172   Handle<JSFunction> function = factory->NewFunction(
173       factory->function_string());
174   Handle<JSObject> key = factory->NewJSObject(function);
175   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
176 
177   // Start second old-space page so that values land on evacuation candidate.
178   Page* first_page = heap->old_space()->anchor()->next_page();
179   heap::SimulateFullSpace(heap->old_space());
180 
181   // Fill up weak set with values on an evacuation candidate.
182   {
183     HandleScope scope(isolate);
184     for (int i = 0; i < 32; i++) {
185       Handle<JSObject> object = factory->NewJSObject(function, TENURED);
186       CHECK(!heap->InNewSpace(*object));
187       CHECK(!first_page->Contains(object->address()));
188       int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
189       JSWeakCollection::Set(weakset, key, object, hash);
190     }
191   }
192 
193   // Force compacting garbage collection.
194   CHECK(FLAG_always_compact);
195   heap->CollectAllGarbage();
196 }
197 
198 
199 // Test that weak set keys on an evacuation candidate which are reachable by
200 // other strong paths are correctly recorded in the slots buffer.
TEST(WeakSet_Regress2060b)201 TEST(WeakSet_Regress2060b) {
202   if (i::FLAG_never_compact) return;
203   FLAG_always_compact = true;
204 #ifdef VERIFY_HEAP
205   FLAG_verify_heap = true;
206 #endif
207 
208   LocalContext context;
209   Isolate* isolate = GetIsolateFrom(&context);
210   Factory* factory = isolate->factory();
211   Heap* heap = isolate->heap();
212   HandleScope scope(isolate);
213   Handle<JSFunction> function = factory->NewFunction(
214       factory->function_string());
215 
216   // Start second old-space page so that keys land on evacuation candidate.
217   Page* first_page = heap->old_space()->anchor()->next_page();
218   heap::SimulateFullSpace(heap->old_space());
219 
220   // Fill up weak set with keys on an evacuation candidate.
221   Handle<JSObject> keys[32];
222   for (int i = 0; i < 32; i++) {
223     keys[i] = factory->NewJSObject(function, TENURED);
224     CHECK(!heap->InNewSpace(*keys[i]));
225     CHECK(!first_page->Contains(keys[i]->address()));
226   }
227   Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
228   for (int i = 0; i < 32; i++) {
229     Handle<Smi> smi(Smi::FromInt(i), isolate);
230     int32_t hash = Object::GetOrCreateHash(isolate, keys[i])->value();
231     JSWeakCollection::Set(weakset, keys[i], smi, hash);
232   }
233 
234   // Force compacting garbage collection. The subsequent collections are used
235   // to verify that key references were actually updated.
236   CHECK(FLAG_always_compact);
237   heap->CollectAllGarbage();
238   heap->CollectAllGarbage();
239   heap->CollectAllGarbage();
240 }
241