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
{ {
((delegate* unmanaged[Stdcall]<int, void>)f)(42); ((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 @@
ret ret
} // end of method Example::Test } // 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 .method public hidebysig specialname rtspecialname
instance void .ctor () cil managed instance void .ctor () cil managed
{ {

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

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

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

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

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

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

@ -309,11 +309,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
else if (type is FunctionPointerType fpt) else if (type is FunctionPointerType fpt)
{ {
var astType = new FunctionPointerAstType(); 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 string callconvName = fpt.CallingConvention switch
{ {
System.Reflection.Metadata.SignatureCallingConvention.Default => "",
System.Reflection.Metadata.SignatureCallingConvention.CDecl => "Cdecl", System.Reflection.Metadata.SignatureCallingConvention.CDecl => "Cdecl",
System.Reflection.Metadata.SignatureCallingConvention.StdCall => "Stdcall", System.Reflection.Metadata.SignatureCallingConvention.StdCall => "Stdcall",
System.Reflection.Metadata.SignatureCallingConvention.ThisCall => "Thiscall", System.Reflection.Metadata.SignatureCallingConvention.ThisCall => "Thiscall",
@ -321,7 +324,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
System.Reflection.Metadata.SignatureCallingConvention.VarArgs => "Varargs", System.Reflection.Metadata.SignatureCallingConvention.VarArgs => "Varargs",
_ => fpt.CallingConvention.ToString() _ => 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++) for (int i = 0; i < fpt.ParameterTypes.Length; i++)
{ {

1
ICSharpCode.Decompiler/SRMExtensions.cs

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

38
ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs

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

Loading…
Cancel
Save