diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs index fddc4ba4e7d836..2b7931880868cd 100644 --- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs @@ -24,7 +24,7 @@ public class PInvokeTableGenerator : Task [Output] public string FileWrites { get; private set; } = string.Empty; - private static char[] s_charsToReplace = new[] { '.', '-', }; + private static char[] s_charsToReplace = new[] { '.', '-', ',', '|', '<', '>' }; public override bool Execute() { @@ -157,7 +157,7 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules foreach (var module in modules.Keys) { - string symbol = ModuleNameToId(module) + "_imports"; + string symbol = FixupSymbolName(module) + "_imports"; w.WriteLine("static PinvokeImport " + symbol + " [] = {"); var assemblies_pinvokes = pinvokes. @@ -176,7 +176,7 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules w.Write("static void *pinvoke_tables[] = { "); foreach (var module in modules.Keys) { - string symbol = ModuleNameToId(module) + "_imports"; + string symbol = FixupSymbolName(module) + "_imports"; w.Write(symbol + ","); } w.WriteLine("};"); @@ -187,18 +187,6 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules } w.WriteLine("};"); - static string ModuleNameToId(string name) - { - if (name.IndexOfAny(s_charsToReplace) < 0) - return name; - - string fixedName = name; - foreach (char c in s_charsToReplace) - fixedName = fixedName.Replace(c, '_'); - - return fixedName; - } - static bool ShouldTreatAsVariadic(PInvoke[] candidates) { if (candidates.Length < 2) @@ -216,6 +204,26 @@ static bool ShouldTreatAsVariadic(PInvoke[] candidates) } } + private static string FixupSymbolName(string name) + { + if (name.IndexOfAny(s_charsToReplace) < 0) + return name; + + string fixedName = name; + foreach (char c in s_charsToReplace) + fixedName = fixedName.Replace(c, '_'); + + return fixedName; + } + + private static string SymbolNameForMethod(MethodInfo method) + { + string module_symbol = method.DeclaringType!.Module!.Assembly!.GetName()!.Name!; + string class_name = method.DeclaringType.Name; + string method_name = method.Name; + return FixupSymbolName($"{module_symbol}_{class_name}_{method_name}"); + } + private string MapType (Type t) { string name = t.Name; @@ -343,15 +351,12 @@ private void EmitNativeToInterp(StreamWriter w, List callbacks) bool is_void = method.ReturnType.Name == "Void"; - string module_symbol = method.DeclaringType!.Module!.Assembly!.GetName()!.Name!.Replace(".", "_"); - uint token = (uint)method.MetadataToken; - string class_name = method.DeclaringType.Name; - string method_name = method.Name; - string entry_name = $"wasm_native_to_interp_{module_symbol}_{class_name}_{method_name}"; + string entry_name = $"wasm_native_to_interp_{SymbolNameForMethod(method)}"; if (callbackNames.Contains (entry_name)) { - Error($"Two callbacks with the same name '{method_name}' are not supported."); + Error($"Two callbacks with the same name '{entry_name}' are not supported."); } + callbackNames.Add (entry_name); cb.EntryName = entry_name; sb.Append(MapType(method.ReturnType)); @@ -395,7 +400,7 @@ private void EmitNativeToInterp(StreamWriter w, List callbacks) // Array of function pointers w.Write ("static void *wasm_native_to_interp_funcs[] = { "); foreach (var cb in callbacks) { - w.Write (cb.EntryName + ","); + w.Write ($"\t{cb.EntryName},{Environment.NewLine}"); } w.WriteLine ("};"); @@ -403,13 +408,8 @@ private void EmitNativeToInterp(StreamWriter w, List callbacks) // The key is a string of the form _ // FIXME: Use a better encoding w.Write ("static const char *wasm_native_to_interp_map[] = { "); - foreach (var cb in callbacks) { - var method = cb.Method; - string module_symbol = method.DeclaringType!.Module!.Assembly!.GetName()!.Name!.Replace(".", "_"); - string class_name = method.DeclaringType.Name; - string method_name = method.Name; - w.WriteLine ($"\"{module_symbol}_{class_name}_{method_name}\","); - } + foreach (var cb in callbacks) + w.WriteLine ($"\"{SymbolNameForMethod(cb.Method)}\","); w.WriteLine ("};"); } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b6fab5c6c434b7..7ad0725c91b88f 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs @@ -253,6 +253,7 @@ protected static void InitProjectDir(string dir) {s_targetFramework} Exe + true true runtime-test.js ##EXTRA_PROPERTIES## diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs index eb306286ba5e0d..08a02be53dfa3d 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs @@ -17,14 +17,14 @@ public NativeLibraryTests(ITestOutputHelper output, SharedBuildPerTestClassFixtu { } - [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + [Theory] [BuildAndRun(aot: false)] [BuildAndRun(aot: true)] public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string id) { string projectName = $"AppUsingNativeLib-a"; buildArgs = buildArgs with { ProjectName = projectName }; - buildArgs = ExpandBuildArgs(buildArgs, extraItems: ""); + buildArgs = ExpandBuildArgs(buildArgs, extraItems: ""); if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? _)) { @@ -33,7 +33,6 @@ public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string Directory.Delete(_projectDir, recursive: true); Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "AppUsingNativeLib"), _projectDir); - File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "native-lib.o"), Path.Combine(_projectDir, "native-lib.o")); } BuildProject(buildArgs, @@ -45,6 +44,7 @@ public void ProjectWithNativeReference(BuildArgs buildArgs, RunHost host, string host: host, id: id); Assert.Contains("print_line: 100", output); + Assert.Contains("total in helper: 253", output); Assert.Contains("from pinvoke: 142", output); } diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs index 5134392c9d8ca3..f250459d06248b 100644 --- a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/Program.cs @@ -12,10 +12,41 @@ public class Test public static int Main(string[] args) { Console.WriteLine ($"from pinvoke: {SimpleConsole.Test.print_line(100)}"); + new Helper(); return 0; } + class Helper { + public Helper () { + int t = 0; + unsafe { + delegate *unmanaged fn = &nested_helper; + t += native_intint_callback_acceptor ((IntPtr)fn, 1); + + fn = &member_helper; + + t += native_intint_callback_acceptor ((IntPtr)fn, 2); + } + + Console.WriteLine ($"total in helper: {t}"); + + // local function inside a nested class ctor. Mangled name will be something like + // int32 SimpleConsole.Test/Helper::'<.ctor>g__Helper|1_0' + [UnmanagedCallersOnly] + static int nested_helper (int j) => j + 20; + + } + } + + [UnmanagedCallersOnly] + private static int member_helper(int x) => x + 30; + + [DllImport("native-lib")] public static extern int print_line(int x); + + // FIXME: support function pointers in pinvoke arguments + [DllImport("native-lib")] + public static unsafe extern int native_intint_callback_acceptor(/*delegate *unmanaged*/ IntPtr fn, int i); } } diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp index 329a593279fe2d..d0f1225c00cccc 100644 --- a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.cpp @@ -9,3 +9,8 @@ int print_line(int x) printf("print_line: %d\n", x); return 42 + x; } + +int native_intint_callback_acceptor(ManagedIntIntCallback fn, int i) +{ + return fn(i + 100); +} diff --git a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h index 826637b3a2d812..8d55b398665314 100644 --- a/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h +++ b/src/tests/BuildWasmApps/testassets/AppUsingNativeLib/native-lib.h @@ -10,6 +10,10 @@ extern "C" { int print_line(int x); +typedef int (*ManagedIntIntCallback)(int x); + +int native_intint_callback_acceptor(ManagedIntIntCallback fn, int i); + #ifdef __cplusplus } #endif diff --git a/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o b/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o deleted file mode 100644 index 10ccf42c5ff239..00000000000000 Binary files a/src/tests/BuildWasmApps/testassets/native-libs/native-lib.o and /dev/null differ