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