Skip to content

Commit 66588de

Browse files
committed
fix(jerryscript): track native holders to ensure cleanup
1 parent b29d6fe commit 66588de

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
mrdocs-release-package-artifact: release-packages-{{{ lowercase os }}}
9898
output-file: matrix.json
9999
trace-commands: true
100+
github-token: ${{ secrets.GITHUB_TOKEN }}
100101

101102
# Set up the version as expected by the LLVM matrix script and @actions/core
102103
- name: Setup Node.js

src/lib/Support/JavaScript.cpp

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
#include <string>
8484
#include <string_view>
8585
#include <thread>
86+
#include <unordered_set>
8687
#include <utility>
8788
#include <vector>
8889

@@ -459,6 +460,21 @@ struct Context::Impl {
459460
// Optional diagnostics: track live JS handles we create (Value copies etc).
460461
std::atomic<int> live_handles{0};
461462

463+
// Track all native holders (DomValueHolder, FunctionHolder) so we can
464+
// delete them during cleanup if JerryScript's GC doesn't finalize them.
465+
// This handles the case where objects are still referenced from globals.
466+
std::unordered_set<NativeHolder*> holders;
467+
468+
void registerHolder(NativeHolder* h)
469+
{
470+
holders.insert(h);
471+
}
472+
473+
void unregisterHolder(NativeHolder* h)
474+
{
475+
holders.erase(h);
476+
}
477+
462478
Impl()
463479
{
464480
// Temporarily set TLS so jerry_init() can find the context.
@@ -531,10 +547,8 @@ struct Context::Impl {
531547
void* prev_ctx = get_tls_jerry_context();
532548
set_tls_jerry_context(jerry_ctx);
533549

534-
// Run full garbage collection to finalize all objects and trigger
535-
// native pointer free callbacks (DomValueHolder::free_cb, etc.)
536-
// before jerry_cleanup(). This ensures all our native holders are
537-
// properly deleted.
550+
// Run full garbage collection to finalize unreferenced objects and
551+
// trigger native pointer free callbacks (DomValueHolder::free_cb, etc.)
538552
jerry_heap_gc(JERRY_GC_PRESSURE_HIGH);
539553

540554
// jerry_cleanup() cleans up JS objects but with JERRY_EXTERNAL_CONTEXT=ON,
@@ -545,6 +559,16 @@ struct Context::Impl {
545559
// jerry_port_context_free is our implementation that calls std::free().
546560
jerry_port_context_free(jerry_ctx, 0);
547561

562+
// Delete any remaining native holders that weren't garbage collected.
563+
// This handles objects still referenced from globals at cleanup time.
564+
// The free_cb won't be called for these since JerryScript just abandons
565+
// them during cleanup, so we delete them manually.
566+
for (NativeHolder* h : holders)
567+
{
568+
delete h;
569+
}
570+
holders.clear();
571+
548572
// Context is now destroyed. Set jerry_ctx to nullptr and mark dead.
549573
jerry_ctx = nullptr;
550574
alive = false;
@@ -568,6 +592,11 @@ struct Context::Impl {
568592
void DomValueHolder::free_cb(void* p, jerry_object_native_info_t*)
569593
{
570594
auto* h = static_cast<DomValueHolder*>(p);
595+
// Always unregister from tracking set so we don't double-free during cleanup.
596+
if (h->impl)
597+
{
598+
h->impl->unregisterHolder(h);
599+
}
571600
delete h;
572601
}
573602

@@ -1634,6 +1663,7 @@ makeObjectProxy(dom::Object obj, std::shared_ptr<Context::Impl> impl)
16341663
auto* holder = new DomValueHolder();
16351664
holder->impl = impl;
16361665
holder->value = dom::Value(std::move(obj));
1666+
impl->registerHolder(holder);
16371667

16381668
// Create an empty target object (the proxy intercepts all access)
16391669
jerry_value_t target = jerry_object();
@@ -1804,6 +1834,11 @@ struct FunctionHolder : NativeHolder {
18041834
free_cb(void* p, jerry_object_native_info_t*)
18051835
{
18061836
auto* h = static_cast<FunctionHolder*>(p);
1837+
// Always unregister from tracking set so we don't double-free during cleanup.
1838+
if (h->impl)
1839+
{
1840+
h->impl->unregisterHolder(h);
1841+
}
18071842
delete h;
18081843
}
18091844
};
@@ -1818,6 +1853,7 @@ makeFunctionProxy(dom::Function fn, std::shared_ptr<Context::Impl> impl)
18181853
auto* holder = new FunctionHolder();
18191854
holder->impl = impl;
18201855
holder->fn = std::move(fn);
1856+
impl->registerHolder(holder);
18211857

18221858
jerry_value_t func = jerry_function_external(
18231859
[](jerry_call_info_t const* call_info_p,

0 commit comments

Comments
 (0)