2021-06-15 22:16:17 +03:00
/*
2022-06-22 23:10:11 +03:00
* Copyright ( c ) 2021 - 2022 , Idan Horowitz < idan . horowitz @ serenityos . org >
2021-06-15 22:16:17 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-01-23 02:12:26 -07:00
# include <LibJS/Runtime/AbstractOperations.h>
2021-06-15 22:16:17 +03:00
# include <LibJS/Runtime/FinalizationRegistry.h>
namespace JS {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( FinalizationRegistry ) ;
2023-11-19 09:45:05 +01:00
2024-11-15 04:01:23 +13:00
FinalizationRegistry : : FinalizationRegistry ( Realm & realm , GC : : Ref < JobCallback > cleanup_callback , Object & prototype )
2022-12-14 12:17:58 +01:00
: Object ( ConstructWithPrototypeTag : : Tag , prototype )
2021-06-15 22:16:17 +03:00
, WeakContainer ( heap ( ) )
2022-09-01 11:38:35 +02:00
, m_realm ( realm )
2024-03-12 03:49:13 +01:00
, m_cleanup_callback ( cleanup_callback )
2021-06-15 22:16:17 +03:00
{
}
2022-06-22 23:10:11 +03:00
void FinalizationRegistry : : add_finalization_record ( Cell & target , Value held_value , Cell * unregister_token )
2021-06-15 22:16:17 +03:00
{
2025-04-04 23:16:34 +02:00
VERIFY ( ! held_value . is_special_empty_value ( ) ) ;
2021-06-15 22:16:17 +03:00
m_records . append ( { & target , held_value , unregister_token } ) ;
}
2023-01-27 21:59:17 +00:00
// Extracted from FinalizationRegistry.prototype.unregister ( unregisterToken )
2022-06-22 23:10:11 +03:00
bool FinalizationRegistry : : remove_by_token ( Cell & unregister_token )
2021-06-15 22:16:17 +03:00
{
2023-01-27 21:59:17 +00:00
// 4. Let removed be false.
2021-06-15 22:16:17 +03:00
auto removed = false ;
2023-01-27 21:59:17 +00:00
// 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of finalizationRegistry.[[Cells]], do
2021-06-15 22:16:17 +03:00
for ( auto it = m_records . begin ( ) ; it ! = m_records . end ( ) ; + + it ) {
2023-01-27 21:59:17 +00:00
// a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then
2021-06-15 22:16:17 +03:00
if ( it - > unregister_token = = & unregister_token ) {
2023-01-27 21:59:17 +00:00
// i. Remove cell from finalizationRegistry.[[Cells]].
2021-06-15 22:16:17 +03:00
it . remove ( m_records ) ;
2023-01-27 21:59:17 +00:00
// ii. Set removed to true.
2021-06-15 22:16:17 +03:00
removed = true ;
}
}
2023-01-27 21:59:17 +00:00
// 6. Return removed.
2021-06-15 22:16:17 +03:00
return removed ;
}
2024-11-15 04:01:23 +13:00
void FinalizationRegistry : : remove_dead_cells ( Badge < GC : : Heap > )
2021-06-15 22:16:17 +03:00
{
2021-10-05 18:44:31 +02:00
auto any_cells_were_removed = false ;
for ( auto & record : m_records ) {
if ( ! record . target | | record . target - > state ( ) = = Cell : : State : : Live )
continue ;
record . target = nullptr ;
any_cells_were_removed = true ;
break ;
2021-06-15 22:16:17 +03:00
}
2025-01-23 09:59:22 +01:00
if ( any_cells_were_removed ) {
// NOTE: We make a GC::Root here to ensure that the FinalizationRegistry stays alive
// even if a subsequent GC is triggered before the callback has a chance to run.
heap ( ) . enqueue_post_gc_task ( [ that = GC : : make_root ( this ) ] ( ) {
that - > vm ( ) . host_enqueue_finalization_registry_cleanup_job ( * that ) ;
} ) ;
}
2021-06-15 22:16:17 +03:00
}
// 9.13 CleanupFinalizationRegistry ( finalizationRegistry ), https://tc39.es/ecma262/#sec-cleanup-finalization-registry
2024-11-15 04:01:23 +13:00
ThrowCompletionOr < void > FinalizationRegistry : : cleanup ( GC : : Ptr < JobCallback > callback )
2021-06-15 22:16:17 +03:00
{
2022-02-06 03:46:45 +00:00
auto & vm = this - > vm ( ) ;
2022-02-07 14:42:24 +01:00
// 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.
// Note: Ensured by type.
// 2. Let callback be finalizationRegistry.[[CleanupCallback]].
2024-03-12 03:49:13 +01:00
auto cleanup_callback = callback ? callback : m_cleanup_callback ;
2022-02-07 14:42:24 +01:00
// 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, an implementation may perform the following steps:
2021-06-15 22:16:17 +03:00
for ( auto it = m_records . begin ( ) ; it ! = m_records . end ( ) ; + + it ) {
2022-02-07 14:42:24 +01:00
// a. Choose any such cell.
2021-06-15 22:16:17 +03:00
if ( it - > target ! = nullptr )
continue ;
2022-02-07 14:42:24 +01:00
// b. Remove cell from finalizationRegistry.[[Cells]].
2024-12-26 14:32:52 +01:00
GC : : RootVector < Value > arguments ( vm . heap ( ) ) ;
2022-02-06 03:46:45 +00:00
arguments . append ( it - > held_value ) ;
2021-06-15 22:16:17 +03:00
it . remove ( m_records ) ;
2022-02-07 14:42:24 +01:00
// c. Perform ? HostCallJobCallback(callback, undefined, « cell.[[HeldValue]] »).
2024-03-12 03:49:13 +01:00
TRY ( vm . host_call_job_callback ( * cleanup_callback , js_undefined ( ) , move ( arguments ) ) ) ;
2021-06-15 22:16:17 +03:00
}
2022-02-07 14:42:24 +01:00
2022-05-02 20:54:39 +02:00
// 4. Return unused.
2022-02-07 14:42:24 +01:00
return { } ;
2021-06-15 22:16:17 +03:00
}
void FinalizationRegistry : : visit_edges ( Cell : : Visitor & visitor )
{
2021-09-11 14:05:12 +02:00
Base : : visit_edges ( visitor ) ;
2022-10-02 11:40:34 +01:00
visitor . visit ( m_realm ) ;
2024-03-12 03:49:13 +01:00
visitor . visit ( m_cleanup_callback ) ;
2021-06-15 22:16:17 +03:00
for ( auto & record : m_records ) {
visitor . visit ( record . held_value ) ;
visitor . visit ( record . unregister_token ) ;
}
}
}