Browse Source

Add support for .NET 5 custom calling conventions.

pull/2179/head
Daniel Grunwald 5 years ago
parent
commit
6010757d22
  1. 17
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs
  2. 39
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il
  3. 4
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 7
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  5. 13
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs
  6. 27
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  7. 1
      ICSharpCode.Decompiler/SRMExtensions.cs
  8. 38
      ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs

17
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs

@ -5,4 +5,19 @@ internal class CallIndirect @@ -5,4 +5,19 @@ internal class CallIndirect
{
((delegate* unmanaged[Stdcall]<int, void>)f)(42);
}
}
private unsafe void UnmanagedDefaultCall(IntPtr f)
{
((delegate* unmanaged<int, void>)f)(42);
}
private unsafe void CustomCall(IntPtr f)
{
((delegate* unmanaged[Custom]<int, void>)f)(42);
}
private unsafe void MultipleCustomCall(IntPtr f)
{
((delegate* unmanaged[SuppressGCTransition, Custom]<int, void>)f)(42);
}
}

39
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il

@ -23,6 +23,45 @@ @@ -23,6 +23,45 @@
ret
} // end of method Example::Test
.method private hidebysig
instance void UnmanagedDefaultCall (
native int f
) cil managed
{
.maxstack 8
ldc.i4.s 42
ldarg.1
calli unmanaged void(int32)
ret
} // end of method Example::Test
.method private hidebysig
instance void CustomCall (
native int f
) cil managed
{
.maxstack 8
ldc.i4.s 42
ldarg.1
calli unmanaged void modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvCustom)(int32)
ret
} // end of method Example::Test
.method private hidebysig
instance void MultipleCustomCall (
native int f
) cil managed
{
.maxstack 8
ldc.i4.s 42
ldarg.1
calli unmanaged void modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvCustom) modreq([System.Private.CoreLib] System.Runtime.CompilerServices.CallConvSuppressGCTransition) (int32)
ret
} // end of method Example::Test
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
@ -3948,7 +3949,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3948,7 +3949,8 @@ namespace ICSharpCode.Decompiler.CSharp
// C# 9 function pointer
var ftp = new FunctionPointerType(
typeSystem.MainModule,
SignatureCallingConvention.Default, // TODO
// TODO: calling convention
SignatureCallingConvention.Default, ImmutableArray.Create<IType>(),
inst.Method.ReturnType, inst.Method.ReturnTypeIsRefReadOnly,
inst.Method.Parameters.SelectImmutableArray(p => p.Type),
inst.Method.Parameters.SelectImmutableArray(p => p.ReferenceKind)

7
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2605,12 +2605,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2605,12 +2605,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(functionPointerType);
WriteKeyword(Roles.DelegateKeyword);
WriteToken(FunctionPointerAstType.PointerRole);
if (!functionPointerType.CallingConvention.IsNull)
if (functionPointerType.HasUnmanagedCallingConvention)
{
Space();
WriteKeyword("unmanaged");
}
if (functionPointerType.CallingConventions.Any())
{
WriteToken(Roles.LBracket);
functionPointerType.CallingConvention.AcceptVisitor(this);
WriteCommaSeparatedList(functionPointerType.CallingConventions);
WriteToken(Roles.RBracket);
}
WriteToken(Roles.LChevron);

13
ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs

@ -36,13 +36,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -36,13 +36,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<AstType> CallingConventionRole = new Role<AstType>("CallConv", AstType.Null);
public AstType CallingConvention {
get {
return GetChildByRole(CallingConventionRole);
}
set {
SetChildByRole(CallingConventionRole, value);
}
public bool HasUnmanagedCallingConvention { get; set; }
public AstNodeCollection<AstType> CallingConventions {
get { return GetChildrenByRole(CallingConventionRole); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
@ -72,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -72,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other is FunctionPointerAstType o
&& this.CallingConvention.DoMatch(o.CallingConvention, match)
&& this.CallingConventions.DoMatch(o.CallingConventions, match)
&& this.Parameters.DoMatch(o.Parameters, match)
&& this.ReturnType.DoMatch(o.ReturnType, match);
}

27
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -309,11 +309,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -309,11 +309,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
else if (type is FunctionPointerType fpt)
{
var astType = new FunctionPointerAstType();
if (fpt.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
if (fpt.CallingConvention == System.Reflection.Metadata.SignatureCallingConvention.Unmanaged)
{
astType.HasUnmanagedCallingConvention = true;
}
else if (fpt.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
{
string callconvName = fpt.CallingConvention switch
{
System.Reflection.Metadata.SignatureCallingConvention.Default => "",
System.Reflection.Metadata.SignatureCallingConvention.CDecl => "Cdecl",
System.Reflection.Metadata.SignatureCallingConvention.StdCall => "Stdcall",
System.Reflection.Metadata.SignatureCallingConvention.ThisCall => "Thiscall",
@ -321,7 +324,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -321,7 +324,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
System.Reflection.Metadata.SignatureCallingConvention.VarArgs => "Varargs",
_ => fpt.CallingConvention.ToString()
};
astType.CallingConvention = new PrimitiveType(callconvName);
astType.HasUnmanagedCallingConvention = true;
astType.CallingConventions.Add(new PrimitiveType(callconvName));
}
foreach (var customCallConv in fpt.CustomCallingConventions)
{
AstType callConvSyntax;
if (customCallConv.Name.StartsWith("CallConv", StringComparison.Ordinal) && customCallConv.Name.Length > 8)
{
callConvSyntax = new PrimitiveType(customCallConv.Name.Substring(8));
if (AddResolveResultAnnotations)
{
callConvSyntax.AddAnnotation(new TypeResolveResult(customCallConv));
}
}
else
{
callConvSyntax = ConvertType(customCallConv);
}
astType.CallingConventions.Add(callConvSyntax);
}
for (int i = 0; i < fpt.ParameterTypes.Length; i++)
{

1
ICSharpCode.Decompiler/SRMExtensions.cs

@ -586,6 +586,7 @@ namespace ICSharpCode.Decompiler @@ -586,6 +586,7 @@ namespace ICSharpCode.Decompiler
SignatureCallingConvention.ThisCall => "unmanaged thiscall",
SignatureCallingConvention.FastCall => "unmanaged fastcall",
SignatureCallingConvention.VarArgs => "vararg",
SignatureCallingConvention.Unmanaged => "unmanaged",
_ => callConv.ToString().ToLowerInvariant()
};
}

38
ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs

@ -33,10 +33,24 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -33,10 +33,24 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
IType returnType = signature.ReturnType;
bool returnIsRefReadOnly = false;
if (returnType is ModifiedType modReturn && modReturn.Modifier.IsKnownType(KnownAttribute.In))
var customCallConvs = ImmutableArray.CreateBuilder<IType>();
while (returnType is ModifiedType modReturn)
{
returnType = modReturn.ElementType;
returnIsRefReadOnly = true;
if (modReturn.Modifier.IsKnownType(KnownAttribute.In))
{
returnType = modReturn.ElementType;
returnIsRefReadOnly = true;
}
else if (modReturn.Modifier.Name.StartsWith("CallConv", StringComparison.Ordinal)
&& modReturn.Modifier.Namespace == "System.Runtime.CompilerServices")
{
returnType = modReturn.ElementType;
customCallConvs.Add(modReturn.Modifier);
}
else
{
break;
}
}
var parameterTypes = ImmutableArray.CreateBuilder<IType>(signature.ParameterTypes.Length);
var parameterReferenceKinds = ImmutableArray.CreateBuilder<ReferenceKind>(signature.ParameterTypes.Length);
@ -70,24 +84,27 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -70,24 +84,27 @@ namespace ICSharpCode.Decompiler.TypeSystem
parameterReferenceKinds.Add(kind);
}
return new FunctionPointerType(
module, signature.Header.CallingConvention,
module, signature.Header.CallingConvention, customCallConvs.ToImmutable(),
returnType, returnIsRefReadOnly,
parameterTypes.MoveToImmutable(), parameterReferenceKinds.MoveToImmutable());
}
private readonly MetadataModule module;
public readonly SignatureCallingConvention CallingConvention;
public readonly ImmutableArray<IType> CustomCallingConventions;
public readonly IType ReturnType;
public readonly bool ReturnIsRefReadOnly;
public readonly ImmutableArray<IType> ParameterTypes;
public readonly ImmutableArray<ReferenceKind> ParameterReferenceKinds;
public FunctionPointerType(MetadataModule module, SignatureCallingConvention callingConvention,
public FunctionPointerType(MetadataModule module,
SignatureCallingConvention callingConvention, ImmutableArray<IType> customCallingConventions,
IType returnType, bool returnIsRefReadOnly,
ImmutableArray<IType> parameterTypes, ImmutableArray<ReferenceKind> parameterReferenceKinds)
{
this.module = module;
this.CallingConvention = callingConvention;
this.CustomCallingConventions = customCallingConventions;
this.ReturnType = returnType;
this.ReturnIsRefReadOnly = returnIsRefReadOnly;
this.ParameterTypes = parameterTypes;
@ -146,7 +163,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -146,7 +163,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return this;
else
return new FunctionPointerType(
module, CallingConvention,
module, CallingConvention, CustomCallingConventions,
r, ReturnIsRefReadOnly,
pt != null ? pt.ToImmutableArray() : ParameterTypes,
ParameterReferenceKinds);
@ -156,6 +173,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -156,6 +173,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return other is FunctionPointerType fpt
&& CallingConvention == fpt.CallingConvention
&& CustomCallingConventions.SequenceEqual(fpt.CustomCallingConventions)
&& ReturnType.Equals(fpt.ReturnType)
&& ReturnIsRefReadOnly == fpt.ReturnIsRefReadOnly
&& ParameterTypes.SequenceEqual(fpt.ParameterTypes)
@ -166,7 +184,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -166,7 +184,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
unchecked
{
int hash = ReturnType.GetHashCode();
int hash = ReturnType.GetHashCode() ^ CallingConvention.GetHashCode();
foreach (var (p, k) in ParameterTypes.Zip(ParameterReferenceKinds))
{
hash ^= p.GetHashCode() ^ k.GetHashCode();
@ -178,8 +196,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -178,8 +196,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
internal IType WithSignature(IType returnType, ImmutableArray<IType> parameterTypes)
{
return new FunctionPointerType(this.module, this.CallingConvention, returnType,
this.ReturnIsRefReadOnly, parameterTypes, this.ParameterReferenceKinds);
return new FunctionPointerType(this.module,
this.CallingConvention, this.CustomCallingConventions,
returnType, this.ReturnIsRefReadOnly,
parameterTypes, this.ParameterReferenceKinds);
}
}
}

Loading…
Cancel
Save