| // 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; |
| } |
| } |