diff --git a/Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp b/Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp index 3ba8e98d1d..ef7fda2010 100644 --- a/Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp +++ b/Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp @@ -23,15 +23,25 @@ void FinalizationRegistry::add_finalization_record(Cell& target, Value held_valu m_records.append({ &target, held_value, unregister_token }); } +// Extracted from FinalizationRegistry.prototype.unregister ( unregisterToken ) bool FinalizationRegistry::remove_by_token(Cell& unregister_token) { + // 4. Let removed be false. auto removed = false; + + // 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of finalizationRegistry.[[Cells]], do for (auto it = m_records.begin(); it != m_records.end(); ++it) { + // a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then if (it->unregister_token == &unregister_token) { + // i. Remove cell from finalizationRegistry.[[Cells]]. it.remove(m_records); + + // ii. Set removed to true. removed = true; } } + + // 6. Return removed. return removed; } diff --git a/Userland/Libraries/LibJS/Runtime/FinalizationRegistryPrototype.cpp b/Userland/Libraries/LibJS/Runtime/FinalizationRegistryPrototype.cpp index 03ad9f4bbd..057152aef2 100644 --- a/Userland/Libraries/LibJS/Runtime/FinalizationRegistryPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/FinalizationRegistryPrototype.cpp @@ -32,50 +32,72 @@ void FinalizationRegistryPrototype::initialize(Realm& realm) // @STAGE 2@ FinalizationRegistry.prototype.cleanupSome ( [ callback ] ), https://github.com/tc39/proposal-cleanup-some/blob/master/spec/finalization-registry.html JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::cleanup_some) { + auto callback = vm.argument(0); + + // 1. Let finalizationRegistry be the this value. + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). auto* finalization_registry = TRY(typed_this_object(vm)); - auto callback = vm.argument(0); + // 3. If callback is present and IsCallable(callback) is false, throw a TypeError exception. if (vm.argument_count() > 0 && !callback.is_function()) return vm.throw_completion(ErrorType::NotAFunction, callback.to_string_without_side_effects()); // IMPLEMENTATION DEFINED: The specification for this function hasn't been updated to accommodate for JobCallback records. // This just follows how the constructor immediately converts the callback to a JobCallback using HostMakeJobCallback. + // 4. Perform ? CleanupFinalizationRegistry(finalizationRegistry, callback). TRY(finalization_registry->cleanup(callback.is_undefined() ? Optional {} : vm.host_make_job_callback(callback.as_function()))); + // 5. Return undefined. return js_undefined(); } // 26.2.3.2 FinalizationRegistry.prototype.register ( target, heldValue [ , unregisterToken ] ), https://tc39.es/ecma262/#sec-finalization-registry.prototype.register JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::register_) { + auto target = vm.argument(0); + auto held_value = vm.argument(1); + auto unregister_token = vm.argument(2); + + // 1. Let finalizationRegistry be the this value. + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). auto* finalization_registry = TRY(typed_this_object(vm)); - auto target = vm.argument(0); + // 3. If target is not an Object, throw a TypeError exception. if (!can_be_held_weakly(target)) return vm.throw_completion(ErrorType::CannotBeHeldWeakly, target.to_string_without_side_effects()); - auto held_value = vm.argument(1); + // 4. If SameValue(target, heldValue) is true, throw a TypeError exception. if (same_value(target, held_value)) return vm.throw_completion(ErrorType::FinalizationRegistrySameTargetAndValue); - auto unregister_token = vm.argument(2); + // 5. If unregisterToken is not an Object, then + // a. If unregisterToken is not undefined, throw a TypeError exception. + // b. Set unregisterToken to empty. if (!can_be_held_weakly(unregister_token) && !unregister_token.is_undefined()) return vm.throw_completion(ErrorType::CannotBeHeldWeakly, unregister_token.to_string_without_side_effects()); + // 6. Let cell be the Record { [[WeakRefTarget]]: target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }. + // 7. Append cell to finalizationRegistry.[[Cells]]. finalization_registry->add_finalization_record(target.as_cell(), held_value, unregister_token.is_undefined() ? nullptr : &unregister_token.as_cell()); + // 8. Return undefined. return js_undefined(); } // 26.2.3.3 FinalizationRegistry.prototype.unregister ( unregisterToken ), https://tc39.es/ecma262/#sec-finalization-registry.prototype.unregister JS_DEFINE_NATIVE_FUNCTION(FinalizationRegistryPrototype::unregister) { + auto unregister_token = vm.argument(0); + + // 1. Let finalizationRegistry be the this value. + // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]). auto* finalization_registry = TRY(typed_this_object(vm)); - auto unregister_token = vm.argument(0); + // 3. If unregisterToken is not an Object, throw a TypeError exception. if (!can_be_held_weakly(unregister_token)) return vm.throw_completion(ErrorType::CannotBeHeldWeakly, unregister_token.to_string_without_side_effects()); + // 4-6. return Value(finalization_registry->remove_by_token(unregister_token.as_cell())); }