• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/api-inl.h"
11 #include "src/heap/heap-write-barrier-inl.h"
12 #include "src/objects/smi-inl.h"
13 
14 // Has to be the last include (doesn't have include guards):
15 #include "src/objects/object-macros.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 #include "torque-generated/src/objects/js-weak-refs-tq-inl.inc"
21 
22 TQ_OBJECT_CONSTRUCTORS_IMPL(WeakCell)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSWeakRef)23 TQ_OBJECT_CONSTRUCTORS_IMPL(JSWeakRef)
24 OBJECT_CONSTRUCTORS_IMPL(JSFinalizationRegistry, JSObject)
25 
26 ACCESSORS(JSFinalizationRegistry, native_context, NativeContext,
27           kNativeContextOffset)
28 ACCESSORS(JSFinalizationRegistry, cleanup, Object, kCleanupOffset)
29 ACCESSORS(JSFinalizationRegistry, active_cells, HeapObject, kActiveCellsOffset)
30 ACCESSORS(JSFinalizationRegistry, cleared_cells, HeapObject,
31           kClearedCellsOffset)
32 ACCESSORS(JSFinalizationRegistry, key_map, Object, kKeyMapOffset)
33 SMI_ACCESSORS(JSFinalizationRegistry, flags, kFlagsOffset)
34 ACCESSORS(JSFinalizationRegistry, next_dirty, Object, kNextDirtyOffset)
35 CAST_ACCESSOR(JSFinalizationRegistry)
36 
37 BIT_FIELD_ACCESSORS(JSFinalizationRegistry, flags, scheduled_for_cleanup,
38                     JSFinalizationRegistry::ScheduledForCleanupBit)
39 
40 void JSFinalizationRegistry::RegisterWeakCellWithUnregisterToken(
41     Handle<JSFinalizationRegistry> finalization_registry,
42     Handle<WeakCell> weak_cell, Isolate* isolate) {
43   Handle<SimpleNumberDictionary> key_map;
44   if (finalization_registry->key_map().IsUndefined(isolate)) {
45     key_map = SimpleNumberDictionary::New(isolate, 1);
46   } else {
47     key_map =
48         handle(SimpleNumberDictionary::cast(finalization_registry->key_map()),
49                isolate);
50   }
51 
52   // Unregister tokens are held weakly as objects are often their own
53   // unregister token. To avoid using an ephemeron map, the map for token
54   // lookup is keyed on the token's identity hash instead of the token itself.
55   uint32_t key = weak_cell->unregister_token().GetOrCreateHash(isolate).value();
56   InternalIndex entry = key_map->FindEntry(isolate, key);
57   if (entry.is_found()) {
58     Object value = key_map->ValueAt(entry);
59     WeakCell existing_weak_cell = WeakCell::cast(value);
60     existing_weak_cell.set_key_list_prev(*weak_cell);
61     weak_cell->set_key_list_next(existing_weak_cell);
62   }
63   key_map = SimpleNumberDictionary::Set(isolate, key_map, key, weak_cell);
64   finalization_registry->set_key_map(*key_map);
65 }
66 
Unregister(Handle<JSFinalizationRegistry> finalization_registry,Handle<JSReceiver> unregister_token,Isolate * isolate)67 bool JSFinalizationRegistry::Unregister(
68     Handle<JSFinalizationRegistry> finalization_registry,
69     Handle<JSReceiver> unregister_token, Isolate* isolate) {
70   // Iterate through the doubly linked list of WeakCells associated with the
71   // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
72   // its FinalizationRegistry; remove it from there.
73   return finalization_registry->RemoveUnregisterToken(
74       *unregister_token, isolate,
75       [isolate](WeakCell matched_cell) {
76         matched_cell.RemoveFromFinalizationRegistryCells(isolate);
77       },
78       [](HeapObject, ObjectSlot, Object) {});
79 }
80 
81 template <typename MatchCallback, typename GCNotifyUpdatedSlotCallback>
RemoveUnregisterToken(JSReceiver unregister_token,Isolate * isolate,MatchCallback match_callback,GCNotifyUpdatedSlotCallback gc_notify_updated_slot)82 bool JSFinalizationRegistry::RemoveUnregisterToken(
83     JSReceiver unregister_token, Isolate* isolate, MatchCallback match_callback,
84     GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
85   // This method is called from both FinalizationRegistry#unregister and for
86   // removing weakly-held dead unregister tokens. The latter is during GC so
87   // this function cannot GC.
88   DisallowHeapAllocation no_gc;
89   if (key_map().IsUndefined(isolate)) {
90     return false;
91   }
92 
93   SimpleNumberDictionary key_map =
94       SimpleNumberDictionary::cast(this->key_map());
95   // If the token doesn't have a hash, it was not used as a key inside any hash
96   // tables.
97   Object hash = unregister_token.GetHash();
98   if (hash.IsUndefined(isolate)) {
99     return false;
100   }
101   uint32_t key = Smi::ToInt(hash);
102   InternalIndex entry = key_map.FindEntry(isolate, key);
103   if (entry.is_not_found()) {
104     return false;
105   }
106 
107   Object value = key_map.ValueAt(entry);
108   bool was_present = false;
109   HeapObject undefined = ReadOnlyRoots(isolate).undefined_value();
110   HeapObject new_key_list_head = undefined;
111   HeapObject new_key_list_prev = undefined;
112   // Compute a new key list that doesn't have unregister_token. Because
113   // unregister tokens are held weakly, key_map is keyed using the tokens'
114   // identity hashes, and identity hashes may collide.
115   while (!value.IsUndefined(isolate)) {
116     WeakCell weak_cell = WeakCell::cast(value);
117     DCHECK(!ObjectInYoungGeneration(weak_cell));
118     value = weak_cell.key_list_next();
119     if (weak_cell.unregister_token() == unregister_token) {
120       // weak_cell has the same unregister token; remove it from the key list.
121       match_callback(weak_cell);
122       weak_cell.set_key_list_prev(undefined);
123       weak_cell.set_key_list_next(undefined);
124       was_present = true;
125     } else {
126       // weak_cell has a different unregister token with the same key (hash
127       // collision); fix up the list.
128       weak_cell.set_key_list_prev(new_key_list_prev);
129       gc_notify_updated_slot(weak_cell,
130                              weak_cell.RawField(WeakCell::kKeyListPrevOffset),
131                              new_key_list_prev);
132       weak_cell.set_key_list_next(undefined);
133       if (new_key_list_prev.IsUndefined(isolate)) {
134         new_key_list_head = weak_cell;
135       } else {
136         DCHECK(new_key_list_head.IsWeakCell());
137         WeakCell prev_cell = WeakCell::cast(new_key_list_prev);
138         prev_cell.set_key_list_next(weak_cell);
139         gc_notify_updated_slot(prev_cell,
140                                prev_cell.RawField(WeakCell::kKeyListNextOffset),
141                                weak_cell);
142       }
143       new_key_list_prev = weak_cell;
144     }
145   }
146   if (new_key_list_head.IsUndefined(isolate)) {
147     DCHECK(was_present);
148     key_map.ClearEntry(entry);
149     key_map.ElementRemoved();
150   } else {
151     key_map.ValueAtPut(entry, new_key_list_head);
152     gc_notify_updated_slot(key_map, key_map.RawFieldOfValueAt(entry),
153                            new_key_list_head);
154   }
155   return was_present;
156 }
157 
NeedsCleanup()158 bool JSFinalizationRegistry::NeedsCleanup() const {
159   return cleared_cells().IsWeakCell();
160 }
161 
relaxed_target()162 HeapObject WeakCell::relaxed_target() const {
163   return TaggedField<HeapObject>::Relaxed_Load(*this, kTargetOffset);
164 }
165 
166 template <typename GCNotifyUpdatedSlotCallback>
Nullify(Isolate * isolate,GCNotifyUpdatedSlotCallback gc_notify_updated_slot)167 void WeakCell::Nullify(Isolate* isolate,
168                        GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
169   // Remove from the WeakCell from the "active_cells" list of its
170   // JSFinalizationRegistry and insert it into the "cleared_cells" list. This is
171   // only called for WeakCells which haven't been unregistered yet, so they will
172   // be in the active_cells list. (The caller must guard against calling this
173   // for unregistered WeakCells by checking that the target is not undefined.)
174   DCHECK(target().IsJSReceiver());
175   set_target(ReadOnlyRoots(isolate).undefined_value());
176 
177   JSFinalizationRegistry fr =
178       JSFinalizationRegistry::cast(finalization_registry());
179   if (prev().IsWeakCell()) {
180     DCHECK_NE(fr.active_cells(), *this);
181     WeakCell prev_cell = WeakCell::cast(prev());
182     prev_cell.set_next(next());
183     gc_notify_updated_slot(prev_cell, prev_cell.RawField(WeakCell::kNextOffset),
184                            next());
185   } else {
186     DCHECK_EQ(fr.active_cells(), *this);
187     fr.set_active_cells(next());
188     gc_notify_updated_slot(
189         fr, fr.RawField(JSFinalizationRegistry::kActiveCellsOffset), next());
190   }
191   if (next().IsWeakCell()) {
192     WeakCell next_cell = WeakCell::cast(next());
193     next_cell.set_prev(prev());
194     gc_notify_updated_slot(next_cell, next_cell.RawField(WeakCell::kPrevOffset),
195                            prev());
196   }
197 
198   set_prev(ReadOnlyRoots(isolate).undefined_value());
199   Object cleared_head = fr.cleared_cells();
200   if (cleared_head.IsWeakCell()) {
201     WeakCell cleared_head_cell = WeakCell::cast(cleared_head);
202     cleared_head_cell.set_prev(*this);
203     gc_notify_updated_slot(cleared_head_cell,
204                            cleared_head_cell.RawField(WeakCell::kPrevOffset),
205                            *this);
206   }
207   set_next(fr.cleared_cells());
208   gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next());
209   fr.set_cleared_cells(*this);
210   gc_notify_updated_slot(
211       fr, fr.RawField(JSFinalizationRegistry::kClearedCellsOffset), *this);
212 }
213 
RemoveFromFinalizationRegistryCells(Isolate * isolate)214 void WeakCell::RemoveFromFinalizationRegistryCells(Isolate* isolate) {
215   // Remove the WeakCell from the list it's in (either "active_cells" or
216   // "cleared_cells" of its JSFinalizationRegistry).
217 
218   // It's important to set_target to undefined here. This guards that we won't
219   // call Nullify (which assumes that the WeakCell is in active_cells).
220   DCHECK(target().IsUndefined() || target().IsJSReceiver());
221   set_target(ReadOnlyRoots(isolate).undefined_value());
222 
223   JSFinalizationRegistry fr =
224       JSFinalizationRegistry::cast(finalization_registry());
225   if (fr.active_cells() == *this) {
226     DCHECK(prev().IsUndefined(isolate));
227     fr.set_active_cells(next());
228   } else if (fr.cleared_cells() == *this) {
229     DCHECK(!prev().IsWeakCell());
230     fr.set_cleared_cells(next());
231   } else {
232     DCHECK(prev().IsWeakCell());
233     WeakCell prev_cell = WeakCell::cast(prev());
234     prev_cell.set_next(next());
235   }
236   if (next().IsWeakCell()) {
237     WeakCell next_cell = WeakCell::cast(next());
238     next_cell.set_prev(prev());
239   }
240   set_prev(ReadOnlyRoots(isolate).undefined_value());
241   set_next(ReadOnlyRoots(isolate).undefined_value());
242 }
243 
244 }  // namespace internal
245 }  // namespace v8
246 
247 #include "src/objects/object-macros-undef.h"
248 
249 #endif  // V8_OBJECTS_JS_WEAK_REFS_INL_H_
250