Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_OBJECTS_JS_WEAK_REFS_INL_H_
6 : #define V8_OBJECTS_JS_WEAK_REFS_INL_H_
7 :
8 : #include "src/objects/js-weak-refs.h"
9 :
10 : #include "src/api-inl.h"
11 : #include "src/heap/heap-write-barrier-inl.h"
12 : #include "src/objects/microtask-inl.h"
13 : #include "src/objects/smi-inl.h"
14 :
15 : // Has to be the last include (doesn't have include guards):
16 : #include "src/objects/object-macros.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 :
21 3790 : OBJECT_CONSTRUCTORS_IMPL(WeakCell, HeapObject)
22 876 : OBJECT_CONSTRUCTORS_IMPL(JSWeakRef, JSObject)
23 4720 : OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroup, JSObject)
24 1116 : OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroupCleanupIterator, JSObject)
25 414 : OBJECT_CONSTRUCTORS_IMPL(FinalizationGroupCleanupJobTask, Microtask)
26 :
27 2111 : ACCESSORS(JSFinalizationGroup, native_context, NativeContext,
28 : kNativeContextOffset)
29 1679 : ACCESSORS(JSFinalizationGroup, cleanup, Object, kCleanupOffset)
30 5094 : ACCESSORS(JSFinalizationGroup, active_cells, Object, kActiveCellsOffset)
31 4949 : ACCESSORS(JSFinalizationGroup, cleared_cells, Object, kClearedCellsOffset)
32 2566 : ACCESSORS(JSFinalizationGroup, key_map, Object, kKeyMapOffset)
33 2062 : SMI_ACCESSORS(JSFinalizationGroup, flags, kFlagsOffset)
34 2277 : ACCESSORS(JSFinalizationGroup, next, Object, kNextOffset)
35 2360 : CAST_ACCESSOR(JSFinalizationGroup)
36 :
37 2894 : ACCESSORS(WeakCell, finalization_group, Object, kFinalizationGroupOffset)
38 7279 : ACCESSORS(WeakCell, target, HeapObject, kTargetOffset)
39 2457 : ACCESSORS(WeakCell, holdings, Object, kHoldingsOffset)
40 9975 : ACCESSORS(WeakCell, next, Object, kNextOffset)
41 6480 : ACCESSORS(WeakCell, prev, Object, kPrevOffset)
42 2517 : ACCESSORS(WeakCell, key, Object, kKeyOffset)
43 3207 : ACCESSORS(WeakCell, key_list_next, Object, kKeyListNextOffset)
44 3110 : ACCESSORS(WeakCell, key_list_prev, Object, kKeyListPrevOffset)
45 1895 : CAST_ACCESSOR(WeakCell)
46 :
47 438 : CAST_ACCESSOR(JSWeakRef)
48 3734 : ACCESSORS(JSWeakRef, target, HeapObject, kTargetOffset)
49 :
50 2052 : ACCESSORS(JSFinalizationGroupCleanupIterator, finalization_group,
51 : JSFinalizationGroup, kFinalizationGroupOffset)
52 558 : CAST_ACCESSOR(JSFinalizationGroupCleanupIterator)
53 :
54 1035 : ACCESSORS(FinalizationGroupCleanupJobTask, finalization_group,
55 : JSFinalizationGroup, kFinalizationGroupOffset)
56 207 : CAST_ACCESSOR(FinalizationGroupCleanupJobTask)
57 :
58 444 : void JSFinalizationGroup::Register(
59 : Handle<JSFinalizationGroup> finalization_group, Handle<JSReceiver> target,
60 : Handle<Object> holdings, Handle<Object> key, Isolate* isolate) {
61 444 : Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell();
62 888 : weak_cell->set_finalization_group(*finalization_group);
63 888 : weak_cell->set_target(*target);
64 444 : weak_cell->set_holdings(*holdings);
65 888 : weak_cell->set_prev(ReadOnlyRoots(isolate).undefined_value());
66 888 : weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());
67 444 : weak_cell->set_key(*key);
68 888 : weak_cell->set_key_list_prev(ReadOnlyRoots(isolate).undefined_value());
69 888 : weak_cell->set_key_list_next(ReadOnlyRoots(isolate).undefined_value());
70 :
71 : // Add to active_cells.
72 444 : weak_cell->set_next(finalization_group->active_cells());
73 888 : if (finalization_group->active_cells()->IsWeakCell()) {
74 248 : WeakCell::cast(finalization_group->active_cells())->set_prev(*weak_cell);
75 : }
76 888 : finalization_group->set_active_cells(*weak_cell);
77 :
78 888 : if (!key->IsUndefined(isolate)) {
79 : Handle<ObjectHashTable> key_map;
80 384 : if (finalization_group->key_map()->IsUndefined(isolate)) {
81 120 : key_map = ObjectHashTable::New(isolate, 1);
82 : } else {
83 : key_map =
84 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
85 : }
86 :
87 192 : Object value = key_map->Lookup(key);
88 192 : if (value->IsWeakCell()) {
89 39 : WeakCell existing_weak_cell = WeakCell::cast(value);
90 39 : existing_weak_cell->set_key_list_prev(*weak_cell);
91 39 : weak_cell->set_key_list_next(existing_weak_cell);
92 : } else {
93 : DCHECK(value->IsTheHole(isolate));
94 : }
95 192 : key_map = ObjectHashTable::Put(key_map, key, weak_cell);
96 384 : finalization_group->set_key_map(*key_map);
97 : }
98 444 : }
99 :
100 138 : void JSFinalizationGroup::Unregister(
101 : Handle<JSFinalizationGroup> finalization_group, Handle<Object> key,
102 : Isolate* isolate) {
103 : // Iterate through the doubly linked list of WeakCells associated with the
104 : // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
105 : // its FinalizationGroup; remove it from there.
106 276 : if (!finalization_group->key_map()->IsUndefined(isolate)) {
107 : Handle<ObjectHashTable> key_map =
108 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
109 124 : Object value = key_map->Lookup(key);
110 124 : Object undefined = ReadOnlyRoots(isolate).undefined_value();
111 345 : while (value->IsWeakCell()) {
112 97 : WeakCell weak_cell = WeakCell::cast(value);
113 97 : weak_cell->RemoveFromFinalizationGroupCells(isolate);
114 97 : value = weak_cell->key_list_next();
115 97 : weak_cell->set_key_list_prev(undefined);
116 97 : weak_cell->set_key_list_next(undefined);
117 : }
118 : bool was_present;
119 124 : key_map = ObjectHashTable::Remove(isolate, key_map, key, &was_present);
120 248 : finalization_group->set_key_map(*key_map);
121 : }
122 138 : }
123 :
124 399 : bool JSFinalizationGroup::NeedsCleanup() const {
125 798 : return cleared_cells()->IsWeakCell();
126 : }
127 :
128 261 : bool JSFinalizationGroup::scheduled_for_cleanup() const {
129 522 : return ScheduledForCleanupField::decode(flags());
130 : }
131 :
132 414 : void JSFinalizationGroup::set_scheduled_for_cleanup(
133 : bool scheduled_for_cleanup) {
134 828 : set_flags(ScheduledForCleanupField::update(flags(), scheduled_for_cleanup));
135 414 : }
136 :
137 237 : Object JSFinalizationGroup::PopClearedCellHoldings(
138 : Handle<JSFinalizationGroup> finalization_group, Isolate* isolate) {
139 : Handle<WeakCell> weak_cell =
140 : handle(WeakCell::cast(finalization_group->cleared_cells()), isolate);
141 : DCHECK(weak_cell->prev()->IsUndefined(isolate));
142 237 : finalization_group->set_cleared_cells(weak_cell->next());
143 474 : weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());
144 :
145 474 : if (finalization_group->cleared_cells()->IsWeakCell()) {
146 : WeakCell cleared_cells_head =
147 73 : WeakCell::cast(finalization_group->cleared_cells());
148 : DCHECK_EQ(cleared_cells_head->prev(), *weak_cell);
149 73 : cleared_cells_head->set_prev(ReadOnlyRoots(isolate).undefined_value());
150 : } else {
151 : DCHECK(finalization_group->cleared_cells()->IsUndefined(isolate));
152 : }
153 :
154 : // Also remove the WeakCell from the key_map (if it's there).
155 474 : if (!weak_cell->key()->IsUndefined(isolate)) {
156 240 : if (weak_cell->key_list_prev()->IsUndefined(isolate) &&
157 120 : weak_cell->key_list_next()->IsUndefined(isolate)) {
158 : // weak_cell is the only one associated with its key; remove the key
159 : // from the hash table.
160 : Handle<ObjectHashTable> key_map =
161 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
162 55 : Handle<Object> key = handle(weak_cell->key(), isolate);
163 : bool was_present;
164 55 : key_map = ObjectHashTable::Remove(isolate, key_map, key, &was_present);
165 : DCHECK(was_present);
166 110 : finalization_group->set_key_map(*key_map);
167 10 : } else if (weak_cell->key_list_prev()->IsUndefined()) {
168 : // weak_cell is the list head for its key; we need to change the value of
169 : // the key in the hash table.
170 : Handle<ObjectHashTable> key_map =
171 : handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
172 5 : Handle<Object> key = handle(weak_cell->key(), isolate);
173 : Handle<WeakCell> next =
174 : handle(WeakCell::cast(weak_cell->key_list_next()), isolate);
175 : DCHECK_EQ(next->key_list_prev(), *weak_cell);
176 10 : next->set_key_list_prev(ReadOnlyRoots(isolate).undefined_value());
177 10 : weak_cell->set_key_list_next(ReadOnlyRoots(isolate).undefined_value());
178 5 : key_map = ObjectHashTable::Put(key_map, key, next);
179 10 : finalization_group->set_key_map(*key_map);
180 : } else {
181 : // weak_cell is somewhere in the middle of its key list.
182 0 : WeakCell prev = WeakCell::cast(weak_cell->key_list_prev());
183 0 : prev->set_key_list_next(weak_cell->key_list_next());
184 0 : if (!weak_cell->key_list_next()->IsUndefined()) {
185 0 : WeakCell next = WeakCell::cast(weak_cell->key_list_next());
186 0 : next->set_key_list_prev(weak_cell->key_list_prev());
187 : }
188 : }
189 : }
190 :
191 237 : return weak_cell->holdings();
192 : }
193 :
194 316 : void WeakCell::Nullify(
195 : Isolate* isolate,
196 : std::function<void(HeapObject object, ObjectSlot slot, Object target)>
197 : gc_notify_updated_slot) {
198 : // Remove from the WeakCell from the "active_cells" list of its
199 : // JSFinalizationGroup and insert it into the "cleared_cells" list. This is
200 : // only called for WeakCells which haven't been unregistered yet, so they will
201 : // be in the active_cells list. (The caller must guard against calling this
202 : // for unregistered WeakCells by checking that the target is not undefined.)
203 : DCHECK(target()->IsJSReceiver());
204 316 : set_target(ReadOnlyRoots(isolate).undefined_value());
205 :
206 316 : JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group());
207 632 : if (prev()->IsWeakCell()) {
208 : DCHECK_NE(fg->active_cells(), *this);
209 55 : WeakCell prev_cell = WeakCell::cast(prev());
210 55 : prev_cell->set_next(next());
211 : gc_notify_updated_slot(prev_cell, prev_cell.RawField(WeakCell::kNextOffset),
212 55 : next());
213 : } else {
214 : DCHECK_EQ(fg->active_cells(), *this);
215 261 : fg->set_active_cells(next());
216 : gc_notify_updated_slot(
217 261 : fg, fg.RawField(JSFinalizationGroup::kActiveCellsOffset), next());
218 : }
219 632 : if (next()->IsWeakCell()) {
220 21 : WeakCell next_cell = WeakCell::cast(next());
221 21 : next_cell->set_prev(prev());
222 : gc_notify_updated_slot(next_cell, next_cell.RawField(WeakCell::kPrevOffset),
223 21 : prev());
224 : }
225 :
226 316 : set_prev(ReadOnlyRoots(isolate).undefined_value());
227 316 : Object cleared_head = fg->cleared_cells();
228 316 : if (cleared_head->IsWeakCell()) {
229 83 : WeakCell cleared_head_cell = WeakCell::cast(cleared_head);
230 83 : cleared_head_cell->set_prev(*this);
231 : gc_notify_updated_slot(cleared_head_cell,
232 : cleared_head_cell.RawField(WeakCell::kPrevOffset),
233 83 : *this);
234 : }
235 316 : set_next(fg->cleared_cells());
236 316 : gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next());
237 316 : fg->set_cleared_cells(*this);
238 : gc_notify_updated_slot(
239 316 : fg, fg.RawField(JSFinalizationGroup::kClearedCellsOffset), *this);
240 316 : }
241 :
242 97 : void WeakCell::RemoveFromFinalizationGroupCells(Isolate* isolate) {
243 : // Remove the WeakCell from the list it's in (either "active_cells" or
244 : // "cleared_cells" of its JSFinalizationGroup).
245 :
246 : // It's important to set_target to undefined here. This guards that we won't
247 : // call Nullify (which assumes that the WeakCell is in active_cells).
248 : DCHECK(target()->IsUndefined() || target()->IsJSReceiver());
249 97 : set_target(ReadOnlyRoots(isolate).undefined_value());
250 :
251 97 : JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group());
252 97 : if (fg->active_cells() == *this) {
253 : DCHECK(prev()->IsUndefined(isolate));
254 28 : fg->set_active_cells(next());
255 69 : } else if (fg->cleared_cells() == *this) {
256 : DCHECK(!prev()->IsWeakCell());
257 41 : fg->set_cleared_cells(next());
258 : } else {
259 : DCHECK(prev()->IsWeakCell());
260 28 : WeakCell prev_cell = WeakCell::cast(prev());
261 28 : prev_cell->set_next(next());
262 : }
263 194 : if (next()->IsWeakCell()) {
264 19 : WeakCell next_cell = WeakCell::cast(next());
265 19 : next_cell->set_prev(prev());
266 : }
267 97 : set_prev(ReadOnlyRoots(isolate).undefined_value());
268 97 : set_next(ReadOnlyRoots(isolate).undefined_value());
269 97 : }
270 :
271 : } // namespace internal
272 : } // namespace v8
273 :
274 : #include "src/objects/object-macros-undef.h"
275 :
276 : #endif // V8_OBJECTS_JS_WEAK_REFS_INL_H_
|