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