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