Skip to content

Commit aceb4ce

Browse files
authored
[wasm] Implement initial support for WasmImportLinkage to the AppBuilders (#94615)
* Implement initial support for WasmImportLinkage to the AppBuilders * Mangle WasmLinkage imports by namespace and handle special modules * Don't mangle the symbolic entry point * Add very basic support for wasm exports using UnmanagedCallersOnly * Escape potential escape characters in string literals * Make the C codegen more readable * Add temporary sample for testing * Simplify workaround for UnmanagedCallersOnly * Remove weak attribute that slipped in * Rename some of the helpers * Use multiline interpolation * Improve output formatting and comments * review feedback
1 parent 52670e8 commit aceb4ce

File tree

6 files changed

+226
-141
lines changed

6 files changed

+226
-141
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
TOP=../../../../..
2+
3+
include ../wasi.mk
4+
5+
ifneq ($(AOT),)
6+
override MSBUILD_ARGS+=/p:RunAOTCompilation=true
7+
endif
8+
9+
ifneq ($(V),)
10+
DOTNET_MONO_LOG_LEVEL=--setenv=MONO_LOG_LEVEL=debug
11+
endif
12+
13+
PROJECT_NAME=Wasi.Console.Sample.csproj
14+
15+
run: run-console
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
public unsafe class Test
8+
{
9+
[UnmanagedCallersOnly(EntryPoint = "ManagedFunc")]
10+
public static int MyExport(int number)
11+
{
12+
// called from MyImport aka UnmanagedFunc
13+
Console.WriteLine($"MyExport({number}) -> 42");
14+
return 42;
15+
}
16+
17+
[DllImport("*", EntryPoint = "UnmanagedFunc")]
18+
public static extern void MyImport(); // calls ManagedFunc aka MyExport
19+
20+
public unsafe static int Main(string[] args)
21+
{
22+
Console.WriteLine($"main: {args.Length}");
23+
// workaround to force the interpreter to initialize wasm_native_to_interp_ftndesc for MyExport
24+
if (args.Length > 10000) {
25+
((IntPtr)(delegate* unmanaged<int,int>)&MyExport).ToString();
26+
}
27+
28+
MyImport();
29+
return 0;
30+
}
31+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
4+
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
5+
6+
<TargetOs>wasi</TargetOs>
7+
<WasmBuildNative>true</WasmBuildNative>
8+
<WasmNativeStrip>false</WasmNativeStrip>
9+
<IsBrowserWasmProject>false</IsBrowserWasmProject>
10+
<WasmSingleFileBundle>true</WasmSingleFileBundle>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<NativeFileReference Include="local.c" />
15+
</ItemGroup>
16+
<Target Name="RunSample" DependsOnTargets="RunSampleWithWasmtime" />
17+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <stdio.h>
2+
3+
int ManagedFunc(int number);
4+
5+
void UnmanagedFunc()
6+
{
7+
int ret = 0;
8+
printf("UnmanagedFunc calling ManagedFunc\n");
9+
ret = ManagedFunc(123);
10+
printf("ManagedFunc returned %d\n", ret);
11+
}

src/tasks/WasmAppBuilder/PInvokeCollector.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
internal sealed class PInvoke : IEquatable<PInvoke>
1616
#pragma warning restore CA1067
1717
{
18-
public PInvoke(string entryPoint, string module, MethodInfo method)
18+
public PInvoke(string entryPoint, string module, MethodInfo method, bool wasmLinkage)
1919
{
2020
EntryPoint = entryPoint;
2121
Module = module;
2222
Method = method;
23+
WasmLinkage = wasmLinkage;
2324
}
2425

2526
public string EntryPoint;
2627
public string Module;
2728
public MethodInfo Method;
2829
public bool Skip;
30+
public bool WasmLinkage;
2931

3032
public bool Equals(PInvoke? other)
3133
=> other != null &&
@@ -100,9 +102,10 @@ void CollectPInvokesForMethod(MethodInfo method)
100102
if ((method.Attributes & MethodAttributes.PinvokeImpl) != 0)
101103
{
102104
var dllimport = method.CustomAttributes.First(attr => attr.AttributeType.Name == "DllImportAttribute");
105+
var wasmLinkage = method.CustomAttributes.Any(attr => attr.AttributeType.Name == "WasmImportLinkageAttribute");
103106
var module = (string)dllimport.ConstructorArguments[0].Value!;
104107
var entrypoint = (string)dllimport.NamedArguments.First(arg => arg.MemberName == "EntryPoint").TypedValue.Value!;
105-
pinvokes.Add(new PInvoke(entrypoint, module, method));
108+
pinvokes.Add(new PInvoke(entrypoint, module, method, wasmLinkage));
106109

107110
string? signature = SignatureMapper.MethodToSignature(method);
108111
if (signature == null)
@@ -241,8 +244,23 @@ internal sealed class PInvokeCallback
241244
public PInvokeCallback(MethodInfo method)
242245
{
243246
Method = method;
247+
foreach (var attr in method.CustomAttributes)
248+
{
249+
if (attr.AttributeType.Name == "UnmanagedCallersOnlyAttribute")
250+
{
251+
foreach(var arg in attr.NamedArguments)
252+
{
253+
if (arg.MemberName == "EntryPoint")
254+
{
255+
EntryPoint = arg.TypedValue.Value!.ToString();
256+
return;
257+
}
258+
}
259+
}
260+
}
244261
}
245262

263+
public string? EntryPoint;
246264
public MethodInfo Method;
247265
public string? EntryName;
248266
}

0 commit comments

Comments
 (0)