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