dotnet / corert

This repo contains CoreRT, an experimental .NET Core runtime optimized for AOT (ahead of time compilation) scenarios, with the accompanying compiler toolchain.

Home Page:http://dot.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Wasm: Provide implementation for InternalCall in external project

yowl opened this issue · comments

commented

To get interoperability with mono WebAssembly, some methods that are InternalCall are needed , such as https://github.com/mono/WebAssembly.JSInterop/blob/9e65a41cf1043ce29c7d722bd3885648a653e734/src/WebAssembly.JSInterop/InternalCalls.cs#L14-L15.

I'm currently working around this with something like

            if (callee.IsInternalCall)
            {
                if (callee.Name == "InvokeJS")
                {
                    var coreRtJsInternalCallsType = _compilation.TypeSystemContext
                        .GetModuleForSimpleName("CoreRT.WebAssembly.Interop")
                        .GetKnownType("CoreRT.WebAssembly.Interop", "InternalCalls");
                    callee = coreRtJsInternalCallsType.GetMethod("InvokeJS", null);
                }

in ILToWebAssemblyImporter.cs, ImportCall but can I do this in a separate dll without substituting the method in Ilc?

The mapping needs to be somewhere. We usually place the mapping on the InternalCall itself, like so:

[MethodImpl(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpSendCustomEventToDebugger")]
internal static extern void RhpSendCustomEventToDebugger(IntPtr payload, int length);

Then the compiler just substitutes calls to this method with "call to an external symbol named in the RuntimeImportAttribute". We sometimes define the symbol in managed code again, using a RuntimeExportAttribute. But sometimes it's defined in C/C++/asm.

Since I assume you're not building your own WebAssembly.JSinterop (but that would be an option), a thing that comes to my mind would be a convention-based approach: if there's an InternalCall in an assembly named WebAssembly.JSinterop, look for a method with the same name and signature, on a type with the same name, but in the CoreRT.WebAssembly.Interop assembly. That way you don't have to hardcode the full list in the compiler.

commented

I can't really do the same as the InternalCall is in an external project: https://github.com/unoplatform/uno/blob/024eebddd33ac0dfa6b6d8ea0871d5c6effc9f12/src/Uno.Foundation/Runtime.wasm.cs#L40.

" you're not building your own WebAssembly.JSinterop" I am trying this route:

namespace WebAssembly
{
    namespace JSInterop
    {
        public static class InternalCalls
        {
            // Matches this signature:
            // https://github.com/mono/mono/blob/f24d652d567c4611f9b4e3095be4e2a1a2ab23a4/sdks/wasm/driver.c#L21
            [RuntimeExport("InvokeJSUnmarshalled")]
            public static IntPtr InvokeJSUnmarshalled(out string exception, string js, IntPtr p1, IntPtr p2, IntPtr p3)
            {
                // convention : if the methodId is known, then js is null and p1 is the method id
                return CoreRT.WebAssembly.Interop.InternalCalls.InvokeJSUnmarshalledInternal(js, js?.Length ?? 0, p1, p2, p3, out exception);
            }
        }
    }
}

However that doesn't get found as the mapping as it seems to want to find it in Uno.Foundation.Runtime.WebAssembly

image

Oh, okay. I guess you'll need to keep this hardcoded as-is, to get compatibility with Mono:

https://github.com/dotnet/runtime/blob/9ba9a300a08170c8170ea52981810f41fad68cf0/src/mono/wasm/runtime/driver.c#L400-L407

The Mono team always prefers whatever quick hack gets the job done (TODO: what happens when two types in different assemblies have the same FQN?) - here the hack is to hardcode a name/namespace in the runtime, which means we need to hardcode in the compiler - there's no opportunity to inject an indirection.