/src/serenity/Userland/Libraries/LibJS/Runtime/FinalizationRegistry.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibJS/Runtime/AbstractOperations.h> |
8 | | #include <LibJS/Runtime/FinalizationRegistry.h> |
9 | | |
10 | | namespace JS { |
11 | | |
12 | | JS_DEFINE_ALLOCATOR(FinalizationRegistry); |
13 | | |
14 | | FinalizationRegistry::FinalizationRegistry(Realm& realm, NonnullGCPtr<JobCallback> cleanup_callback, Object& prototype) |
15 | 0 | : Object(ConstructWithPrototypeTag::Tag, prototype) |
16 | 0 | , WeakContainer(heap()) |
17 | 0 | , m_realm(realm) |
18 | 0 | , m_cleanup_callback(cleanup_callback) |
19 | 0 | { |
20 | 0 | } |
21 | | |
22 | | void FinalizationRegistry::add_finalization_record(Cell& target, Value held_value, Cell* unregister_token) |
23 | 0 | { |
24 | 0 | VERIFY(!held_value.is_empty()); |
25 | 0 | m_records.append({ &target, held_value, unregister_token }); |
26 | 0 | } |
27 | | |
28 | | // Extracted from FinalizationRegistry.prototype.unregister ( unregisterToken ) |
29 | | bool FinalizationRegistry::remove_by_token(Cell& unregister_token) |
30 | 0 | { |
31 | | // 4. Let removed be false. |
32 | 0 | auto removed = false; |
33 | | |
34 | | // 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of finalizationRegistry.[[Cells]], do |
35 | 0 | for (auto it = m_records.begin(); it != m_records.end(); ++it) { |
36 | | // a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then |
37 | 0 | if (it->unregister_token == &unregister_token) { |
38 | | // i. Remove cell from finalizationRegistry.[[Cells]]. |
39 | 0 | it.remove(m_records); |
40 | | |
41 | | // ii. Set removed to true. |
42 | 0 | removed = true; |
43 | 0 | } |
44 | 0 | } |
45 | | |
46 | | // 6. Return removed. |
47 | 0 | return removed; |
48 | 0 | } |
49 | | |
50 | | void FinalizationRegistry::remove_dead_cells(Badge<Heap>) |
51 | 0 | { |
52 | 0 | auto any_cells_were_removed = false; |
53 | 0 | for (auto& record : m_records) { |
54 | 0 | if (!record.target || record.target->state() == Cell::State::Live) |
55 | 0 | continue; |
56 | 0 | record.target = nullptr; |
57 | 0 | any_cells_were_removed = true; |
58 | 0 | break; |
59 | 0 | } |
60 | 0 | if (any_cells_were_removed) |
61 | 0 | vm().host_enqueue_finalization_registry_cleanup_job(*this); |
62 | 0 | } |
63 | | |
64 | | // 9.13 CleanupFinalizationRegistry ( finalizationRegistry ), https://tc39.es/ecma262/#sec-cleanup-finalization-registry |
65 | | ThrowCompletionOr<void> FinalizationRegistry::cleanup(JS::GCPtr<JobCallback> callback) |
66 | 0 | { |
67 | 0 | auto& vm = this->vm(); |
68 | | |
69 | | // 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots. |
70 | | // Note: Ensured by type. |
71 | | |
72 | | // 2. Let callback be finalizationRegistry.[[CleanupCallback]]. |
73 | 0 | auto cleanup_callback = callback ? callback : m_cleanup_callback; |
74 | | |
75 | | // 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, an implementation may perform the following steps: |
76 | 0 | for (auto it = m_records.begin(); it != m_records.end(); ++it) { |
77 | | // a. Choose any such cell. |
78 | 0 | if (it->target != nullptr) |
79 | 0 | continue; |
80 | | |
81 | | // b. Remove cell from finalizationRegistry.[[Cells]]. |
82 | 0 | MarkedVector<Value> arguments(vm.heap()); |
83 | 0 | arguments.append(it->held_value); |
84 | 0 | it.remove(m_records); |
85 | | |
86 | | // c. Perform ? HostCallJobCallback(callback, undefined, « cell.[[HeldValue]] »). |
87 | 0 | TRY(vm.host_call_job_callback(*cleanup_callback, js_undefined(), move(arguments))); |
88 | 0 | } |
89 | | |
90 | | // 4. Return unused. |
91 | 0 | return {}; |
92 | 0 | } |
93 | | |
94 | | void FinalizationRegistry::visit_edges(Cell::Visitor& visitor) |
95 | 0 | { |
96 | 0 | Base::visit_edges(visitor); |
97 | 0 | visitor.visit(m_realm); |
98 | 0 | visitor.visit(m_cleanup_callback); |
99 | 0 | for (auto& record : m_records) { |
100 | 0 | visitor.visit(record.held_value); |
101 | 0 | visitor.visit(record.unregister_token); |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | | } |