diff --git a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
index ae143cac6f37e3..dab10296c1c529 100644
--- a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
+++ b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
@@ -680,7 +680,7 @@ public enum LoweringFlags
public static WasmSignature GetSignature(MethodSignature signature, LoweringFlags flags)
{
- if (!flags.HasFlag(LoweringFlags.IsUnmanagedCallersOnly) && signature.Flags.HasFlag(MethodSignatureFlags.UnmanagedCallingConvention))
+ if ((signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) is not 0 and not MethodSignatureFlags.CallingConventionVarargs)
{
flags |= LoweringFlags.IsUnmanagedCallersOnly;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
index c0a77643aa0ff2..b11ea54aef5090 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs
@@ -270,6 +270,22 @@ private static MethodDesc GetTaskReturningCoreLibMethod(ReadyToRunCompilerContex
throw new InvalidOperationException($"No single-argument {type.Name.ToString()}.{methodName} overload in System.Private.CoreLib");
}
+ [Theory]
+ [InlineData(MethodSignatureFlags.None, "iip")]
+ [InlineData(MethodSignatureFlags.CallingConventionVarargs, "iip")]
+ [InlineData(MethodSignatureFlags.UnmanagedCallingConventionCdecl, "ii")]
+ [InlineData(MethodSignatureFlags.UnmanagedCallingConventionStdCall, "ii")]
+ [InlineData(MethodSignatureFlags.UnmanagedCallingConventionThisCall, "ii")]
+ [InlineData(MethodSignatureFlags.UnmanagedCallingConvention, "ii")]
+ public void SignatureCallingConventionSelectsLowering(MethodSignatureFlags callingConvention, string expectedSignature)
+ {
+ ReadyToRunCompilerContext context = CreateWasmContext();
+ TypeDesc int32Type = context.GetWellKnownType(WellKnownType.Int32);
+ MethodSignature signature = new MethodSignature(callingConvention | MethodSignatureFlags.Static, 0, int32Type, [int32Type]);
+
+ Assert.Equal(expectedSignature, WasmLowering.GetSignature(signature, WasmLowering.LoweringFlags.None).SignatureString);
+ }
+
[Theory]
[InlineData(MethodSignatureFlags.None)]
[InlineData(MethodSignatureFlags.CallingConventionVarargs)]
@@ -887,28 +903,6 @@ public static class Exports
AssertPortableCallHelpersGeneratorRejects(source, $"has unsupported signature token '{expectedToken}'");
}
- [Fact]
- public void PortableCallHelpersGeneratorRejectsHiddenReturnBufferCallbacks()
- {
- string source = """
- using System.Runtime.InteropServices;
-
- public struct Pair
- {
- public int First;
- public int Second;
- }
-
- public static class Exports
- {
- [UnmanagedCallersOnly(EntryPoint = "callback")]
- public static Pair Handle(int value) => default;
- }
- """;
-
- AssertPortableCallHelpersGeneratorRejects(source, "uses a hidden return buffer");
- }
-
private void AssertPortableCallHelpersGeneratorRejects(string source, string expectedError)
{
string workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
@@ -1028,6 +1022,153 @@ public static class Exports
}
}
+ ///
+ /// The interpreter looks up a thunk for each unmanaged calli by signature, so the generator
+ /// has to emit one for every function pointer signature an app calls through, whatever the
+ /// calling convention spelling.
+ ///
+ [Fact]
+ public void PortableCallHelpersGeneratorEmitsThunksForUnmanagedCalliSites()
+ {
+ string source = """
+ public struct FloatPair
+ {
+ public float X;
+ public float Y;
+ }
+
+ public static unsafe class Calls
+ {
+ public static FloatPair MemberFunction(delegate* unmanaged[Cdecl, MemberFunction] fn)
+ => fn(null, 1);
+
+ public static int ThisCall(delegate* unmanaged[Thiscall] fn)
+ => fn(null, 1, 2, 3);
+
+ public static double Managed(delegate* fn)
+ => fn(1, 2, 3, 4, 5, 6);
+
+ public static T Generic(delegate* unmanaged fn)
+ => fn();
+ }
+ """;
+
+ string workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(workingDirectory);
+
+ try
+ {
+ string inputAssembly = CompileCallbackAssembly(source, Path.Combine(workingDirectory, "Calls.dll"), allowUnsafe: true);
+ string outputDirectory = Path.Combine(workingDirectory, "generated");
+
+ var options = new PortableCallHelpersGeneratorOptions
+ {
+ OutputDirectory = outputDirectory,
+ TargetOS = "browser",
+ PInvokeModules = new[] { "libSystem.Native" },
+ };
+
+ var log = new StringWriter();
+ int exitCode = PortableCallHelpersGenerator.Run(
+ CreateWasmContext(inputAssembly), options, new Logger(log, isVerbose: true));
+
+ Assert.True(exitCode == 0, log.ToString());
+
+ string thunks = File.ReadAllText(Path.Combine(outputDirectory, "callhelpers-interp-to-managed.cpp"));
+ Assert.Contains("\"MS8il\"", thunks);
+ Assert.Contains("\"Miiddd\"", thunks);
+ Assert.DoesNotContain("\"Mdiiiiii\"", thunks);
+ }
+ finally
+ {
+ try
+ {
+ Directory.Delete(workingDirectory, recursive: true);
+ }
+ catch (Exception e) when (e is IOException or UnauthorizedAccessException)
+ {
+ }
+ }
+ }
+
+ ///
+ /// Native code calls a reverse thunk with the wasm C ABI, which returns a struct it cannot pass as a
+ /// single scalar through a hidden leading pointer instead of a return value. The thunk has to take
+ /// that pointer and hand it to the interpreter as the return buffer, or the indirect call from
+ /// native code traps with a signature mismatch.
+ ///
+ [Fact]
+ public void PortableCallHelpersGeneratorReturnsStructsThroughHiddenPointerInReverseThunks()
+ {
+ string source = """
+ using System.Runtime.InteropServices;
+
+ public struct FloatPair
+ {
+ public float X;
+ public float Y;
+ }
+
+ public struct Width
+ {
+ public float Value;
+ }
+
+ public static class Callbacks
+ {
+ [UnmanagedCallersOnly]
+ public static FloatPair GetPair(nint self, int unused) => default;
+
+ [UnmanagedCallersOnly]
+ public static Width GetWidth(nint self) => default;
+
+ [UnmanagedCallersOnly(EntryPoint = "export_pair")]
+ public static FloatPair ExportPair(int value) => default;
+ }
+ """;
+
+ string workingDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(workingDirectory);
+
+ try
+ {
+ string inputAssembly = CompileCallbackAssembly(source, Path.Combine(workingDirectory, "Callbacks.dll"));
+ string outputDirectory = Path.Combine(workingDirectory, "generated");
+
+ var options = new PortableCallHelpersGeneratorOptions
+ {
+ OutputDirectory = outputDirectory,
+ TargetOS = "browser",
+ PInvokeModules = new[] { "libSystem.Native" },
+ };
+
+ var log = new StringWriter();
+ int exitCode = PortableCallHelpersGenerator.Run(
+ CreateWasmContext(inputAssembly), options, new Logger(log, isVerbose: false));
+
+ Assert.True(exitCode == 0, log.ToString());
+
+ string reverse = File.ReadAllText(Path.Combine(outputDirectory, "callhelpers-reverse.cpp"));
+ Assert.Contains("static void Call_Callbacks__Callbacks_GetPair_I32_I32_RetS8(void * sret, void * arg0, int32_t arg1)", reverse);
+ Assert.Contains("static float Call_Callbacks__Callbacks_GetWidth_I32_RetF32(void * arg0)", reverse);
+ Assert.Contains("extern \"C\" void export_pair(void * sret, int32_t arg0)", reverse);
+ Assert.Contains("Call_Callbacks__Callbacks_ExportPair_I32_RetS8(sret, arg0);", reverse);
+ Assert.Contains("(int8_t*)sret, (PCODE)&Call_Callbacks__Callbacks_GetPair_I32_I32_RetS8", reverse);
+ Assert.Contains("((void(*)(void *, int32_t))r2r)(sret, arg0);", reverse);
+ Assert.DoesNotContain("RetS8(void * arg", reverse);
+ }
+ finally
+ {
+ try
+ {
+ Directory.Delete(workingDirectory, recursive: true);
+ }
+ catch (Exception e) when (e is IOException or UnauthorizedAccessException)
+ {
+ }
+ }
+ }
+
///
/// The generated wrapper only needs to reach the R2R native entrypoint for callbacks exported by
/// name: native code calls the extern "C" export (and thus the wrapper) directly. Every other
@@ -1101,13 +1242,13 @@ public static class Exports
/// Builds an input assembly for the generator to scan. It references the same CoreLib the context
/// reads, so the attributes it applies are the ones the type system will resolve.
///
- private static string CompileCallbackAssembly(string source, string outputPath)
+ private static string CompileCallbackAssembly(string source, string outputPath, bool allowUnsafe = false)
{
CSharpCompilation compilation = CSharpCompilation.Create(
Path.GetFileNameWithoutExtension(outputPath),
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(TestPaths.SystemPrivateCoreLibPath) },
- new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: allowUnsafe));
EmitResult result = compilation.Emit(outputPath);
Assert.True(result.Success,
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeCollector.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeCollector.cs
index f83b04e7a8b89c..4b619929dfb3e0 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeCollector.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeCollector.cs
@@ -6,6 +6,7 @@
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
+using Internal.IL;
using Internal.JitInterface;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
@@ -138,6 +139,7 @@ public void CollectPInvokes(List pinvokes, List ca
CollectPInvokesForMethod(method);
if (IsMethodCallback(method))
callbacks.Add(new PInvokeCallback(method));
+ CollectUnmanagedCalliSignatures(signatures, method);
}
catch (Exception ex) when (ex is not LogAsErrorException)
{
@@ -178,6 +180,80 @@ void CollectPInvokesForMethod(EcmaMethod method)
}
}
+ ///
+ /// Adds the signature of every calli through an unmanaged function pointer in the
+ /// method's body. The interpreter calls those through the same thunks as P/Invokes, and a
+ /// function pointer's signature appears nowhere else the scan looks.
+ ///
+ private void CollectUnmanagedCalliSignatures(Dictionary signatures, EcmaMethod method)
+ {
+ if (method.IsPInvoke || method.IsAbstract)
+ return;
+
+ MethodIL methodIL = EcmaMethodIL.Create(method);
+ if (methodIL is null)
+ return;
+
+ ILReader reader = new ILReader(methodIL.GetILBytes());
+ while (reader.HasNext)
+ {
+ ILOpcode opcode = reader.ReadILOpcode();
+ if (opcode != ILOpcode.calli)
+ {
+ reader.Skip(opcode);
+ continue;
+ }
+
+ if (methodIL.GetObject(reader.ReadILToken(), NotFoundBehavior.ReturnNull) is not MethodSignature calliSignature
+ || (calliSignature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) is 0 or MethodSignatureFlags.CallingConventionVarargs)
+ {
+ continue;
+ }
+
+ if (IsUnsupportedOnPlatform(method))
+ return;
+
+ // A signature over the enclosing generic parameters has no single native shape.
+ if (ContainsSignatureVariables(calliSignature))
+ {
+ log.Verbose($"Skipping generic unmanaged calli in '{method.OwningType}.{method.Name.ToString()}'");
+ continue;
+ }
+
+ string signature;
+ try
+ {
+ signature = WasmLowering.GetSignature(calliSignature, WasmLowering.LoweringFlags.None).SignatureString;
+ foreach (string token in InteropSignature.ParseSignatureTokens(signature))
+ InteropSignature.TokenToNativeType(token);
+ }
+ catch (Exception ex) when (ex is InvalidSignatureCharException or LogAsErrorException or TypeSystemException)
+ {
+ // A P/Invoke with such a signature is a build error, but a calli is only a call
+ // site that may never run. Leave it to fail at run time rather than fail the build.
+ log.Verbose($"Skipping unmanaged calli in '{method.OwningType}.{method.Name.ToString()}': {ex.Message}");
+ continue;
+ }
+
+ if (signatures.TryAdd(signature, method))
+ log.Verbose($"Adding calli signature {signature} for method '{method.OwningType}.{method.Name.ToString()}'");
+ }
+
+ static bool ContainsSignatureVariables(MethodSignature signature)
+ {
+ if (signature.ReturnType.ContainsSignatureVariables())
+ return true;
+
+ for (int i = 0; i < signature.Length; i++)
+ {
+ if (signature[i].ContainsSignatureVariables())
+ return true;
+ }
+
+ return false;
+ }
+ }
+
private void AddSignature(Dictionary signatures, MethodDesc method, WasmLowering.LoweringFlags flags, string kind)
{
string signature = InteropSignature.GetMethodSignature(method, flags);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeTableGenerator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeTableGenerator.cs
index a3a8a9cd391f69..0dcfbfdd982248 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeTableGenerator.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/PortableCallHelpers/PInvokeTableGenerator.cs
@@ -294,8 +294,18 @@ public void EmitNativeToInterp(TextWriter w, List callbacks)
? $"\n\n int64_t args[{parameterCount}] = {{ {string.Join(", ", Enumerable.Range(0, parameterCount).Select(i => CarriesBits(i) ? "0" : $"(int64_t)arg{i}"))} }};"
+ string.Concat(Enumerable.Range(0, parameterCount).Where(CarriesBits).Select(i => $"\n memcpy(&args[{i}], &arg{i}, sizeof(arg{i}));"))
: string.Empty;
- string parametersDeclaration = string.Join(", ", parameterCTypes.Select((p, i) => $"{p} arg{i}"));
- string arguments = string.Join(", ", Enumerable.Range(0, parameterCount).Select(i => $"arg{i}"));
+ // A struct the wasm C ABI returns by reference arrives as a hidden leading pointer to
+ // caller-owned storage, with no return value, so the interpreter writes straight into it.
+ bool returnsViaBuffer = IsPassedByReference(cb.ReturnType);
+ bool hasResult = !cb.IsVoid && !returnsViaBuffer;
+ string returnCType = returnsViaBuffer ? "void" : MapType(cb.ReturnType);
+ string retArg = returnsViaBuffer ? "(int8_t*)sret" : hasResult ? "(int8_t*)&result" : "nullptr";
+ string parametersDeclaration = string.Join(", ",
+ (returnsViaBuffer ? ["void * sret"] : Array.Empty())
+ .Concat(parameterCTypes.Select((p, i) => $"{p} arg{i}")));
+ string arguments = string.Join(", ",
+ (returnsViaBuffer ? ["sret"] : Array.Empty())
+ .Concat(Enumerable.Range(0, parameterCount).Select(i => $"arg{i}")));
// A partial R2R image can compile an UnmanagedCallersOnly callback to native code. That R2R
// code is the directly-callable native entrypoint (same ABI as this wrapper's parameters), so
@@ -306,10 +316,11 @@ public void EmitNativeToInterp(TextWriter w, List callbacks)
// other reverse thunk is handed out by the runtime through GetUnmanagedCallersOnlyThunk, which
// already returns the R2R native code directly and only falls back to this interpreter wrapper
// when the method has no native code -- so the dispatch would be dead code there.
- string paramTypesOnly = string.Join(", ", parameterCTypes);
- string r2rDispatch = cb.IsVoid
- ? $"((void(*)({paramTypesOnly}))r2r)({arguments});{w.NewLine} return;"
- : $"return (({MapType(cb.ReturnType)}(*)({paramTypesOnly}))r2r)({arguments});";
+ string paramTypesOnly = string.Join(", ",
+ (returnsViaBuffer ? ["void *"] : Array.Empty()).Concat(parameterCTypes));
+ string r2rDispatch = hasResult
+ ? $"return (({returnCType}(*)({paramTypesOnly}))r2r)({arguments});"
+ : $"((void(*)({paramTypesOnly}))r2r)({arguments});{w.NewLine} return;";
// Cache the resolved entrypoint in a per-callback static, published with acquire/release
// atomics: these are native entry points that can be entered concurrently, and the value is
// computed identically on every call, so the racing read/write is benign but must not tear.
@@ -336,9 +347,9 @@ public void EmitNativeToInterp(TextWriter w, List callbacks)
$$"""
- extern "C" {{MapType(cb.ReturnType)}} {{cb.EntryPoint}}({{parametersDeclaration}})
+ extern "C" {{returnCType}} {{cb.EntryPoint}}({{parametersDeclaration}})
{
- {{(cb.IsVoid ? "" : "return ")}}Call_{{cb.EntrySymbol}}({{arguments}});
+ {{(hasResult ? "return " : "")}}Call_{{cb.EntrySymbol}}({{arguments}});
}
""" : string.Empty;
w.Write(
@@ -346,16 +357,16 @@ public void EmitNativeToInterp(TextWriter w, List callbacks)
static MethodDesc* MD_{{cb.EntrySymbol}} = nullptr;{{r2rStaticDecl}}
static {{
- MapType(cb.ReturnType)}} Call_{{cb.EntrySymbol}}({{parametersDeclaration}})
+ returnCType}} Call_{{cb.EntrySymbol}}({{parametersDeclaration}})
{
// Lazy lookup of MethodDesc for the function export scenario.
if (!MD_{{cb.EntrySymbol}})
{
LookupUnmanagedCallersOnlyMethodByName("{{cb.TypeFullName}}, {{cb.AssemblyName}}", "{{cb.MethodName}}", &MD_{{cb.EntrySymbol}});
}{{r2rSection}}{{argsDeclaration}}{{
- (!cb.IsVoid ? $"{w.NewLine} {MapType(cb.ReturnType)} result;" : "")}}
- ExecuteInterpretedMethodFromUnmanaged(MD_{{cb.EntrySymbol}}, {{argsArgs}}, {{(cb.IsVoid ? "nullptr" : "(int8_t*)&result")}}, (PCODE)&Call_{{cb.EntrySymbol}});{{
- (!cb.IsVoid ? $"{w.NewLine} return result;" : "")}}
+ (hasResult ? $"{w.NewLine} {returnCType} result;" : "")}}
+ ExecuteInterpretedMethodFromUnmanaged(MD_{{cb.EntrySymbol}}, {{argsArgs}}, {{retArg}}, (PCODE)&Call_{{cb.EntrySymbol}});{{
+ (hasResult ? $"{w.NewLine} return result;" : "")}}
}{{exportFunction}}
""");
@@ -424,12 +435,6 @@ static void RejectUnsupportedCallbackSignature(PInvokeCallback cb)
$"UnmanagedCallersOnly callback '{cb.Method}' has unsupported signature token '{token}', which the generated native wrapper does not support.");
}
- if (!cb.IsVoid && IsPassedByReference(cb.ReturnType))
- {
- throw new LogAsErrorException(
- $"UnmanagedCallersOnly callback '{cb.Method}' has return type '{cb.ReturnType}' that uses a hidden return buffer, which the generated native wrapper does not support.");
- }
-
static bool IsUnsupportedToken(string token) =>
token is "V" || InteropSignature.IsMultiSlotToken(token);
}
diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp
index ab5d5c900d7e16..064c2679f9d396 100644
--- a/src/coreclr/vm/wasm/helpers.cpp
+++ b/src/coreclr/vm/wasm/helpers.cpp
@@ -1276,6 +1276,7 @@ namespace
case IMAGE_CEE_CS_CALLCONV_DEFAULT:
case IMAGE_CEE_CS_CALLCONV_C:
case IMAGE_CEE_CS_CALLCONV_STDCALL:
+ case IMAGE_CEE_CS_CALLCONV_THISCALL:
case IMAGE_CEE_CS_CALLCONV_FASTCALL:
case IMAGE_CEE_CS_CALLCONV_UNMANAGED:
break;
diff --git a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs
index 99d4ad905f22bd..038d5853e50f4d 100644
--- a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs
+++ b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs
@@ -7,7 +7,6 @@
using Xunit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using TestLibrary;
unsafe class CdeclMemberFunctionNative
{
@@ -68,7 +67,6 @@ public unsafe class CdeclMemberFunctionTest
{
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50440", TestPlatforms.Windows, runtimes: TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/131811", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsCoreCLR))]
public static int TestEntryPoint()
{
try
diff --git a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj
index a55d78d80bc6a8..00eaa467d6d71d 100644
--- a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj
+++ b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj
@@ -1,8 +1,5 @@
-
- false
true
true
diff --git a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs
index 8d8a03acc117c9..f8ed6ff7dc95a5 100644
--- a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs
+++ b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs
@@ -67,7 +67,6 @@ public enum E : uint
public unsafe class PlatformDefaultMemberFunctionTest
{
[ActiveIssue("https://github.com/dotnet/runtime/issues/50440", TestPlatforms.Windows, runtimes: TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/131811", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsCoreCLR))]
[Fact]
public static int TestEntryPoint()
{
diff --git a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj
index a55d78d80bc6a8..00eaa467d6d71d 100644
--- a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj
+++ b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj
@@ -1,8 +1,5 @@
-
- false
true
true
diff --git a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs
index 5bd075f8d54d0c..7b9cb8b7638229 100644
--- a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs
+++ b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs
@@ -7,7 +7,6 @@
using Xunit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using TestLibrary;
unsafe class StdCallMemberFunctionNative
{
@@ -68,7 +67,6 @@ public unsafe class StdCallMemberFunctionTest
{
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50440", TestPlatforms.Windows, runtimes: TestRuntimes.Mono)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/131811", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsCoreCLR))]
public static int TestEntryPoint()
{
try
diff --git a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj
index a55d78d80bc6a8..00eaa467d6d71d 100644
--- a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj
+++ b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj
@@ -1,8 +1,5 @@
-
- false
true
true
diff --git a/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs
index c0cd4b09813020..06dc917a6ae08b 100644
--- a/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs
+++ b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs
@@ -95,11 +95,16 @@ public static int TestEntryPoint()
Test4ByteNonHFA(instance);
TestEnum(instance);
TestCLong(instance);
- Test8ByteHFAReverse();
- Test4ByteHFAReverse();
- Test4ByteNonHFAReverse();
- TestEnumReverse();
- TestCLongReverse();
+ // Marshal.GetFunctionPointerForDelegate needs a stub created at run time, which CoreCLR
+ // on wasm cannot allocate.
+ if (!PlatformDetection.IsWasm)
+ {
+ Test8ByteHFAReverse();
+ Test4ByteHFAReverse();
+ Test4ByteNonHFAReverse();
+ TestEnumReverse();
+ TestCLongReverse();
+ }
Test8ByteHFAUnmanagedCallersOnly();
Test4ByteHFAUnmanagedCallersOnly();
Test4ByteNonHFAUnmanagedCallersOnly();
diff --git a/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj
index 9dc3602543121d..c943b6059364df 100644
--- a/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj
+++ b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj
@@ -1,10 +1,5 @@
-
- false
-
- true
true
true