• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 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
5namespace runtime {
6extern runtime
7ShrinkFinalizationRegistryUnregisterTokenMap(
8    Context, JSFinalizationRegistry): void;
9extern runtime JSFinalizationRegistryRegisterWeakCellWithUnregisterToken(
10    implicit context: Context)(JSFinalizationRegistry, WeakCell): void;
11}
12
13namespace weakref {
14extern transitioning macro
15RemoveFinalizationRegistryCellFromUnregisterTokenMap(
16    JSFinalizationRegistry, WeakCell): void;
17
18macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
19  const weakCellTail = weakCell.next;
20  weakCell.next = Undefined;
21  typeswitch (weakCellTail) {
22    case (Undefined): {
23    }
24    case (tailIsNowAHead: WeakCell): {
25      assert(tailIsNowAHead.prev == weakCell);
26      tailIsNowAHead.prev = Undefined;
27    }
28  }
29  return weakCellTail;
30}
31
32transitioning macro
33PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell|
34    Undefined {
35  typeswitch (finalizationRegistry.cleared_cells) {
36    case (Undefined): {
37      return Undefined;
38    }
39    case (weakCell: WeakCell): {
40      assert(weakCell.prev == Undefined);
41      finalizationRegistry.cleared_cells = SplitOffTail(weakCell);
42
43      // If the WeakCell has an unregister token, remove the cell from the
44      // unregister token linked lists and and the unregister token from
45      // key_map. This doesn't shrink key_map, which is done manually after
46      // the cleanup loop to avoid a runtime call.
47      if (weakCell.unregister_token != Undefined) {
48        RemoveFinalizationRegistryCellFromUnregisterTokenMap(
49            finalizationRegistry, weakCell);
50      }
51
52      return weakCell;
53    }
54  }
55}
56
57transitioning macro PushCell(
58    finalizationRegistry: JSFinalizationRegistry, cell: WeakCell) {
59  cell.next = finalizationRegistry.active_cells;
60  typeswitch (finalizationRegistry.active_cells) {
61    case (Undefined): {
62    }
63    case (oldHead: WeakCell): {
64      oldHead.prev = cell;
65    }
66  }
67  finalizationRegistry.active_cells = cell;
68}
69
70transitioning macro
71FinalizationRegistryCleanupLoop(implicit context: Context)(
72    finalizationRegistry: JSFinalizationRegistry, callback: Callable) {
73  while (true) {
74    const weakCellHead = PopClearedCell(finalizationRegistry);
75    typeswitch (weakCellHead) {
76      case (Undefined): {
77        break;
78      }
79      case (weakCell: WeakCell): {
80        try {
81          Call(context, callback, Undefined, weakCell.holdings);
82        } catch (e) {
83          runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
84              context, finalizationRegistry);
85          ReThrow(context, e);
86        }
87      }
88    }
89  }
90
91  runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
92      context, finalizationRegistry);
93}
94
95transitioning javascript builtin
96FinalizationRegistryConstructor(
97    js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
98    target: JSFunction)(cleanupCallback: JSAny): JSFinalizationRegistry {
99  // 1. If NewTarget is undefined, throw a TypeError exception.
100  if (newTarget == Undefined) {
101    ThrowTypeError(
102        MessageTemplate::kConstructorNotFunction, 'FinalizationRegistry');
103  }
104  // 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.
105  const cleanupCallback = Cast<Callable>(cleanupCallback) otherwise
106  ThrowTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable);
107  // 3. Let finalizationRegistry be ? OrdinaryCreateFromConstructor(NewTarget,
108  // "%FinalizationRegistryPrototype%", « [[Realm]], [[CleanupCallback]],
109  // [[Cells]] »).
110  const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
111  const finalizationRegistry = UnsafeCast<JSFinalizationRegistry>(
112      AllocateFastOrSlowJSObjectFromMap(map));
113  // 4. Let fn be the active function object.
114  // 5. Set finalizationRegistry.[[Realm]] to fn.[[Realm]].
115  finalizationRegistry.native_context = context;
116  // 6. Set finalizationRegistry.[[CleanupCallback]] to cleanupCallback.
117  finalizationRegistry.cleanup = cleanupCallback;
118  finalizationRegistry.flags =
119      SmiTag(FinalizationRegistryFlags{scheduled_for_cleanup: false});
120  // 7. Set finalizationRegistry.[[Cells]] to be an empty List.
121  assert(finalizationRegistry.active_cells == Undefined);
122  assert(finalizationRegistry.cleared_cells == Undefined);
123  assert(finalizationRegistry.key_map == Undefined);
124  // 8. Return finalizationRegistry.
125  return finalizationRegistry;
126}
127
128transitioning javascript builtin
129FinalizationRegistryRegister(
130    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
131  // 1. Let finalizationRegistry be the this value.
132  // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
133  const finalizationRegistry = Cast<JSFinalizationRegistry>(receiver) otherwise
134  ThrowTypeError(
135      MessageTemplate::kIncompatibleMethodReceiver,
136      'FinalizationRegistry.prototype.register', receiver);
137  // 3. If Type(target) is not Object, throw a TypeError exception.
138  const target = Cast<JSReceiver>(arguments[0]) otherwise ThrowTypeError(
139      MessageTemplate::kWeakRefsRegisterTargetMustBeObject);
140  const heldValue = arguments[1];
141  // 4. If SameValue(target, heldValue), throw a TypeError exception.
142  if (target == heldValue) {
143    ThrowTypeError(
144        MessageTemplate::kWeakRefsRegisterTargetAndHoldingsMustNotBeSame);
145  }
146  const unregisterToken = arguments[2];
147  // 5. If Type(unregisterToken) is not Object,
148  //   a. If unregisterToken is not undefined, throw a TypeError exception.
149  //   b. Set unregisterToken to empty.
150  let hasUnregisterToken: bool = false;
151  typeswitch (unregisterToken) {
152    case (Undefined): {
153    }
154    case (JSReceiver): {
155      hasUnregisterToken = true;
156    }
157    case (JSAny): deferred {
158      ThrowTypeError(
159          MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
160          unregisterToken);
161    }
162  }
163  // 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]:
164  //    heldValue, [[UnregisterToken]]: unregisterToken }.
165  // Allocate the WeakCell object in the old space, because 1) WeakCell weakness
166  // handling is only implemented in the old space 2) they're supposedly
167  // long-living. TODO(marja, gsathya): Support WeakCells in Scavenger.
168  const cell = new (Pretenured) WeakCell{
169    map: GetWeakCellMap(),
170    finalization_registry: finalizationRegistry,
171    target: target,
172    unregister_token: unregisterToken,
173    holdings: heldValue,
174    prev: Undefined,
175    next: Undefined,
176    key_list_prev: Undefined,
177    key_list_next: Undefined
178  };
179  // 7. Append cell to finalizationRegistry.[[Cells]].
180  PushCell(finalizationRegistry, cell);
181  if (hasUnregisterToken) {
182    // If an unregister token is provided, a runtime call is needed to
183    // do some OrderedHashTable operations and register the mapping.
184    // See v8:10705.
185    runtime::JSFinalizationRegistryRegisterWeakCellWithUnregisterToken(
186        finalizationRegistry, cell);
187  }
188  // 8. Return undefined.
189  return Undefined;
190}
191
192transitioning javascript builtin
193FinalizationRegistryPrototypeCleanupSome(
194    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
195  // 1. Let finalizationRegistry be the this value.
196  //
197  // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
198  const methodName: constexpr string =
199      'FinalizationRegistry.prototype.cleanupSome';
200  const finalizationRegistry =
201      Cast<JSFinalizationRegistry>(receiver) otherwise ThrowTypeError(
202          MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
203
204  let callback: Callable;
205  if (arguments[0] != Undefined) {
206    // 4. If callback is not undefined and IsCallable(callback) is
207    //    false, throw a TypeError exception.
208    callback = Cast<Callable>(arguments[0]) otherwise ThrowTypeError(
209        MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]);
210  } else {
211    callback = finalizationRegistry.cleanup;
212  }
213
214  FinalizationRegistryCleanupLoop(finalizationRegistry, callback);
215  return Undefined;
216}
217}
218