| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_OBJECTS_JS_WEAK_REFS_INL_H_ |
| #define V8_OBJECTS_JS_WEAK_REFS_INL_H_ |
| |
| #include "src/objects/js-weak-refs.h" |
| |
| #include "src/api-inl.h" |
| #include "src/heap/heap-write-barrier-inl.h" |
| #include "src/objects/microtask-inl.h" |
| #include "src/objects/smi-inl.h" |
| |
| // Has to be the last include (doesn't have include guards): |
| #include "src/objects/object-macros.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| OBJECT_CONSTRUCTORS_IMPL(WeakCell, HeapObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSWeakRef, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroup, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroupCleanupIterator, JSObject) |
| OBJECT_CONSTRUCTORS_IMPL(FinalizationGroupCleanupJobTask, Microtask) |
| |
| ACCESSORS(JSFinalizationGroup, native_context, NativeContext, |
| kNativeContextOffset) |
| ACCESSORS(JSFinalizationGroup, cleanup, Object, kCleanupOffset) |
| ACCESSORS(JSFinalizationGroup, active_cells, Object, kActiveCellsOffset) |
| ACCESSORS(JSFinalizationGroup, cleared_cells, Object, kClearedCellsOffset) |
| ACCESSORS(JSFinalizationGroup, key_map, Object, kKeyMapOffset) |
| SMI_ACCESSORS(JSFinalizationGroup, flags, kFlagsOffset) |
| ACCESSORS(JSFinalizationGroup, next, Object, kNextOffset) |
| CAST_ACCESSOR(JSFinalizationGroup) |
| |
| ACCESSORS(WeakCell, finalization_group, Object, kFinalizationGroupOffset) |
| ACCESSORS(WeakCell, target, HeapObject, kTargetOffset) |
| ACCESSORS(WeakCell, holdings, Object, kHoldingsOffset) |
| ACCESSORS(WeakCell, next, Object, kNextOffset) |
| ACCESSORS(WeakCell, prev, Object, kPrevOffset) |
| ACCESSORS(WeakCell, key, Object, kKeyOffset) |
| ACCESSORS(WeakCell, key_list_next, Object, kKeyListNextOffset) |
| ACCESSORS(WeakCell, key_list_prev, Object, kKeyListPrevOffset) |
| CAST_ACCESSOR(WeakCell) |
| |
| CAST_ACCESSOR(JSWeakRef) |
| ACCESSORS(JSWeakRef, target, HeapObject, kTargetOffset) |
| |
| ACCESSORS(JSFinalizationGroupCleanupIterator, finalization_group, |
| JSFinalizationGroup, kFinalizationGroupOffset) |
| CAST_ACCESSOR(JSFinalizationGroupCleanupIterator) |
| |
| ACCESSORS(FinalizationGroupCleanupJobTask, finalization_group, |
| JSFinalizationGroup, kFinalizationGroupOffset) |
| CAST_ACCESSOR(FinalizationGroupCleanupJobTask) |
| |
| void JSFinalizationGroup::Register( |
| Handle<JSFinalizationGroup> finalization_group, Handle<JSReceiver> target, |
| Handle<Object> holdings, Handle<Object> key, Isolate* isolate) { |
| Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(); |
| weak_cell->set_finalization_group(*finalization_group); |
| weak_cell->set_target(*target); |
| weak_cell->set_holdings(*holdings); |
| weak_cell->set_prev(ReadOnlyRoots(isolate).undefined_value()); |
| weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value()); |
| weak_cell->set_key(*key); |
| weak_cell->set_key_list_prev(ReadOnlyRoots(isolate).undefined_value()); |
| weak_cell->set_key_list_next(ReadOnlyRoots(isolate).undefined_value()); |
| |
| // Add to active_cells. |
| weak_cell->set_next(finalization_group->active_cells()); |
| if (finalization_group->active_cells()->IsWeakCell()) { |
| WeakCell::cast(finalization_group->active_cells())->set_prev(*weak_cell); |
| } |
| finalization_group->set_active_cells(*weak_cell); |
| |
| if (!key->IsUndefined(isolate)) { |
| Handle<ObjectHashTable> key_map; |
| if (finalization_group->key_map()->IsUndefined(isolate)) { |
| key_map = ObjectHashTable::New(isolate, 1); |
| } else { |
| key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| } |
| |
| Object value = key_map->Lookup(key); |
| if (value->IsWeakCell()) { |
| WeakCell existing_weak_cell = WeakCell::cast(value); |
| existing_weak_cell->set_key_list_prev(*weak_cell); |
| weak_cell->set_key_list_next(existing_weak_cell); |
| } else { |
| DCHECK(value->IsTheHole(isolate)); |
| } |
| key_map = ObjectHashTable::Put(key_map, key, weak_cell); |
| finalization_group->set_key_map(*key_map); |
| } |
| } |
| |
| void JSFinalizationGroup::Unregister( |
| Handle<JSFinalizationGroup> finalization_group, Handle<Object> key, |
| Isolate* isolate) { |
| // Iterate through the doubly linked list of WeakCells associated with the |
| // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of |
| // its FinalizationGroup; remove it from there. |
| if (!finalization_group->key_map()->IsUndefined(isolate)) { |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| Object value = key_map->Lookup(key); |
| Object undefined = ReadOnlyRoots(isolate).undefined_value(); |
| while (value->IsWeakCell()) { |
| WeakCell weak_cell = WeakCell::cast(value); |
| weak_cell->RemoveFromFinalizationGroupCells(isolate); |
| value = weak_cell->key_list_next(); |
| weak_cell->set_key_list_prev(undefined); |
| weak_cell->set_key_list_next(undefined); |
| } |
| bool was_present; |
| key_map = ObjectHashTable::Remove(isolate, key_map, key, &was_present); |
| finalization_group->set_key_map(*key_map); |
| } |
| } |
| |
| bool JSFinalizationGroup::NeedsCleanup() const { |
| return cleared_cells()->IsWeakCell(); |
| } |
| |
| bool JSFinalizationGroup::scheduled_for_cleanup() const { |
| return ScheduledForCleanupField::decode(flags()); |
| } |
| |
| void JSFinalizationGroup::set_scheduled_for_cleanup( |
| bool scheduled_for_cleanup) { |
| set_flags(ScheduledForCleanupField::update(flags(), scheduled_for_cleanup)); |
| } |
| |
| Object JSFinalizationGroup::PopClearedCellHoldings( |
| Handle<JSFinalizationGroup> finalization_group, Isolate* isolate) { |
| Handle<WeakCell> weak_cell = |
| handle(WeakCell::cast(finalization_group->cleared_cells()), isolate); |
| DCHECK(weak_cell->prev()->IsUndefined(isolate)); |
| finalization_group->set_cleared_cells(weak_cell->next()); |
| weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value()); |
| |
| if (finalization_group->cleared_cells()->IsWeakCell()) { |
| WeakCell cleared_cells_head = |
| WeakCell::cast(finalization_group->cleared_cells()); |
| DCHECK_EQ(cleared_cells_head->prev(), *weak_cell); |
| cleared_cells_head->set_prev(ReadOnlyRoots(isolate).undefined_value()); |
| } else { |
| DCHECK(finalization_group->cleared_cells()->IsUndefined(isolate)); |
| } |
| |
| // Also remove the WeakCell from the key_map (if it's there). |
| if (!weak_cell->key()->IsUndefined(isolate)) { |
| if (weak_cell->key_list_prev()->IsUndefined(isolate) && |
| weak_cell->key_list_next()->IsUndefined(isolate)) { |
| // weak_cell is the only one associated with its key; remove the key |
| // from the hash table. |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| Handle<Object> key = handle(weak_cell->key(), isolate); |
| bool was_present; |
| key_map = ObjectHashTable::Remove(isolate, key_map, key, &was_present); |
| DCHECK(was_present); |
| finalization_group->set_key_map(*key_map); |
| } else if (weak_cell->key_list_prev()->IsUndefined()) { |
| // weak_cell is the list head for its key; we need to change the value of |
| // the key in the hash table. |
| Handle<ObjectHashTable> key_map = |
| handle(ObjectHashTable::cast(finalization_group->key_map()), isolate); |
| Handle<Object> key = handle(weak_cell->key(), isolate); |
| Handle<WeakCell> next = |
| handle(WeakCell::cast(weak_cell->key_list_next()), isolate); |
| DCHECK_EQ(next->key_list_prev(), *weak_cell); |
| next->set_key_list_prev(ReadOnlyRoots(isolate).undefined_value()); |
| weak_cell->set_key_list_next(ReadOnlyRoots(isolate).undefined_value()); |
| key_map = ObjectHashTable::Put(key_map, key, next); |
| finalization_group->set_key_map(*key_map); |
| } else { |
| // weak_cell is somewhere in the middle of its key list. |
| WeakCell prev = WeakCell::cast(weak_cell->key_list_prev()); |
| prev->set_key_list_next(weak_cell->key_list_next()); |
| if (!weak_cell->key_list_next()->IsUndefined()) { |
| WeakCell next = WeakCell::cast(weak_cell->key_list_next()); |
| next->set_key_list_prev(weak_cell->key_list_prev()); |
| } |
| } |
| } |
| |
| return weak_cell->holdings(); |
| } |
| |
| void WeakCell::Nullify( |
| Isolate* isolate, |
| std::function<void(HeapObject object, ObjectSlot slot, Object target)> |
| gc_notify_updated_slot) { |
| // Remove from the WeakCell from the "active_cells" list of its |
| // JSFinalizationGroup and insert it into the "cleared_cells" list. This is |
| // only called for WeakCells which haven't been unregistered yet, so they will |
| // be in the active_cells list. (The caller must guard against calling this |
| // for unregistered WeakCells by checking that the target is not undefined.) |
| DCHECK(target()->IsJSReceiver()); |
| set_target(ReadOnlyRoots(isolate).undefined_value()); |
| |
| JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group()); |
| if (prev()->IsWeakCell()) { |
| DCHECK_NE(fg->active_cells(), *this); |
| WeakCell prev_cell = WeakCell::cast(prev()); |
| prev_cell->set_next(next()); |
| gc_notify_updated_slot(prev_cell, prev_cell.RawField(WeakCell::kNextOffset), |
| next()); |
| } else { |
| DCHECK_EQ(fg->active_cells(), *this); |
| fg->set_active_cells(next()); |
| gc_notify_updated_slot( |
| fg, fg.RawField(JSFinalizationGroup::kActiveCellsOffset), next()); |
| } |
| if (next()->IsWeakCell()) { |
| WeakCell next_cell = WeakCell::cast(next()); |
| next_cell->set_prev(prev()); |
| gc_notify_updated_slot(next_cell, next_cell.RawField(WeakCell::kPrevOffset), |
| prev()); |
| } |
| |
| set_prev(ReadOnlyRoots(isolate).undefined_value()); |
| Object cleared_head = fg->cleared_cells(); |
| if (cleared_head->IsWeakCell()) { |
| WeakCell cleared_head_cell = WeakCell::cast(cleared_head); |
| cleared_head_cell->set_prev(*this); |
| gc_notify_updated_slot(cleared_head_cell, |
| cleared_head_cell.RawField(WeakCell::kPrevOffset), |
| *this); |
| } |
| set_next(fg->cleared_cells()); |
| gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next()); |
| fg->set_cleared_cells(*this); |
| gc_notify_updated_slot( |
| fg, fg.RawField(JSFinalizationGroup::kClearedCellsOffset), *this); |
| } |
| |
| void WeakCell::RemoveFromFinalizationGroupCells(Isolate* isolate) { |
| // Remove the WeakCell from the list it's in (either "active_cells" or |
| // "cleared_cells" of its JSFinalizationGroup). |
| |
| // It's important to set_target to undefined here. This guards that we won't |
| // call Nullify (which assumes that the WeakCell is in active_cells). |
| DCHECK(target()->IsUndefined() || target()->IsJSReceiver()); |
| set_target(ReadOnlyRoots(isolate).undefined_value()); |
| |
| JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group()); |
| if (fg->active_cells() == *this) { |
| DCHECK(prev()->IsUndefined(isolate)); |
| fg->set_active_cells(next()); |
| } else if (fg->cleared_cells() == *this) { |
| DCHECK(!prev()->IsWeakCell()); |
| fg->set_cleared_cells(next()); |
| } else { |
| DCHECK(prev()->IsWeakCell()); |
| WeakCell prev_cell = WeakCell::cast(prev()); |
| prev_cell->set_next(next()); |
| } |
| if (next()->IsWeakCell()) { |
| WeakCell next_cell = WeakCell::cast(next()); |
| next_cell->set_prev(prev()); |
| } |
| set_prev(ReadOnlyRoots(isolate).undefined_value()); |
| set_next(ReadOnlyRoots(isolate).undefined_value()); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #include "src/objects/object-macros-undef.h" |
| |
| #endif // V8_OBJECTS_JS_WEAK_REFS_INL_H_ |