v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-weak-refs-inl.h
Go to the documentation of this file.
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
9// Include the non-inl header before the rest of the headers.
10
11#include "src/api/api-inl.h"
14#include "src/objects/smi-inl.h"
15
16// Has to be the last include (doesn't have include guards):
18
19namespace v8 {
20namespace internal {
21
22#include "torque-generated/src/objects/js-weak-refs-tq-inl.inc"
23
26TQ_OBJECT_CONSTRUCTORS_IMPL(JSFinalizationRegistry)
27
28BIT_FIELD_ACCESSORS(JSFinalizationRegistry, flags, scheduled_for_cleanup,
29 JSFinalizationRegistry::ScheduledForCleanupBit)
30
31void JSFinalizationRegistry::RegisterWeakCellWithUnregisterToken(
32 DirectHandle<JSFinalizationRegistry> finalization_registry,
33 DirectHandle<WeakCell> weak_cell, Isolate* isolate) {
35 if (IsUndefined(finalization_registry->key_map(), isolate)) {
36 key_map = SimpleNumberDictionary::New(isolate, 1);
37 } else {
38 key_map =
39 handle(Cast<SimpleNumberDictionary>(finalization_registry->key_map()),
40 isolate);
41 }
42
43 // Unregister tokens are held weakly as objects are often their own
44 // unregister token. To avoid using an ephemeron map, the map for token
45 // lookup is keyed on the token's identity hash instead of the token itself.
46 uint32_t key =
47 Object::GetOrCreateHash(weak_cell->unregister_token(), isolate).value();
48 InternalIndex entry = key_map->FindEntry(isolate, key);
49 if (entry.is_found()) {
50 Tagged<Object> value = key_map->ValueAt(entry);
51 Tagged<WeakCell> existing_weak_cell = Cast<WeakCell>(value);
52 existing_weak_cell->set_key_list_prev(*weak_cell);
53 weak_cell->set_key_list_next(existing_weak_cell);
54 }
55 key_map = SimpleNumberDictionary::Set(isolate, key_map, key, weak_cell);
56 finalization_registry->set_key_map(*key_map);
57}
58
60 DirectHandle<JSFinalizationRegistry> finalization_registry,
61 DirectHandle<HeapObject> unregister_token, Isolate* isolate) {
62 // Iterate through the doubly linked list of WeakCells associated with the
63 // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
64 // its FinalizationRegistry; remove it from there.
65 return finalization_registry->RemoveUnregisterToken(
66 *unregister_token, isolate, kRemoveMatchedCellsFromRegistry,
68}
69
70template <typename GCNotifyUpdatedSlotCallback>
72 Tagged<HeapObject> unregister_token, Isolate* isolate,
73 RemoveUnregisterTokenMode removal_mode,
74 GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
75 // This method is called from both FinalizationRegistry#unregister and for
76 // removing weakly-held dead unregister tokens. The latter is during GC so
77 // this function cannot GC.
79 if (IsUndefined(key_map(), isolate)) {
80 return false;
81 }
82
84 Cast<SimpleNumberDictionary>(this->key_map());
85 // If the token doesn't have a hash, it was not used as a key inside any hash
86 // tables.
87 Tagged<Object> hash = Object::GetHash(unregister_token);
88 if (IsUndefined(hash, isolate)) {
89 return false;
90 }
91 uint32_t key = Smi::ToInt(hash);
92 InternalIndex entry = key_map->FindEntry(isolate, key);
93 if (entry.is_not_found()) {
94 return false;
95 }
96
97 Tagged<Object> value = key_map->ValueAt(entry);
98 bool was_present = false;
99 Tagged<Undefined> undefined = ReadOnlyRoots(isolate).undefined_value();
102 // Compute a new key list that doesn't have unregister_token. Because
103 // unregister tokens are held weakly, key_map is keyed using the tokens'
104 // identity hashes, and identity hashes may collide.
105 while (!IsUndefined(value, isolate)) {
106 Tagged<WeakCell> weak_cell = Cast<WeakCell>(value);
108 value = weak_cell->key_list_next();
109 if (weak_cell->unregister_token() == unregister_token) {
110 // weak_cell has the same unregister token; remove it from the key list.
111 switch (removal_mode) {
113 weak_cell->RemoveFromFinalizationRegistryCells(isolate);
114 break;
116 // Do nothing.
117 break;
118 }
119 // Clear unregister token-related fields.
120 weak_cell->set_unregister_token(undefined);
121 weak_cell->set_key_list_prev(undefined);
122 weak_cell->set_key_list_next(undefined);
123 was_present = true;
124 } else {
125 // weak_cell has a different unregister token with the same key (hash
126 // collision); fix up the list.
127 weak_cell->set_key_list_prev(new_key_list_prev);
128 gc_notify_updated_slot(weak_cell,
129 weak_cell->RawField(WeakCell::kKeyListPrevOffset),
130 new_key_list_prev);
131 weak_cell->set_key_list_next(undefined);
132 if (IsUndefined(new_key_list_prev, isolate)) {
133 new_key_list_head = weak_cell;
134 } else {
135 DCHECK(IsWeakCell(new_key_list_head));
136 Tagged<WeakCell> prev_cell = Cast<WeakCell>(new_key_list_prev);
137 prev_cell->set_key_list_next(weak_cell);
138 gc_notify_updated_slot(
139 prev_cell, prev_cell->RawField(WeakCell::kKeyListNextOffset),
140 weak_cell);
141 }
142 new_key_list_prev = weak_cell;
143 }
144 }
145 if (IsUndefined(new_key_list_head, isolate)) {
146 DCHECK(was_present);
147 key_map->ClearEntry(entry);
148 key_map->ElementRemoved();
149 } else {
150 key_map->ValueAtPut(entry, new_key_list_head);
151 gc_notify_updated_slot(key_map, key_map->RawFieldOfValueAt(entry),
152 new_key_list_head);
153 }
154 return was_present;
155}
156
158 return IsWeakCell(cleared_cells());
159}
160
164
168
169template <typename GCNotifyUpdatedSlotCallback>
171 GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
172 // Remove from the WeakCell from the "active_cells" list of its
173 // JSFinalizationRegistry and insert it into the "cleared_cells" list. This is
174 // only called for WeakCells which haven't been unregistered yet, so they will
175 // be in the active_cells list. (The caller must guard against calling this
176 // for unregistered WeakCells by checking that the target is not undefined.)
178 set_target(ReadOnlyRoots(isolate).undefined_value());
179
181 Cast<JSFinalizationRegistry>(finalization_registry());
182 if (IsWeakCell(prev())) {
183 DCHECK_NE(fr->active_cells(), *this);
184 Tagged<WeakCell> prev_cell = Cast<WeakCell>(prev());
185 prev_cell->set_next(next());
186 gc_notify_updated_slot(prev_cell,
187 prev_cell->RawField(WeakCell::kNextOffset), next());
188 } else {
189 DCHECK_EQ(fr->active_cells(), *this);
190 fr->set_active_cells(next());
191 gc_notify_updated_slot(
192 fr, fr->RawField(JSFinalizationRegistry::kActiveCellsOffset), next());
193 }
194 if (IsWeakCell(next())) {
195 Tagged<WeakCell> next_cell = Cast<WeakCell>(next());
196 next_cell->set_prev(prev());
197 gc_notify_updated_slot(next_cell,
198 next_cell->RawField(WeakCell::kPrevOffset), prev());
199 }
200
201 set_prev(ReadOnlyRoots(isolate).undefined_value());
202 Tagged<UnionOf<Undefined, WeakCell>> cleared_head = fr->cleared_cells();
203 if (IsWeakCell(cleared_head)) {
204 Tagged<WeakCell> cleared_head_cell = Cast<WeakCell>(cleared_head);
205 cleared_head_cell->set_prev(*this);
206 gc_notify_updated_slot(cleared_head_cell,
207 cleared_head_cell->RawField(WeakCell::kPrevOffset),
208 *this);
209 }
210 set_next(fr->cleared_cells());
211 gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next());
212 fr->set_cleared_cells(*this);
213 gc_notify_updated_slot(
214 fr, fr->RawField(JSFinalizationRegistry::kClearedCellsOffset), *this);
215}
216
218 // Remove the WeakCell from the list it's in (either "active_cells" or
219 // "cleared_cells" of its JSFinalizationRegistry).
220
221 // It's important to set_target to undefined here. This guards that we won't
222 // call Nullify (which assumes that the WeakCell is in active_cells).
223 DCHECK(IsUndefined(target()) || Object::CanBeHeldWeakly(target()));
224 set_target(ReadOnlyRoots(isolate).undefined_value());
225
227 Cast<JSFinalizationRegistry>(finalization_registry());
228 if (fr->active_cells() == *this) {
229 DCHECK(IsUndefined(prev(), isolate));
230 fr->set_active_cells(next());
231 } else if (fr->cleared_cells() == *this) {
232 DCHECK(!IsWeakCell(prev()));
233 fr->set_cleared_cells(next());
234 } else {
235 DCHECK(IsWeakCell(prev()));
236 Tagged<WeakCell> prev_cell = Cast<WeakCell>(prev());
237 prev_cell->set_next(next());
238 }
239 if (IsWeakCell(next())) {
240 Tagged<WeakCell> next_cell = Cast<WeakCell>(next());
241 next_cell->set_prev(prev());
242 }
243 set_prev(ReadOnlyRoots(isolate).undefined_value());
244 set_next(ReadOnlyRoots(isolate).undefined_value());
245}
246
247} // namespace internal
248} // namespace v8
249
251
252#endif // V8_OBJECTS_JS_WEAK_REFS_INL_H_
uint32_t GetHash()
Definition api.cc:4282
static V8_INLINE bool InYoungGeneration(Tagged< Object > object)
static bool Unregister(DirectHandle< JSFinalizationRegistry > finalization_registry, DirectHandle< HeapObject > unregister_token, Isolate *isolate)
bool RemoveUnregisterToken(Tagged< HeapObject > unregister_token, Isolate *isolate, RemoveUnregisterTokenMode removal_mode, GCNotifyUpdatedSlotCallback gc_notify_updated_slot)
static bool CanBeHeldWeakly(Tagged< Object > obj)
static V8_EXPORT_PRIVATE Tagged< Smi > GetOrCreateHash(Tagged< Object > obj, Isolate *isolate)
Definition objects.cc:1696
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Handle< SimpleNumberDictionary > Set(Isolate *isolate, Handle< SimpleNumberDictionary > dictionary, uint32_t key, DirectHandle< Object > value)
Definition objects.cc:5852
static constexpr int ToInt(const Tagged< Object > object)
Definition smi.h:33
static PtrType Relaxed_Load(Tagged< HeapObject > host, int offset=0)
V8_INLINE constexpr int32_t value() const
Definition tagged.h:427
Tagged< HeapObject > relaxed_unregister_token() const
void Nullify(Isolate *isolate, GCNotifyUpdatedSlotCallback gc_notify_updated_slot)
Tagged< HeapObject > relaxed_target() const
void RemoveFromFinalizationRegistryCells(Isolate *isolate)
TNode< Object > target
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define TQ_OBJECT_CONSTRUCTORS_IMPL(Type)
#define BIT_FIELD_ACCESSORS(holder, field, name, BitField)
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485