dotnet / runtimelab

This repo is for experimentation and exploring new ideas that may or may not make it into the main dotnet/runtime repo.
MIT License
1.41k stars 197 forks source link

[NativeAOT-LLVM] DotnetJS exception on marshalled c#-objects finalizer call #2625

Closed maxkatz6 closed 2 months ago

maxkatz6 commented 3 months ago

If any C# object is passed to JSImport, JavaScript side of the code will register it in FinalizationRegistry. This way C# side can get notification, when object is no longer used by JS side.

This logic doesn't seem to work with DotnetJS in LLVM. After a while app will output error in the console:

dotnet.runtime.js:3 
 Uncaught 
Error: Missing wasm export '_System_Runtime_InteropServices_JavaScript_JavaScriptExports_ReleaseJSOwnedObjectByGCHandle' (for System.Runtime.InteropServices.JavaScript.JavaScriptExports.ReleaseJSOwnedObjectByGCHandle)
    at dotnet.runtime.js:3:23922
    at Zt (dotnet.runtime.js:3:23460)
    at dotnet.runtime.js:3:34991
    at vr (dotnet.runtime.js:3:35059)
    at Tr (dotnet.runtime.js:3:35231)
    at FinalizationRegistry.cleanupSome (<anonymous>)

Minimal repro:

public static async Task Main()
{
    while (true)
    {
        // Callback is expected to be hold in memory, until JS FinalizationRegistry notifies it's no longer used by JS code.
        SetTimeout(() =>
        {
            Console.WriteLine("From callback: " + DateTime.Now);
        }, 1000);
        await Task.Delay(1000);
    }
}
[JSImport("globalThis.setTimeout")]
public static partial int SetTimeout([JSMarshalAs<JSType.Function>] Action callback, int intervalMs);

Run it and wait, resizing window also helps to trigger JS FinalizationRegistry.cleanupSome. Or run minimal repro: naot-llvm-demo-finalizer.zip

pavelsavara commented 2 months ago

cc @maraf

maraf commented 2 months ago

Thank you for the report. I have encountered it as well when running some tests and have already a patch. I'll send PR soon