11cb0ef41Sopenharmony_ci// Copyright 2018 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#ifndef V8_OBJECTS_JS_WEAK_REFS_INL_H_
61cb0ef41Sopenharmony_ci#define V8_OBJECTS_JS_WEAK_REFS_INL_H_
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci#include "src/api/api-inl.h"
91cb0ef41Sopenharmony_ci#include "src/heap/heap-write-barrier-inl.h"
101cb0ef41Sopenharmony_ci#include "src/objects/js-weak-refs.h"
111cb0ef41Sopenharmony_ci#include "src/objects/smi-inl.h"
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ci// Has to be the last include (doesn't have include guards):
141cb0ef41Sopenharmony_ci#include "src/objects/object-macros.h"
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cinamespace v8 {
171cb0ef41Sopenharmony_cinamespace internal {
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci#include "torque-generated/src/objects/js-weak-refs-tq-inl.inc"
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ciTQ_OBJECT_CONSTRUCTORS_IMPL(WeakCell)
221cb0ef41Sopenharmony_ciTQ_OBJECT_CONSTRUCTORS_IMPL(JSWeakRef)
231cb0ef41Sopenharmony_ciTQ_OBJECT_CONSTRUCTORS_IMPL(JSFinalizationRegistry)
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciBIT_FIELD_ACCESSORS(JSFinalizationRegistry, flags, scheduled_for_cleanup,
261cb0ef41Sopenharmony_ci                    JSFinalizationRegistry::ScheduledForCleanupBit)
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_civoid JSFinalizationRegistry::RegisterWeakCellWithUnregisterToken(
291cb0ef41Sopenharmony_ci    Handle<JSFinalizationRegistry> finalization_registry,
301cb0ef41Sopenharmony_ci    Handle<WeakCell> weak_cell, Isolate* isolate) {
311cb0ef41Sopenharmony_ci  Handle<SimpleNumberDictionary> key_map;
321cb0ef41Sopenharmony_ci  if (finalization_registry->key_map().IsUndefined(isolate)) {
331cb0ef41Sopenharmony_ci    key_map = SimpleNumberDictionary::New(isolate, 1);
341cb0ef41Sopenharmony_ci  } else {
351cb0ef41Sopenharmony_ci    key_map =
361cb0ef41Sopenharmony_ci        handle(SimpleNumberDictionary::cast(finalization_registry->key_map()),
371cb0ef41Sopenharmony_ci               isolate);
381cb0ef41Sopenharmony_ci  }
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  // Unregister tokens are held weakly as objects are often their own
411cb0ef41Sopenharmony_ci  // unregister token. To avoid using an ephemeron map, the map for token
421cb0ef41Sopenharmony_ci  // lookup is keyed on the token's identity hash instead of the token itself.
431cb0ef41Sopenharmony_ci  uint32_t key = weak_cell->unregister_token().GetOrCreateHash(isolate).value();
441cb0ef41Sopenharmony_ci  InternalIndex entry = key_map->FindEntry(isolate, key);
451cb0ef41Sopenharmony_ci  if (entry.is_found()) {
461cb0ef41Sopenharmony_ci    Object value = key_map->ValueAt(entry);
471cb0ef41Sopenharmony_ci    WeakCell existing_weak_cell = WeakCell::cast(value);
481cb0ef41Sopenharmony_ci    existing_weak_cell.set_key_list_prev(*weak_cell);
491cb0ef41Sopenharmony_ci    weak_cell->set_key_list_next(existing_weak_cell);
501cb0ef41Sopenharmony_ci  }
511cb0ef41Sopenharmony_ci  key_map = SimpleNumberDictionary::Set(isolate, key_map, key, weak_cell);
521cb0ef41Sopenharmony_ci  finalization_registry->set_key_map(*key_map);
531cb0ef41Sopenharmony_ci}
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_cibool JSFinalizationRegistry::Unregister(
561cb0ef41Sopenharmony_ci    Handle<JSFinalizationRegistry> finalization_registry,
571cb0ef41Sopenharmony_ci    Handle<HeapObject> unregister_token, Isolate* isolate) {
581cb0ef41Sopenharmony_ci  // Iterate through the doubly linked list of WeakCells associated with the
591cb0ef41Sopenharmony_ci  // key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
601cb0ef41Sopenharmony_ci  // its FinalizationRegistry; remove it from there.
611cb0ef41Sopenharmony_ci  return finalization_registry->RemoveUnregisterToken(
621cb0ef41Sopenharmony_ci      *unregister_token, isolate, kRemoveMatchedCellsFromRegistry,
631cb0ef41Sopenharmony_ci      [](HeapObject, ObjectSlot, Object) {});
641cb0ef41Sopenharmony_ci}
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_citemplate <typename GCNotifyUpdatedSlotCallback>
671cb0ef41Sopenharmony_cibool JSFinalizationRegistry::RemoveUnregisterToken(
681cb0ef41Sopenharmony_ci    HeapObject unregister_token, Isolate* isolate,
691cb0ef41Sopenharmony_ci    RemoveUnregisterTokenMode removal_mode,
701cb0ef41Sopenharmony_ci    GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
711cb0ef41Sopenharmony_ci  // This method is called from both FinalizationRegistry#unregister and for
721cb0ef41Sopenharmony_ci  // removing weakly-held dead unregister tokens. The latter is during GC so
731cb0ef41Sopenharmony_ci  // this function cannot GC.
741cb0ef41Sopenharmony_ci  DisallowGarbageCollection no_gc;
751cb0ef41Sopenharmony_ci  if (key_map().IsUndefined(isolate)) {
761cb0ef41Sopenharmony_ci    return false;
771cb0ef41Sopenharmony_ci  }
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  SimpleNumberDictionary key_map =
801cb0ef41Sopenharmony_ci      SimpleNumberDictionary::cast(this->key_map());
811cb0ef41Sopenharmony_ci  // If the token doesn't have a hash, it was not used as a key inside any hash
821cb0ef41Sopenharmony_ci  // tables.
831cb0ef41Sopenharmony_ci  Object hash = unregister_token.GetHash();
841cb0ef41Sopenharmony_ci  if (hash.IsUndefined(isolate)) {
851cb0ef41Sopenharmony_ci    return false;
861cb0ef41Sopenharmony_ci  }
871cb0ef41Sopenharmony_ci  uint32_t key = Smi::ToInt(hash);
881cb0ef41Sopenharmony_ci  InternalIndex entry = key_map.FindEntry(isolate, key);
891cb0ef41Sopenharmony_ci  if (entry.is_not_found()) {
901cb0ef41Sopenharmony_ci    return false;
911cb0ef41Sopenharmony_ci  }
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  Object value = key_map.ValueAt(entry);
941cb0ef41Sopenharmony_ci  bool was_present = false;
951cb0ef41Sopenharmony_ci  HeapObject undefined = ReadOnlyRoots(isolate).undefined_value();
961cb0ef41Sopenharmony_ci  HeapObject new_key_list_head = undefined;
971cb0ef41Sopenharmony_ci  HeapObject new_key_list_prev = undefined;
981cb0ef41Sopenharmony_ci  // Compute a new key list that doesn't have unregister_token. Because
991cb0ef41Sopenharmony_ci  // unregister tokens are held weakly, key_map is keyed using the tokens'
1001cb0ef41Sopenharmony_ci  // identity hashes, and identity hashes may collide.
1011cb0ef41Sopenharmony_ci  while (!value.IsUndefined(isolate)) {
1021cb0ef41Sopenharmony_ci    WeakCell weak_cell = WeakCell::cast(value);
1031cb0ef41Sopenharmony_ci    DCHECK(!ObjectInYoungGeneration(weak_cell));
1041cb0ef41Sopenharmony_ci    value = weak_cell.key_list_next();
1051cb0ef41Sopenharmony_ci    if (weak_cell.unregister_token() == unregister_token) {
1061cb0ef41Sopenharmony_ci      // weak_cell has the same unregister token; remove it from the key list.
1071cb0ef41Sopenharmony_ci      switch (removal_mode) {
1081cb0ef41Sopenharmony_ci        case kRemoveMatchedCellsFromRegistry:
1091cb0ef41Sopenharmony_ci          weak_cell.RemoveFromFinalizationRegistryCells(isolate);
1101cb0ef41Sopenharmony_ci          break;
1111cb0ef41Sopenharmony_ci        case kKeepMatchedCellsInRegistry:
1121cb0ef41Sopenharmony_ci          // Do nothing.
1131cb0ef41Sopenharmony_ci          break;
1141cb0ef41Sopenharmony_ci      }
1151cb0ef41Sopenharmony_ci      // Clear unregister token-related fields.
1161cb0ef41Sopenharmony_ci      weak_cell.set_unregister_token(undefined);
1171cb0ef41Sopenharmony_ci      weak_cell.set_key_list_prev(undefined);
1181cb0ef41Sopenharmony_ci      weak_cell.set_key_list_next(undefined);
1191cb0ef41Sopenharmony_ci      was_present = true;
1201cb0ef41Sopenharmony_ci    } else {
1211cb0ef41Sopenharmony_ci      // weak_cell has a different unregister token with the same key (hash
1221cb0ef41Sopenharmony_ci      // collision); fix up the list.
1231cb0ef41Sopenharmony_ci      weak_cell.set_key_list_prev(new_key_list_prev);
1241cb0ef41Sopenharmony_ci      gc_notify_updated_slot(weak_cell,
1251cb0ef41Sopenharmony_ci                             weak_cell.RawField(WeakCell::kKeyListPrevOffset),
1261cb0ef41Sopenharmony_ci                             new_key_list_prev);
1271cb0ef41Sopenharmony_ci      weak_cell.set_key_list_next(undefined);
1281cb0ef41Sopenharmony_ci      if (new_key_list_prev.IsUndefined(isolate)) {
1291cb0ef41Sopenharmony_ci        new_key_list_head = weak_cell;
1301cb0ef41Sopenharmony_ci      } else {
1311cb0ef41Sopenharmony_ci        DCHECK(new_key_list_head.IsWeakCell());
1321cb0ef41Sopenharmony_ci        WeakCell prev_cell = WeakCell::cast(new_key_list_prev);
1331cb0ef41Sopenharmony_ci        prev_cell.set_key_list_next(weak_cell);
1341cb0ef41Sopenharmony_ci        gc_notify_updated_slot(prev_cell,
1351cb0ef41Sopenharmony_ci                               prev_cell.RawField(WeakCell::kKeyListNextOffset),
1361cb0ef41Sopenharmony_ci                               weak_cell);
1371cb0ef41Sopenharmony_ci      }
1381cb0ef41Sopenharmony_ci      new_key_list_prev = weak_cell;
1391cb0ef41Sopenharmony_ci    }
1401cb0ef41Sopenharmony_ci  }
1411cb0ef41Sopenharmony_ci  if (new_key_list_head.IsUndefined(isolate)) {
1421cb0ef41Sopenharmony_ci    DCHECK(was_present);
1431cb0ef41Sopenharmony_ci    key_map.ClearEntry(entry);
1441cb0ef41Sopenharmony_ci    key_map.ElementRemoved();
1451cb0ef41Sopenharmony_ci  } else {
1461cb0ef41Sopenharmony_ci    key_map.ValueAtPut(entry, new_key_list_head);
1471cb0ef41Sopenharmony_ci    gc_notify_updated_slot(key_map, key_map.RawFieldOfValueAt(entry),
1481cb0ef41Sopenharmony_ci                           new_key_list_head);
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci  return was_present;
1511cb0ef41Sopenharmony_ci}
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_cibool JSFinalizationRegistry::NeedsCleanup() const {
1541cb0ef41Sopenharmony_ci  return cleared_cells().IsWeakCell();
1551cb0ef41Sopenharmony_ci}
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ciHeapObject WeakCell::relaxed_target() const {
1581cb0ef41Sopenharmony_ci  return TaggedField<HeapObject>::Relaxed_Load(*this, kTargetOffset);
1591cb0ef41Sopenharmony_ci}
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ciHeapObject WeakCell::relaxed_unregister_token() const {
1621cb0ef41Sopenharmony_ci  return TaggedField<HeapObject>::Relaxed_Load(*this, kUnregisterTokenOffset);
1631cb0ef41Sopenharmony_ci}
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_citemplate <typename GCNotifyUpdatedSlotCallback>
1661cb0ef41Sopenharmony_civoid WeakCell::Nullify(Isolate* isolate,
1671cb0ef41Sopenharmony_ci                       GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
1681cb0ef41Sopenharmony_ci  // Remove from the WeakCell from the "active_cells" list of its
1691cb0ef41Sopenharmony_ci  // JSFinalizationRegistry and insert it into the "cleared_cells" list. This is
1701cb0ef41Sopenharmony_ci  // only called for WeakCells which haven't been unregistered yet, so they will
1711cb0ef41Sopenharmony_ci  // be in the active_cells list. (The caller must guard against calling this
1721cb0ef41Sopenharmony_ci  // for unregistered WeakCells by checking that the target is not undefined.)
1731cb0ef41Sopenharmony_ci  DCHECK(target().CanBeHeldWeakly());
1741cb0ef41Sopenharmony_ci  set_target(ReadOnlyRoots(isolate).undefined_value());
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  JSFinalizationRegistry fr =
1771cb0ef41Sopenharmony_ci      JSFinalizationRegistry::cast(finalization_registry());
1781cb0ef41Sopenharmony_ci  if (prev().IsWeakCell()) {
1791cb0ef41Sopenharmony_ci    DCHECK_NE(fr.active_cells(), *this);
1801cb0ef41Sopenharmony_ci    WeakCell prev_cell = WeakCell::cast(prev());
1811cb0ef41Sopenharmony_ci    prev_cell.set_next(next());
1821cb0ef41Sopenharmony_ci    gc_notify_updated_slot(prev_cell, prev_cell.RawField(WeakCell::kNextOffset),
1831cb0ef41Sopenharmony_ci                           next());
1841cb0ef41Sopenharmony_ci  } else {
1851cb0ef41Sopenharmony_ci    DCHECK_EQ(fr.active_cells(), *this);
1861cb0ef41Sopenharmony_ci    fr.set_active_cells(next());
1871cb0ef41Sopenharmony_ci    gc_notify_updated_slot(
1881cb0ef41Sopenharmony_ci        fr, fr.RawField(JSFinalizationRegistry::kActiveCellsOffset), next());
1891cb0ef41Sopenharmony_ci  }
1901cb0ef41Sopenharmony_ci  if (next().IsWeakCell()) {
1911cb0ef41Sopenharmony_ci    WeakCell next_cell = WeakCell::cast(next());
1921cb0ef41Sopenharmony_ci    next_cell.set_prev(prev());
1931cb0ef41Sopenharmony_ci    gc_notify_updated_slot(next_cell, next_cell.RawField(WeakCell::kPrevOffset),
1941cb0ef41Sopenharmony_ci                           prev());
1951cb0ef41Sopenharmony_ci  }
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci  set_prev(ReadOnlyRoots(isolate).undefined_value());
1981cb0ef41Sopenharmony_ci  Object cleared_head = fr.cleared_cells();
1991cb0ef41Sopenharmony_ci  if (cleared_head.IsWeakCell()) {
2001cb0ef41Sopenharmony_ci    WeakCell cleared_head_cell = WeakCell::cast(cleared_head);
2011cb0ef41Sopenharmony_ci    cleared_head_cell.set_prev(*this);
2021cb0ef41Sopenharmony_ci    gc_notify_updated_slot(cleared_head_cell,
2031cb0ef41Sopenharmony_ci                           cleared_head_cell.RawField(WeakCell::kPrevOffset),
2041cb0ef41Sopenharmony_ci                           *this);
2051cb0ef41Sopenharmony_ci  }
2061cb0ef41Sopenharmony_ci  set_next(fr.cleared_cells());
2071cb0ef41Sopenharmony_ci  gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next());
2081cb0ef41Sopenharmony_ci  fr.set_cleared_cells(*this);
2091cb0ef41Sopenharmony_ci  gc_notify_updated_slot(
2101cb0ef41Sopenharmony_ci      fr, fr.RawField(JSFinalizationRegistry::kClearedCellsOffset), *this);
2111cb0ef41Sopenharmony_ci}
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_civoid WeakCell::RemoveFromFinalizationRegistryCells(Isolate* isolate) {
2141cb0ef41Sopenharmony_ci  // Remove the WeakCell from the list it's in (either "active_cells" or
2151cb0ef41Sopenharmony_ci  // "cleared_cells" of its JSFinalizationRegistry).
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci  // It's important to set_target to undefined here. This guards that we won't
2181cb0ef41Sopenharmony_ci  // call Nullify (which assumes that the WeakCell is in active_cells).
2191cb0ef41Sopenharmony_ci  DCHECK(target().IsUndefined() || target().CanBeHeldWeakly());
2201cb0ef41Sopenharmony_ci  set_target(ReadOnlyRoots(isolate).undefined_value());
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  JSFinalizationRegistry fr =
2231cb0ef41Sopenharmony_ci      JSFinalizationRegistry::cast(finalization_registry());
2241cb0ef41Sopenharmony_ci  if (fr.active_cells() == *this) {
2251cb0ef41Sopenharmony_ci    DCHECK(prev().IsUndefined(isolate));
2261cb0ef41Sopenharmony_ci    fr.set_active_cells(next());
2271cb0ef41Sopenharmony_ci  } else if (fr.cleared_cells() == *this) {
2281cb0ef41Sopenharmony_ci    DCHECK(!prev().IsWeakCell());
2291cb0ef41Sopenharmony_ci    fr.set_cleared_cells(next());
2301cb0ef41Sopenharmony_ci  } else {
2311cb0ef41Sopenharmony_ci    DCHECK(prev().IsWeakCell());
2321cb0ef41Sopenharmony_ci    WeakCell prev_cell = WeakCell::cast(prev());
2331cb0ef41Sopenharmony_ci    prev_cell.set_next(next());
2341cb0ef41Sopenharmony_ci  }
2351cb0ef41Sopenharmony_ci  if (next().IsWeakCell()) {
2361cb0ef41Sopenharmony_ci    WeakCell next_cell = WeakCell::cast(next());
2371cb0ef41Sopenharmony_ci    next_cell.set_prev(prev());
2381cb0ef41Sopenharmony_ci  }
2391cb0ef41Sopenharmony_ci  set_prev(ReadOnlyRoots(isolate).undefined_value());
2401cb0ef41Sopenharmony_ci  set_next(ReadOnlyRoots(isolate).undefined_value());
2411cb0ef41Sopenharmony_ci}
2421cb0ef41Sopenharmony_ci
2431cb0ef41Sopenharmony_ci}  // namespace internal
2441cb0ef41Sopenharmony_ci}  // namespace v8
2451cb0ef41Sopenharmony_ci
2461cb0ef41Sopenharmony_ci#include "src/objects/object-macros-undef.h"
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci#endif  // V8_OBJECTS_JS_WEAK_REFS_INL_H_
249