Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NativeAOT LLVM] Enable JS interop #2440

Merged
merged 32 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e218cf7
Replace icalls with dllimports for JS import/export. Update javascrip…
maraf Nov 24, 2023
cbb8d6f
Add js import sample
maraf Dec 14, 2023
44611b9
Fix typescript build
maraf Dec 15, 2023
9f6993e
Fix Program.cs
maraf Dec 15, 2023
0f36fbf
Fix Program.cs
maraf Dec 15, 2023
b07ad92
Try to fix JS interop with explicit project reference
maraf Dec 17, 2023
3f05288
Check math result
maraf Dec 18, 2023
132bc23
Comment about analyzer references
maraf Dec 18, 2023
3aec518
Move DllImports to a new file
maraf Dec 18, 2023
6b67394
Split mono_wasm_bind_js_function to mono and naot
maraf Dec 18, 2023
43a5389
Split mono_wasm_bind_cs_function
maraf Dec 18, 2023
1a666ec
Fix emscripten import names
maraf Dec 18, 2023
b8621b8
Add JSExport to sample
maraf Dec 18, 2023
d2107e4
Use different exit codes than runtime does
maraf Dec 18, 2023
4ee6148
Feedback about Interop.Runtime.cs
maraf Dec 18, 2023
da24ea2
Split typescript imports
maraf Dec 19, 2023
fce6919
Fix sample
maraf Dec 19, 2023
a4a2a1c
Revert typescript split
maraf Dec 19, 2023
2542cc9
Fix export sample
maraf Dec 19, 2023
eee20bd
Remove export sample
maraf Dec 19, 2023
d112a86
Make JSExport work
maraf Dec 19, 2023
a0cb512
Comments
maraf Dec 19, 2023
3b3b7a5
Import DotNetJsApi.targets earlier so IlcExportUnmanagedEntrypoints=t…
maraf Dec 19, 2023
0ecbd84
Add exception sample. Move todos to string marshaling in exception ha…
maraf Dec 19, 2023
4b81dab
Feedback
maraf Dec 19, 2023
bb0fed7
Use library import and utf16
maraf Dec 19, 2023
79ff965
utf16ToString instead of Module.UTF16ToString
maraf Dec 19, 2023
bfd9d76
Write to ExportsFile only if it doesn't contain required exports
maraf Dec 19, 2023
4ffd55f
Drop emscripten's UTF16ToString
maraf Dec 19, 2023
7025ec8
Quotes around variables
maraf Dec 19, 2023
2b48d5a
Feedback
maraf Dec 19, 2023
edc82cb
More feedback
maraf Dec 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/coreclr/nativeaot/BuildIntegration/DotNetJsApi.targets
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LinkNativeDependsOn Condition="'$(_targetOS)' == 'browser' and '$(DotNetJsApi)' == 'true'">$(LinkNativeDependsOn);PrepareDotNetJsApiForLinking</LinkNativeDependsOn>
<LinkNativeDependsOn>$(LinkNativeDependsOn);PrepareDotNetJsApiForLinking</LinkNativeDependsOn>
<NativeBinary>$(NativeOutputPath)dotnet.native.js</NativeBinary>
<IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints><!-- For JSExports -->
</PropertyGroup>
<ItemGroup>
<EmccExportedFunction Include="_free" />
Expand Down Expand Up @@ -52,7 +53,7 @@
<_DotNetJsLinkerFlag Include="-Wl,--export,__main_argc_argv" /><!-- Export main, a temporal solution until we have a way to export async main -->
<_DotNetJsLinkerFlag Include="-s EXPORT_ES6=1" /><!-- Produce ES6 module as expented by hosting API -->
<_DotNetJsLinkerFlag Include="-s MODULARIZE=1" /><!-- Produce ES6 module as expented by hosting API -->
<_DotNetJsLinkerFlag Include="-s INVOKE_RUN=0" /><!-- Don't automatically run main, it is called explicitly by ser -->
<_DotNetJsLinkerFlag Include="-s INVOKE_RUN=0" /><!-- Don't automatically run main, it is called explicitly by user -->
<_DotNetJsLinkerFlag Include="-s EXPORT_NAME=&quot;'createDotnetRuntime'&quot;" /><!-- Export emscripten factory as 'createDotnetRuntime', as expented by hosting API -->
<_DotNetJsLinkerFlag Include="-s ENVIRONMENT=&quot;'web,webview,worker,node,shell'&quot;" /><!-- Add support for all enviroments (default list is smaller) -->
<_DotNetJsLinkerFlag Condition="'$(EmccEnvironment)' != ''" Include="-s ENVIRONMENT=&quot;$(EmccEnvironment)&quot;" />
Expand All @@ -72,18 +73,26 @@
<!-- Exported functions required by user or runtime API -->
<_DotNetJsLinkerFlag Include="-s DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$(_EmccExportedLibraryFunction)" Condition="'$(_EmccExportedLibraryFunction)' != ''" />
<_DotNetJsLinkerFlag Include="-s EXPORTED_RUNTIME_METHODS=$(_EmccExportedRuntimeMethods)" />
<_DotNetJsLinkerFlag Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" />
<_DotNetJsLinkerFlag Include="-s EXPORTED_FUNCTIONS=$(_EmccExportedFunctions)" Condition="'$(ExportsFile)' == ''" /><!-- Multiple args in the same rsp file is not support, add functions to app exports -->
maraf marked this conversation as resolved.
Show resolved Hide resolved
<_DotNetJsLinkerFlag Include="$(EmccExtraLDFlags)" />
</ItemGroup>
<ItemGroup>
<CustomLinkerArg Include="@(_DotNetJsLinkerFlag)" />
</ItemGroup>
<ReadLinesFromFile File="$(ExportsFile)" Condition="'$(ExportsFile)' != ''">
<Output TaskParameter="Lines" ItemName="_ExistingExports" />
</ReadLinesFromFile>
<ItemGroup Condition="'$(ExportsFile)' != ''">
<_ExportsToAddToExportsFile Include="@(EmccExportedFunction)" />
<_ExportsToAddToExportsFile Remove="@(_ExistingExports)" />
</ItemGroup>
<WriteLinesToFile File="$(ExportsFile)" Lines="@(_ExportsToAddToExportsFile)" Overwrite="false" Encoding="utf-8" Condition="'@(_ExportsToAddToExportsFile->Count())' != '0'" />
</Target>
<Target Name="CopyDotnetJsAfterLinking" AfterTargets="LinkNativeLlvm">
<ItemGroup>
<_FilesToCopyToNative Include="$(IlcFrameworkNativePath)\dotnet*.js" />
<_FilesToCopyToNative Include="$(IlcFrameworkNativePath)\dotnet*.map" Condition="'$(WasmEmitSourceMap)' == 'true'" />
<_FilesToCopyToNative Include="@(WasmExtraFilesToDeploy)" /><!-- Use defined extra files to be included in the build output -->
<_FilesToCopyToNative Include="@(WasmExtraFilesToDeploy)" /><!-- User defined extra files to be included in the build output -->
</ItemGroup>
<Copy SourceFiles="@(_FilesToCopyToNative)" DestinationFolder="$(NativeOutputPath)" />
</Target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ The .NET Foundation licenses this file to you under the MIT license.
<EnableAotAnalyzer Condition="'$(EnableAotAnalyzer)' == ''">false</EnableAotAnalyzer>
</PropertyGroup>

<PropertyGroup>
<!-- Keep these before DotNetJsApi.targets -->
<LinkNativeDependsOn>IlcCompile</LinkNativeDependsOn>
<LinkNativeDependsOn Condition="$(NativeCodeGen) == 'llvm'">CompileWasmObjects</LinkNativeDependsOn>

<LinkNativeDependsOnSingleOrLlvm>LinkNativeSingle</LinkNativeDependsOnSingleOrLlvm>
<LinkNativeDependsOnSingleOrLlvm Condition="$(NativeCodeGen) == 'llvm'">LinkNativeLlvm</LinkNativeDependsOnSingleOrLlvm>
</PropertyGroup>

<Import Project="$(MSBuildThisFileDirectory)DotNetJsApi.targets" Condition="'$(_targetArchitecture)' == 'wasm' and '$(DotNetJsApi)' == 'true'" />

<PropertyGroup>
<NativeObjectExt Condition="'$(_targetOS)' == 'win'">.obj</NativeObjectExt>
<NativeObjectExt Condition="'$(_targetOS)' != 'win'">.o</NativeObjectExt>
Expand Down Expand Up @@ -87,18 +98,12 @@ The .NET Foundation licenses this file to you under the MIT license.
<ExportsFileExt Condition="'$(_targetOS)' != 'win'">.exports</ExportsFileExt>

<NativeObject>$(NativeIntermediateOutputPath)$(TargetName)$(NativeObjectExt)</NativeObject>
<NativeBinary>$(NativeOutputPath)$(TargetName)$(NativeBinaryExt)</NativeBinary>
<NativeBinary Condition="'$(NativeBinary)' == ''">$(NativeOutputPath)$(TargetName)$(NativeBinaryExt)</NativeBinary>
<IlcExportUnmanagedEntrypoints Condition="'$(IlcExportUnmanagedEntrypoints)' == '' and '$(NativeLib)' == 'Shared'">true</IlcExportUnmanagedEntrypoints>
<ExportsFile Condition="$(IlcExportUnmanagedEntrypoints) == 'true' and $(ExportsFile) == ''">$(NativeIntermediateOutputPath)$(TargetName)$(ExportsFileExt)</ExportsFile>

<IlcCompileOutput>$(NativeObject)</IlcCompileOutput>

<LinkNativeDependsOn>IlcCompile</LinkNativeDependsOn>
<LinkNativeDependsOn Condition="$(NativeCodeGen) == 'llvm'">CompileWasmObjects</LinkNativeDependsOn>

<LinkNativeDependsOnSingleOrLlvm>LinkNativeSingle</LinkNativeDependsOnSingleOrLlvm>
<LinkNativeDependsOnSingleOrLlvm Condition="$(NativeCodeGen) == 'llvm'">LinkNativeLlvm</LinkNativeDependsOnSingleOrLlvm>

<FrameworkLibPath Condition="'$(FrameworkLibPath)' == ''">$(NativeOutputPath)</FrameworkLibPath>
<FrameworkObjPath Condition="'$(FrameworkObjPath)' == ''">$(NativeIntermediateOutputPath)</FrameworkObjPath>

Expand Down Expand Up @@ -492,8 +497,6 @@ The .NET Foundation licenses this file to you under the MIT license.
strip -no_code_signature_warning $(_StripFlag) &quot;$(NativeBinary)&quot;" />
</Target>

<Import Project="$(MSBuildThisFileDirectory)DotNetJsApi.targets" Condition="'$(_targetArchitecture)' == 'wasm' and '$(DotNetJsApi)' == 'true'" />

<!-- NativeAOT-LLVM: separate target to reduce conflicts -->
<Target Name="LinkNativeLlvm"
Inputs="@(NativeObjects);@(NativeLibrary)"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;

internal static partial class Interop
{
internal static unsafe partial class Runtime
{
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void ReleaseCSOwnedObject(IntPtr jsHandle);
[LibraryImport("*", EntryPoint = "mono_wasm_bind_js_function", StringMarshalling = StringMarshalling.Utf16)]
public static unsafe partial void BindJSFunction(string function_name, int function_name_length, string module_name, int module_name_length, void* signature, out IntPtr bound_function_js_handle, out int is_exception);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void InvokeJSFunction(IntPtr bound_function_js_handle, void* data);
[LibraryImport("*", EntryPoint = "mono_wasm_invoke_import", StringMarshalling = StringMarshalling.Utf16)]
public static unsafe partial void InvokeImport(IntPtr fn_handle, void* data);
[LibraryImport("*", EntryPoint = "mono_wasm_bind_cs_function", StringMarshalling = StringMarshalling.Utf16)]
public static unsafe partial void BindCSFunction(string fully_qualified_name, int fully_qualified_name_length, int signature_hash, void* signature, out int is_exception);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void ResolveOrRejectPromise(void* data);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern IntPtr RegisterGCRoot(IntPtr start, int bytesSize, IntPtr name);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void DeregisterGCRoot(IntPtr handle);

public static unsafe void BindJSFunction(string function_name, string module_name, void* signature, out IntPtr bound_function_js_handle, out int is_exception, out object result)
{
BindJSFunction(function_name, function_name.Length, module_name, module_name.Length, signature, out bound_function_js_handle, out is_exception);
if (is_exception != 0)
result = "Runtime.BindJSFunction failed"; // TODO-LLVM-JSInterop: Marshal exception message
else
result = "";
}

public static unsafe void BindCSFunction(in string fully_qualified_name, int signature_hash, void* signature, out int is_exception, out object result)
{
BindCSFunction(fully_qualified_name, fully_qualified_name.Length, signature_hash, signature, out is_exception);
if (is_exception != 0)
result = "Runtime.BindCSFunction failed"; // TODO-LLVM-JSInterop: Marshal exception message
else
result = "";
}

#region Legacy

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void InvokeJSWithArgsRef(IntPtr jsHandle, in string method, in object?[] parms, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void GetObjectPropertyRef(IntPtr jsHandle, in string propertyName, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void SetObjectPropertyRef(IntPtr jsHandle, in string propertyName, in object? value, bool createIfNotExists, bool hasOwnProperty, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void GetByIndexRef(IntPtr jsHandle, int index, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void SetByIndexRef(IntPtr jsHandle, int index, in object? value, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void GetGlobalObjectRef(in string? globalName, out int exceptionalResult, out object result);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void TypedArrayToArrayRef(IntPtr jsHandle, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void CreateCSOwnedObjectRef(in string className, in object[] parms, out int exceptionalResult, out object result);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern void TypedArrayFromRef(int arrayPtr, int begin, int end, int bytesPerElement, int type, out int exceptionalResult, out object result);

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'browser'">
<Compile Include="$(CommonPath)Interop\Browser\Interop.Runtime.cs" Link="System\Runtime\InteropServices\JavaScript\Interop\Interop.Runtime.cs" />
<!-- TODO LLVM: This is not upstreamable. The proper way to do this would be to come up with a scheme that works for all runtime flavors and/or move any runtime specific parts into CoreLib. -->
<Compile Include="$(CommonPath)Interop\Browser\Interop.Runtime.cs" Link="System\Runtime\InteropServices\JavaScript\Interop\Interop.Runtime.cs" Condition="'$(RuntimeFlavor)' != 'CoreCLR'" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libraries build system is not setup to build different binaries per runtime flavor. We have all libraries (except CoreLib) 100% shared between all runtimes flavors.

This change is ok as a quick hack for runtimelab, but it would not be upstreamable. Add a comment about it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The proper way to do this would be to come up with a scheme that works for all runtime flavors and/or move any runtime specific parts into CoreLib.)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect this can be solved similar to Linq.Expressions, with an internal feature switch (always off for Mono, always on for NativeAOT).

<Compile Include="$(CommonPath)Interop\Browser\Interop.Runtime.NativeAOT.cs" Link="System\Runtime\InteropServices\JavaScript\Interop\Interop.Runtime.NativeAOT.cs" Condition="'$(RuntimeFlavor)' == 'CoreCLR'" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptImports.Generated.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs" />
<Compile Include="System\Runtime\InteropServices\JavaScript\Interop\JavaScriptImports.cs" />
Expand Down
Loading