diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs index 93ca7236c..5379e4948 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs @@ -1,6 +1,6 @@ -#if !(CS110 && NET70) using System; -#endif +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -46,15 +46,168 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return (delegate*)(&VarianceTest); } - public unsafe delegate* AddressOfLocalFunction() + public unsafe delegate* AddressOfLocalFunction_Managed() + { + return &LocalFunction; + + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged AddressOfLocalFunction_Unmanaged() + { + return &LocalFunction; + + [UnmanagedCallersOnly] + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged[Cdecl] AddressOfLocalFunction_CDecl() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvCdecl) })] + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged[Fastcall] AddressOfLocalFunction_Fastcall() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvFastcall) })] + static void LocalFunction() + { + } + } + +#if NET60 + public unsafe delegate* unmanaged[MemberFunction] AddressOfLocalFunction_MemberFunction() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvMemberFunction) })] + static void LocalFunction() + { + } + } +#endif + + public unsafe delegate* unmanaged[Stdcall] AddressOfLocalFunction_Stdcall() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvStdcall) })] + static void LocalFunction() + { + } + } + +#if NET60 + public unsafe delegate* unmanaged[SuppressGCTransition] AddressOfLocalFunction_SuppressGCTransition() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSuppressGCTransition) })] + static void LocalFunction() + { + } + } +#endif + + public unsafe delegate* unmanaged[Thiscall] AddressOfLocalFunction_Thiscall() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvThiscall) })] + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged[Cdecl, Fastcall] AddressOfLocalFunction_CDeclAndFastcall() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvCdecl), + typeof(CallConvFastcall) + })] + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged[Fastcall, Cdecl] AddressOfLocalFunction_FastcallAndCDecl() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvFastcall), + typeof(CallConvCdecl) + })] + static void LocalFunction() + { + } + } + +#if NET60 + public unsafe delegate* unmanaged[Cdecl, SuppressGCTransition] AddressOfLocalFunction_CDeclAndSuppressGCTransition() { return &LocalFunction; + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvCdecl), + typeof(CallConvSuppressGCTransition) + })] static void LocalFunction() { + } + } + + public unsafe delegate* unmanaged[Fastcall, SuppressGCTransition] AddressOfLocalFunction_FastcallAndSuppressGCTransition() + { + return &LocalFunction; + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvFastcall), + typeof(CallConvSuppressGCTransition) + })] + static void LocalFunction() + { } } + + public unsafe delegate* unmanaged[Stdcall, SuppressGCTransition] AddressOfLocalFunction_StdcallAndSuppressGCTransition() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvStdcall), + typeof(CallConvSuppressGCTransition) + })] + static void LocalFunction() + { + } + } + + public unsafe delegate* unmanaged[Thiscall, SuppressGCTransition] AddressOfLocalFunction_ThiscallAndSuppressGCTransition() + { + return &LocalFunction; + + [UnmanagedCallersOnly(CallConvs = new Type[] { + typeof(CallConvThiscall), + typeof(CallConvSuppressGCTransition) + })] + static void LocalFunction() + { + } + } +#endif } internal class FunctionPointersWithCallingConvention diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 350a8287c..0e79d9b5d 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -4598,10 +4598,50 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst); } // C# 9 function pointer + SignatureCallingConvention callingConvention = SignatureCallingConvention.Default; + ImmutableArray customCallingConventions; + var unmanagedCallersOnlyAttribute = inst.Method.GetAttributes().FirstOrDefault(m => m.AttributeType.IsKnownType(KnownAttribute.UnmanagedCallersOnly)); + if (unmanagedCallersOnlyAttribute != null) + { + callingConvention = SignatureCallingConvention.Unmanaged; + + var callingConventionsArgument = unmanagedCallersOnlyAttribute.NamedArguments.FirstOrDefault(a => a.Name == "CallConvs"); + if (callingConventionsArgument.Value is ImmutableArray> array) + { + var builder = ImmutableArray.CreateBuilder(array.Length); + foreach (var type in array.Select(a => a.Value).OfType()) + { + SignatureCallingConvention? foundCallingConvention = type.Namespace is not "System.Runtime.CompilerServices" || callingConvention != SignatureCallingConvention.Unmanaged ? null : type.Name switch { + "CallConvCdecl" => SignatureCallingConvention.CDecl, + "CallConvFastcall" => SignatureCallingConvention.FastCall, + "CallConvStdcall" => SignatureCallingConvention.StdCall, + "CallConvThiscall" => SignatureCallingConvention.ThisCall, + _ => null, + }; + if (foundCallingConvention is not null) + { + callingConvention = foundCallingConvention.Value; + } + else + { + builder.Add(type); + } + } + customCallingConventions = builder.ToImmutable(); + } + else + { + customCallingConventions = []; + } + } + else + { + callingConvention = SignatureCallingConvention.Default; + customCallingConventions = []; + } var ftp = new FunctionPointerType( typeSystem.MainModule, - // TODO: calling convention - SignatureCallingConvention.Default, ImmutableArray.Create(), + callingConvention, customCallingConventions, inst.Method.ReturnType, inst.Method.ReturnTypeIsRefReadOnly, inst.Method.Parameters.SelectImmutableArray(p => p.Type), inst.Method.Parameters.SelectImmutableArray(p => p.ReferenceKind) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index dae49f6d0..d8c29dbe7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -368,7 +368,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (var customCallConv in fpt.CustomCallingConventions) { AstType callConvSyntax; - if (customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && customCallConv.Name.Length > 8) + if (customCallConv.Namespace == "System.Runtime.CompilerServices" && customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && customCallConv.Name.Length > 8) { callConvSyntax = new PrimitiveType(customCallConv.Name.Substring(8)); if (AddResolveResultAnnotations) diff --git a/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs b/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs index 78500e6f4..75fe0fd75 100644 --- a/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs @@ -33,6 +33,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { IType returnType = signature.ReturnType; bool returnIsRefReadOnly = false; + SignatureCallingConvention callingConvention = signature.Header.CallingConvention; var customCallConvs = ImmutableArray.CreateBuilder(); while (returnType is ModifiedType modReturn) { @@ -45,7 +46,31 @@ namespace ICSharpCode.Decompiler.TypeSystem && modReturn.Modifier.Namespace == "System.Runtime.CompilerServices") { returnType = modReturn.ElementType; - customCallConvs.Add(modReturn.Modifier); + if (callingConvention == SignatureCallingConvention.Unmanaged) + { + switch (modReturn.Modifier.Name) + { + case "CallConvCdecl": + callingConvention = SignatureCallingConvention.CDecl; + break; + case "CallConvFastcall": + callingConvention = SignatureCallingConvention.FastCall; + break; + case "CallConvStdcall": + callingConvention = SignatureCallingConvention.StdCall; + break; + case "CallConvThiscall": + callingConvention = SignatureCallingConvention.ThisCall; + break; + default: + customCallConvs.Add(modReturn.Modifier); + break; + } + } + else + { + customCallConvs.Add(modReturn.Modifier); + } } else { @@ -89,7 +114,7 @@ namespace ICSharpCode.Decompiler.TypeSystem parameterReferenceKinds.Add(kind); } return new FunctionPointerType( - module, signature.Header.CallingConvention, customCallConvs.ToImmutable(), + module, callingConvention, customCallConvs.ToImmutable(), returnType, returnIsRefReadOnly, parameterTypes.MoveToImmutable(), parameterReferenceKinds.MoveToImmutable()); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 065864daf..52a20c337 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.TypeSystem // C# 9 attributes: NativeInteger, PreserveBaseOverrides, + UnmanagedCallersOnly, // C# 11 attributes: Required, @@ -192,6 +193,7 @@ namespace ICSharpCode.Decompiler.TypeSystem // C# 9 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"), + new TopLevelTypeName("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute"), // C# 11 attributes: new TopLevelTypeName("System.Runtime.CompilerServices", "RequiredMemberAttribute"), // C# 12 attributes: