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 25880 : TEST(WeakSet_Weakness) {
75 5 : FLAG_incremental_marking = false;
76 5 : LocalContext context;
77 5 : 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 5 : 1, EphemeronHashTable::cast(weakset->table())->NumberOfDeletedElements());
121 5 : }
122 :
123 :
124 25875 : 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 25880 : 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 5 : 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 165 : for (int i = 0; i < 32; i++) {
187 160 : Handle<JSObject> object = factory->NewJSObject(function, TENURED);
188 160 : CHECK(!Heap::InYoungGeneration(*object));
189 160 : CHECK(!first_page->Contains(object->address()));
190 320 : int32_t hash = key->GetOrCreateHash(isolate)->value();
191 160 : JSWeakCollection::Set(weakset, key, object, hash);
192 : }
193 : }
194 :
195 : // Force compacting garbage collection.
196 5 : CHECK(FLAG_always_compact);
197 10 : CcTest::CollectAllGarbage();
198 : }
199 :
200 :
201 : // Test that weak set keys on an evacuation candidate which are reachable by
202 : // other strong paths are correctly recorded in the slots buffer.
203 25880 : TEST(WeakSet_Regress2060b) {
204 5 : if (i::FLAG_never_compact) return;
205 5 : FLAG_always_compact = true;
206 : #ifdef VERIFY_HEAP
207 : FLAG_verify_heap = true;
208 : #endif
209 :
210 5 : LocalContext context;
211 : Isolate* isolate = GetIsolateFrom(&context);
212 : Factory* factory = isolate->factory();
213 5 : Heap* heap = isolate->heap();
214 : HandleScope scope(isolate);
215 : Handle<JSFunction> function =
216 5 : factory->NewFunctionForTest(factory->function_string());
217 :
218 : // Start second old-space page so that keys land on evacuation candidate.
219 : Page* first_page = heap->old_space()->first_page();
220 5 : heap::SimulateFullSpace(heap->old_space());
221 :
222 : // Fill up weak set with keys on an evacuation candidate.
223 165 : Handle<JSObject> keys[32];
224 160 : for (int i = 0; i < 32; i++) {
225 160 : keys[i] = factory->NewJSObject(function, TENURED);
226 320 : CHECK(!Heap::InYoungGeneration(*keys[i]));
227 160 : CHECK(!first_page->Contains(keys[i]->address()));
228 : }
229 5 : Handle<JSWeakSet> weakset = AllocateJSWeakSet(isolate);
230 165 : for (int i = 0; i < 32; i++) {
231 : Handle<Smi> smi(Smi::FromInt(i), isolate);
232 480 : int32_t hash = keys[i]->GetOrCreateHash(isolate)->value();
233 160 : JSWeakCollection::Set(weakset, keys[i], smi, hash);
234 : }
235 :
236 : // Force compacting garbage collection. The subsequent collections are used
237 : // to verify that key references were actually updated.
238 5 : CHECK(FLAG_always_compact);
239 5 : CcTest::CollectAllGarbage();
240 5 : CcTest::CollectAllGarbage();
241 10 : CcTest::CollectAllGarbage();
242 : }
243 :
244 : } // namespace test_weaksets
245 : } // namespace internal
246 77625 : } // namespace v8
|