From 96cc3c6a336e34d3dd9927469b16e8c294592f83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:57:05 +0000 Subject: [PATCH 1/2] Initial plan From b594430b3673ba34c2465463def9d5cd045c0427 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:04:28 +0000 Subject: [PATCH 2/2] Fix CoreCLR PInvokeCollector versioned OS platform matching, add regression test Co-authored-by: akoeplinger <1376924+akoeplinger@users.noreply.github.com> --- .../PInvokeTableGeneratorTests.cs | 37 +++++++++++++++++++ .../PInvoke/VersionedOSPlatform.cs | 26 +++++++++++++ .../native-libs/versioned-osplatform.c | 4 ++ .../coreclr/PInvokeCollector.cs | 20 +++++++++- 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/mono/wasm/testassets/EntryPoints/PInvoke/VersionedOSPlatform.cs create mode 100644 src/mono/wasm/testassets/native-libs/versioned-osplatform.c diff --git a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs index c64eefc6650922..0c7fe35ecdf6e3 100644 --- a/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs @@ -442,6 +442,43 @@ public void UnsupportedOSPlatformPInvokeIsSkipped(Configuration config, bool aot Assert.DoesNotContain("WASM0062", output); } + [Theory] + [BuildAndRun(aot: false)] + [TestCategory("native-mono")] + public async Task VersionedOSPlatformPInvokeIsIncluded(Configuration config, bool aot) + { + // Regression coverage for https://github.com/dotnet/runtime/issues/132297: + // a versioned platform attribute like [SupportedOSPlatform("browser1.0")] must still + // be treated as matching TargetOS=browser, so the pinvoke must be kept (not silently + // filtered out), while a versioned attribute for a different OS (e.g. "windows1.0") + // must still be filtered out. + string extraItems = @""; + ProjectInfo info = CopyTestAsset(config, aot, TestAsset.WasmBasicTestApp, "versioned_osplatform_pinvoke", + extraItems: extraItems, extraProperties: "true"); + ReplaceFile(Path.Combine("Common", "Program.cs"), Path.Combine(BuildEnvironment.TestAssetsPath, "EntryPoints", "PInvoke", "VersionedOSPlatform.cs")); + File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "versioned-osplatform.c"), Path.Combine(_projectDir, "versioned-osplatform.c")); + + (_, string output) = BuildProject(info, config, new BuildOptions(AssertAppBundle: false, AOT: aot), isNativeBuild: true); + Assert.DoesNotContain("WASM0001", output); + + string objDir = Path.Combine(_projectDir, "obj", config.ToString(), DefaultTargetFramework, "wasm", "for-build"); + string pinvokeTableFileName = IsCoreClrRuntime ? "callhelpers-pinvoke.cpp" : "pinvoke-table.h"; + string pinvokeTable = File.ReadAllText(Path.Combine(objDir, pinvokeTableFileName)); + + string includedPInvokeTableEntry = IsCoreClrRuntime + ? "DllImportEntry(versioned_browser_add)" + : "\"versioned_browser_add\", versioned_browser_add"; + Assert.Contains(includedPInvokeTableEntry, pinvokeTable); + + string excludedPInvokeTableEntry = IsCoreClrRuntime + ? "DllImportEntry(versioned_windows_add)" + : "\"versioned_windows_add\", versioned_windows_add"; + Assert.DoesNotContain(excludedPInvokeTableEntry, pinvokeTable); + + RunResult result = await RunForBuildWithDotnetRun(new BrowserRunOptions(config, TestScenario: "DotnetRun", ExpectedExitCode: 42)); + Assert.Contains("sum: 42", result.TestOutput); + } + [Theory] [BuildAndRun(aot: true, config: Configuration.Release)] [TestCategory("native-mono")] diff --git a/src/mono/wasm/testassets/EntryPoints/PInvoke/VersionedOSPlatform.cs b/src/mono/wasm/testassets/EntryPoints/PInvoke/VersionedOSPlatform.cs new file mode 100644 index 00000000000000..a339d9ba8a7121 --- /dev/null +++ b/src/mono/wasm/testassets/EntryPoints/PInvoke/VersionedOSPlatform.cs @@ -0,0 +1,26 @@ +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +Console.WriteLine($"TestOutput -> sum: {VersionedBrowserInterop.versioned_browser_add(19, 23)}"); +return 42; + +// Regression coverage for https://github.com/dotnet/runtime/issues/132297: +// a versioned platform attribute like [SupportedOSPlatform("browser1.0")] must still match +// TargetOS=browser, so this pinvoke must be included in the generated pinvoke table and +// callable at runtime. +[SupportedOSPlatform("browser1.0")] +internal static class VersionedBrowserInterop +{ + [DllImport("versioned-osplatform")] + public static extern int versioned_browser_add(int a, int b); +} + +// A versioned platform attribute for a different OS must not match TargetOS=browser, so this +// pinvoke must be skipped and must not appear in the generated pinvoke table. +[SupportedOSPlatform("windows1.0")] +internal static class VersionedWindowsInterop +{ + [DllImport("versioned-osplatform")] + public static extern int versioned_windows_add(int a, int b); +} diff --git a/src/mono/wasm/testassets/native-libs/versioned-osplatform.c b/src/mono/wasm/testassets/native-libs/versioned-osplatform.c new file mode 100644 index 00000000000000..1868b6fc2456c8 --- /dev/null +++ b/src/mono/wasm/testassets/native-libs/versioned-osplatform.c @@ -0,0 +1,4 @@ +int versioned_browser_add(int a, int b) +{ + return a + b; +} diff --git a/src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs b/src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs index 150a2e50361454..ba59810c1b54c8 100644 --- a/src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs +++ b/src/tasks/WasmAppBuilder/coreclr/PInvokeCollector.cs @@ -291,7 +291,7 @@ private PlatformSupport EvaluatePlatformAttributes(IList at { if (cattr.AttributeType.FullName == "System.Runtime.Versioning.UnsupportedOSPlatformAttribute" && cattr.ConstructorArguments.Count > 0 && - cattr.ConstructorArguments[0].Value?.ToString() == _targetOS) + MatchesTargetOS(cattr.ConstructorArguments[0].Value?.ToString())) { return PlatformSupport.Unsupported; } @@ -299,7 +299,7 @@ private PlatformSupport EvaluatePlatformAttributes(IList at cattr.ConstructorArguments.Count > 0) { hasSupportedOSPlatform = true; - if (cattr.ConstructorArguments[0].Value?.ToString() == _targetOS) + if (MatchesTargetOS(cattr.ConstructorArguments[0].Value?.ToString())) hasSupportedTarget = true; } } @@ -314,6 +314,22 @@ private PlatformSupport EvaluatePlatformAttributes(IList at return PlatformSupport.Unknown; } + + private bool MatchesTargetOS(string? platformName) + { + if (string.Equals(platformName, _targetOS, StringComparison.OrdinalIgnoreCase)) + return true; + + if (platformName?.StartsWith(_targetOS, StringComparison.OrdinalIgnoreCase) != true) + return false; + +#if NETFRAMEWORK + string version = platformName.Substring(_targetOS.Length); +#else + ReadOnlySpan version = platformName.AsSpan(_targetOS.Length); +#endif + return Version.TryParse(version, out _); + } } internal sealed class PInvokeCallbackComparer : IComparer