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