// 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; } 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 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 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(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(arguments[0]) otherwise ThrowTypeError( MessageTemplate::kWeakRefsCleanupMustBeCallable, arguments[0]); } else { callback = finalizationRegistry.cleanup; } FinalizationRegistryCleanupLoop(finalizationRegistry, callback); return Undefined; } }