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