From 7840c9b6f69324167571f142cd70daeca16e4a6d Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Tue, 5 May 2026 11:52:28 -0300 Subject: [PATCH] Free RustCallStatus pointer after every Rust call The call-status pointer allocated by createPointer in makeRustCall was never released. ffi-rs's createPointer Box-allocates non-StackStruct values via Box::into_raw(Box::new(generate_c_struct(...))), and the JsExternal wrapper does not free that memory on garbage collection per ffi-rs's documented contract. Neither restorePointer nor unwrapPointer free anything either. Result: every synchronous Rust call leaked the RustCallStatus struct plus its wrapper Box, and method calls leaked twice (once for clonePointer, once for the call itself). Wrap the call site in try/finally and call freePointer with the same struct paramsType so the allocation is reclaimed regardless of whether the call returned normally or threw. --- templates/sys.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/templates/sys.ts b/templates/sys.ts index cc0ca1a..12bfd0b 100644 --- a/templates/sys.ts +++ b/templates/sys.ts @@ -195,15 +195,23 @@ class UniffiFfiRsRustCaller { _checkUniffiLoaded(); const $callStatus = this.createCallStatus(); - let returnedVal = caller(unwrapPointer($callStatus)[0]); - - const [callStatus] = restorePointer({ - retType: [DataType_UniffiRustCallStatus], - paramsValue: $callStatus, - }); - uniffiCheckCallStatus(callStatus, liftString, liftError); - - return returnedVal; + try { + let returnedVal = caller(unwrapPointer($callStatus)[0]); + + const [callStatus] = restorePointer({ + retType: [DataType_UniffiRustCallStatus], + paramsValue: $callStatus, + }); + uniffiCheckCallStatus(callStatus, liftString, liftError); + + return returnedVal; + } finally { + freePointer({ + paramsType: [DataType_UniffiRustCallStatus], + paramsValue: $callStatus, + pointerType: PointerType.RsPointer, + }); + } } }