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