js-weak-refs-inl.h 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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"

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 15 16 17 18 19

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

20
OBJECT_CONSTRUCTORS_IMPL(WeakCell, HeapObject)
21
OBJECT_CONSTRUCTORS_IMPL(JSWeakRef, JSObject)
22 23 24 25 26 27 28 29 30 31 32 33 34 35
OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroup, JSObject)
OBJECT_CONSTRUCTORS_IMPL(JSFinalizationGroupCleanupIterator, JSObject)

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)
36
ACCESSORS(WeakCell, target, HeapObject, kTargetOffset)
37 38 39 40 41 42 43
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)
44

45
CAST_ACCESSOR(JSWeakRef)
46
ACCESSORS(JSWeakRef, target, HeapObject, kTargetOffset)
47

48 49 50
ACCESSORS(JSFinalizationGroupCleanupIterator, finalization_group,
          JSFinalizationGroup, kFinalizationGroupOffset)
CAST_ACCESSOR(JSFinalizationGroupCleanupIterator)
51

52 53 54 55 56 57 58 59 60 61 62 63
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());
64

65 66
  // Add to active_cells.
  weak_cell->set_next(finalization_group->active_cells());
67 68
  if (finalization_group->active_cells().IsWeakCell()) {
    WeakCell::cast(finalization_group->active_cells()).set_prev(*weak_cell);
69 70 71 72 73
  }
  finalization_group->set_active_cells(*weak_cell);

  if (!key->IsUndefined(isolate)) {
    Handle<ObjectHashTable> key_map;
74
    if (finalization_group->key_map().IsUndefined(isolate)) {
75 76 77 78 79 80 81
      key_map = ObjectHashTable::New(isolate, 1);
    } else {
      key_map =
          handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
    }

    Object value = key_map->Lookup(key);
82
    if (value.IsWeakCell()) {
83
      WeakCell existing_weak_cell = WeakCell::cast(value);
84
      existing_weak_cell.set_key_list_prev(*weak_cell);
85 86
      weak_cell->set_key_list_next(existing_weak_cell);
    } else {
87
      DCHECK(value.IsTheHole(isolate));
88 89 90 91 92 93
    }
    key_map = ObjectHashTable::Put(key_map, key, weak_cell);
    finalization_group->set_key_map(*key_map);
  }
}

94 95 96
bool JSFinalizationGroup::Unregister(
    Handle<JSFinalizationGroup> finalization_group,
    Handle<JSReceiver> unregister_token, Isolate* isolate) {
97 98 99
  // 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.
100
  if (!finalization_group->key_map().IsUndefined(isolate)) {
101 102
    Handle<ObjectHashTable> key_map =
        handle(ObjectHashTable::cast(finalization_group->key_map()), isolate);
103
    Object value = key_map->Lookup(unregister_token);
104
    Object undefined = ReadOnlyRoots(isolate).undefined_value();
105
    while (value.IsWeakCell()) {
106
      WeakCell weak_cell = WeakCell::cast(value);
107 108 109 110
      weak_cell.RemoveFromFinalizationGroupCells(isolate);
      value = weak_cell.key_list_next();
      weak_cell.set_key_list_prev(undefined);
      weak_cell.set_key_list_next(undefined);
111 112
    }
    bool was_present;
113 114
    key_map = ObjectHashTable::Remove(isolate, key_map, unregister_token,
                                      &was_present);
115
    finalization_group->set_key_map(*key_map);
116
    return was_present;
117
  }
118 119

  return false;
120 121
}

122
bool JSFinalizationGroup::NeedsCleanup() const {
123
  return cleared_cells().IsWeakCell();
124 125
}

126
bool JSFinalizationGroup::scheduled_for_cleanup() const {
127 128 129
  return ScheduledForCleanupField::decode(flags());
}

130 131
void JSFinalizationGroup::set_scheduled_for_cleanup(
    bool scheduled_for_cleanup) {
132 133 134
  set_flags(ScheduledForCleanupField::update(flags(), scheduled_for_cleanup));
}

135 136 137 138
Object JSFinalizationGroup::PopClearedCellHoldings(
    Handle<JSFinalizationGroup> finalization_group, Isolate* isolate) {
  Handle<WeakCell> weak_cell =
      handle(WeakCell::cast(finalization_group->cleared_cells()), isolate);
139
  DCHECK(weak_cell->prev().IsUndefined(isolate));
140
  finalization_group->set_cleared_cells(weak_cell->next());
141 142
  weak_cell->set_next(ReadOnlyRoots(isolate).undefined_value());

143
  if (finalization_group->cleared_cells().IsWeakCell()) {
144 145
    WeakCell cleared_cells_head =
        WeakCell::cast(finalization_group->cleared_cells());
146 147
    DCHECK_EQ(cleared_cells_head.prev(), *weak_cell);
    cleared_cells_head.set_prev(ReadOnlyRoots(isolate).undefined_value());
148
  } else {
149
    DCHECK(finalization_group->cleared_cells().IsUndefined(isolate));
150
  }
151 152

  // Also remove the WeakCell from the key_map (if it's there).
153 154 155
  if (!weak_cell->key().IsUndefined(isolate)) {
    if (weak_cell->key_list_prev().IsUndefined(isolate) &&
        weak_cell->key_list_next().IsUndefined(isolate)) {
156 157 158 159 160 161 162 163 164
      // 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);
165
    } else if (weak_cell->key_list_prev().IsUndefined()) {
166 167 168 169 170 171 172
      // 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);
173 174 175
      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());
176 177 178 179 180
      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());
181 182
      prev.set_key_list_next(weak_cell->key_list_next());
      if (!weak_cell->key_list_next().IsUndefined()) {
183
        WeakCell next = WeakCell::cast(weak_cell->key_list_next());
184
        next.set_key_list_prev(weak_cell->key_list_prev());
185 186 187 188 189
      }
    }
  }

  return weak_cell->holdings();
190 191
}

192
void WeakCell::Nullify(
193
    Isolate* isolate,
194
    std::function<void(HeapObject object, ObjectSlot slot, Object target)>
195
        gc_notify_updated_slot) {
196 197 198 199 200
  // 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.)
201
  DCHECK(target().IsJSReceiver());
202
  set_target(ReadOnlyRoots(isolate).undefined_value());
203

204
  JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group());
205 206
  if (prev().IsWeakCell()) {
    DCHECK_NE(fg.active_cells(), *this);
207
    WeakCell prev_cell = WeakCell::cast(prev());
208
    prev_cell.set_next(next());
209 210
    gc_notify_updated_slot(prev_cell, prev_cell.RawField(WeakCell::kNextOffset),
                           next());
211
  } else {
212 213
    DCHECK_EQ(fg.active_cells(), *this);
    fg.set_active_cells(next());
214
    gc_notify_updated_slot(
215
        fg, fg.RawField(JSFinalizationGroup::kActiveCellsOffset), next());
216
  }
217
  if (next().IsWeakCell()) {
218
    WeakCell next_cell = WeakCell::cast(next());
219
    next_cell.set_prev(prev());
220 221
    gc_notify_updated_slot(next_cell, next_cell.RawField(WeakCell::kPrevOffset),
                           prev());
222 223 224
  }

  set_prev(ReadOnlyRoots(isolate).undefined_value());
225 226
  Object cleared_head = fg.cleared_cells();
  if (cleared_head.IsWeakCell()) {
227
    WeakCell cleared_head_cell = WeakCell::cast(cleared_head);
228
    cleared_head_cell.set_prev(*this);
229
    gc_notify_updated_slot(cleared_head_cell,
230
                           cleared_head_cell.RawField(WeakCell::kPrevOffset),
231
                           *this);
232
  }
233
  set_next(fg.cleared_cells());
234
  gc_notify_updated_slot(*this, RawField(WeakCell::kNextOffset), next());
235
  fg.set_cleared_cells(*this);
236
  gc_notify_updated_slot(
237
      fg, fg.RawField(JSFinalizationGroup::kClearedCellsOffset), *this);
238 239
}

240 241 242
void WeakCell::RemoveFromFinalizationGroupCells(Isolate* isolate) {
  // Remove the WeakCell from the list it's in (either "active_cells" or
  // "cleared_cells" of its JSFinalizationGroup).
243

244 245
  // 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).
246
  DCHECK(target().IsUndefined() || target().IsJSReceiver());
247 248
  set_target(ReadOnlyRoots(isolate).undefined_value());

249
  JSFinalizationGroup fg = JSFinalizationGroup::cast(finalization_group());
250 251 252 253 254 255
  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());
256
  } else {
257
    DCHECK(prev().IsWeakCell());
258
    WeakCell prev_cell = WeakCell::cast(prev());
259
    prev_cell.set_next(next());
260
  }
261
  if (next().IsWeakCell()) {
262
    WeakCell next_cell = WeakCell::cast(next());
263
    next_cell.set_prev(prev());
264 265 266
  }
  set_prev(ReadOnlyRoots(isolate).undefined_value());
  set_next(ReadOnlyRoots(isolate).undefined_value());
267 268
}

269 270 271 272 273 274
}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_JS_WEAK_REFS_INL_H_