blob: 84499e19e1a0c90908706cc802d8679f4cf66dda [file] [log] [blame]
// Copyright 2020 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.
namespace runtime {
extern runtime
ShrinkFinalizationRegistryUnregisterTokenMap(
Context, JSFinalizationRegistry): void;
extern runtime JSFinalizationRegistryRegisterWeakCellWithUnregisterToken(
implicit context: Context)(JSFinalizationRegistry, WeakCell): void;
}
namespace weakref {
extern transitioning macro
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
JSFinalizationRegistry, WeakCell): void;
macro SplitOffTail(weakCell: WeakCell): WeakCell|Undefined {
const weakCellTail = weakCell.next;
weakCell.next = Undefined;
typeswitch (weakCellTail) {
case (Undefined): {
}
case (tailIsNowAHead: WeakCell): {
assert(tailIsNowAHead.prev == weakCell);
tailIsNowAHead.prev = Undefined;
}
}
return weakCellTail;
}
transitioning macro
PopClearedCell(finalizationRegistry: JSFinalizationRegistry): WeakCell|
Undefined {
typeswitch (finalizationRegistry.cleared_cells) {
case (Undefined): {
return Undefined;
}
case (weakCell: WeakCell): {
assert(weakCell.prev == Undefined);
finalizationRegistry.cleared_cells = SplitOffTail(weakCell);
// If the WeakCell has an unregister token, remove the cell from the
// unregister token linked lists and and the unregister token from
// key_map. This doesn't shrink key_map, which is done manually after
// the cleanup loop to avoid a runtime call.
if (weakCell.unregister_token != Undefined) {
RemoveFinalizationRegistryCellFromUnregisterTokenMap(
finalizationRegistry, weakCell);
}
return weakCell;
}
}
}
transitioning macro PushCell(
finalizationRegistry: JSFinalizationRegistry, cell: WeakCell) {
cell.next = finalizationRegistry.active_cells;
typeswitch (finalizationRegistry.active_cells) {
case (Undefined): {
}
case (oldHead: WeakCell): {
oldHead.prev = cell;
}
}
finalizationRegistry.active_cells = cell;
}
transitioning macro
FinalizationRegistryCleanupLoop(implicit context: Context)(
finalizationRegistry: JSFinalizationRegistry, callback: Callable) {
while (true) {
const weakCellHead = PopClearedCell(finalizationRegistry);
typeswitch (weakCellHead) {
case (Undefined): {
break;
}
case (weakCell: WeakCell): {
try {
Call(context, callback, Undefined, weakCell.holdings);
} catch (e) {
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
ReThrow(context, e);
}
}
}
}
runtime::ShrinkFinalizationRegistryUnregisterTokenMap(
context, finalizationRegistry);
}
transitioning javascript builtin
FinalizationRegistryConstructor(
js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
target: JSFunction)(cleanupCallback: JSAny): JSFinalizationRegistry {
// 1. If NewTarget is undefined, throw a TypeError exception.
if (newTarget == Undefined) {
ThrowTypeError(
MessageTemplate::kConstructorNotFunction, 'FinalizationRegistry');
}
// 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.
const cleanupCallback = Cast<Callable>(cleanupCallback) otherwise
ThrowTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable);
// 3. Let finalizationRegistry be ? OrdinaryCreateFromConstructor(NewTarget,
// "%FinalizationRegistryPrototype%", « [[Realm]], [[CleanupCallback]],
// [[Cells]] »).
const map = GetDerivedMap(target, UnsafeCast<JSReceiver>(newTarget));
const finalizationRegistry = UnsafeCast<JSFinalizationRegistry>(
AllocateFastOrSlowJSObjectFromMap(map));
// 4. Let fn be the active function object.
// 5. Set finalizationRegistry.[[Realm]] to fn.[[Realm]].
finalizationRegistry.native_context = context;
// 6. Set finalizationRegistry.[[CleanupCallback]] to cleanupCallback.
finalizationRegistry.cleanup = cleanupCallback;
finalizationRegistry.flags =
SmiTag(FinalizationRegistryFlags{scheduled_for_cleanup: false});
// 7. Set finalizationRegistry.[[Cells]] to be an empty List.
assert(finalizationRegistry.active_cells == Undefined);
assert(finalizationRegistry.cleared_cells == Undefined);
assert(finalizationRegistry.key_map == Undefined);
// 8. Return finalizationRegistry.
return finalizationRegistry;
}
transitioning javascript builtin
FinalizationRegistryRegister(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let finalizationRegistry be the this value.
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
const finalizationRegistry = Cast<JSFinalizationRegistry>(receiver) otherwise
ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver,
'FinalizationRegistry.prototype.register', receiver);
// 3. If Type(target) is not Object, throw a TypeError exception.
const target = Cast<JSReceiver>(arguments[0]) otherwise ThrowTypeError(
MessageTemplate::kWeakRefsRegisterTargetMustBeObject);
const heldValue = arguments[1];
// 4. If SameValue(target, heldValue), throw a TypeError exception.
if (target == heldValue) {
ThrowTypeError(
MessageTemplate::kWeakRefsRegisterTargetAndHoldingsMustNotBeSame);
}
const unregisterToken = arguments[2];
// 5. If Type(unregisterToken) is not Object,
// a. If unregisterToken is not undefined, throw a TypeError exception.
// b. Set unregisterToken to empty.
let hasUnregisterToken: bool = false;
typeswitch (unregisterToken) {
case (Undefined): {
}
case (JSReceiver): {
hasUnregisterToken = true;
}
case (JSAny): deferred {
ThrowTypeError(
MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
unregisterToken);
}
}
// 6. Let cell be the Record { [[WeakRefTarget]] : target, [[HeldValue]]:
// heldValue, [[UnregisterToken]]: unregisterToken }.
// Allocate the WeakCell object in the old space, because 1) WeakCell weakness
// handling is only implemented in the old space 2) they're supposedly
// long-living. TODO(marja, gsathya): Support WeakCells in Scavenger.
const cell = new (Pretenured) WeakCell{
map: GetWeakCellMap(),
finalization_registry: finalizationRegistry,
target: target,
unregister_token: unregisterToken,
holdings: heldValue,
prev: Undefined,
next: Undefined,
key_list_prev: Undefined,
key_list_next: Undefined
};
// 7. Append cell to finalizationRegistry.[[Cells]].
PushCell(finalizationRegistry, cell);
if (hasUnregisterToken) {
// If an unregister token is provided, a runtime call is needed to
// do some OrderedHashTable operations and register the mapping.
// See v8:10705.
runtime::JSFinalizationRegistryRegisterWeakCellWithUnregisterToken(
finalizationRegistry, cell);
}
// 8. Return undefined.
return Undefined;
}
transitioning javascript builtin
FinalizationRegistryPrototypeCleanupSome(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
// 1. Let finalizationRegistry be the this value.
//
// 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).
const methodName: constexpr string =
'FinalizationRegistry.prototype.cleanupSome';
const finalizationRegistry =
Cast<JSFinalizationRegistry>(receiver) otherwise ThrowTypeError(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
let callback: Callable;
if (arguments[0] != Undefined) {
// 4. If callback is not undefined and IsCallable(callback) is
// false, throw a TypeError exception.
callback = Cast<Callable>(arguments[0]) otherwise ThrowTypeError(
MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]);
} else {
callback = finalizationRegistry.cleanup;
}
FinalizationRegistryCleanupLoop(finalizationRegistry, callback);
return Undefined;
}
}