Browse Source

Function pointers: add support for the builtin unmanaged calling conventions.

pull/2176/head
Daniel Grunwald 5 years ago
parent
commit
6d3239678c
  1. 20
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs
  2. 7
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  3. 13
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs
  4. 12
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  5. 15
      ICSharpCode.Decompiler/Disassembler/DisassemblerSignatureTypeProvider.cs
  6. 14
      ICSharpCode.Decompiler/SRMExtensions.cs
  7. 5
      ILSpy/Languages/Language.cs

20
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs

@ -48,6 +48,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -48,6 +48,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
internal class FunctionPointersWithCallingConvention
{
public unsafe delegate*<void> fn_default;
// Unmanaged without explicit callconv is only supported with .NET 5,
// and emits metadata that cannot be parsed by older SRM versions.
//public delegate* unmanaged<void> fn_unmanaged;
public unsafe delegate* unmanaged[Cdecl]<void> fn_cdecl;
public unsafe delegate* unmanaged[Fastcall]<void> fn_fastcall;
public unsafe delegate* unmanaged[Stdcall]<void> fn_stdcall;
public unsafe delegate* unmanaged[Thiscall]<void> fn_thiscall;
}
internal class FunctionPointersWithDynamicTypes
{
public class D<T, U>
@ -107,14 +119,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -107,14 +119,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
// TODO: the new calling convention syntax isn't yet available in the released Roslyn version
//internal unsafe class FunctionPointersWithCallingConvention
//{
// public delegate*<void> managed;
// public delegate* unmanaged<void> unmanaged;
// public delegate* unmanaged[Cdecl]<void> cdecl;
//}
internal class FunctionPointerTypeInference
{
private static char Test(int i)

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

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

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

@ -34,19 +34,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -34,19 +34,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class FunctionPointerAstType : AstType
{
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<Identifier> CallingConventionRole = new Role<Identifier>("Target", Identifier.Null);
public static readonly Role<AstType> CallingConventionRole = new Role<AstType>("CallConv", AstType.Null);
public string CallingConvention {
public AstType CallingConvention {
get {
return GetChildByRole(CallingConventionRole).Name;
return GetChildByRole(CallingConventionRole);
}
set {
SetChildByRole(CallingConventionRole, Identifier.Create(value));
SetChildByRole(CallingConventionRole, value);
}
}
public Identifier CallingConventionIdentifier => GetChildByRole(CallingConventionRole);
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
@ -73,7 +71,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -73,7 +71,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other is FunctionPointerAstType o && MatchString(this.CallingConvention, o.CallingConvention)
return other is FunctionPointerAstType o
&& this.CallingConvention.DoMatch(o.CallingConvention, match)
&& this.Parameters.DoMatch(o.Parameters, match)
&& this.ReturnType.DoMatch(o.ReturnType, match);
}

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

@ -311,7 +311,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -311,7 +311,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
var astType = new FunctionPointerAstType();
if (fpt.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
{
astType.CallingConvention = fpt.CallingConvention.ToString().ToLowerInvariant();
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",
System.Reflection.Metadata.SignatureCallingConvention.FastCall => "Fastcall",
System.Reflection.Metadata.SignatureCallingConvention.VarArgs => "Varargs",
_ => fpt.CallingConvention.ToString()
};
astType.CallingConvention = new PrimitiveType(callconvName);
}
for (int i = 0; i < fpt.ParameterTypes.Length; i++)
{

15
ICSharpCode.Decompiler/Disassembler/DisassemblerSignatureTypeProvider.cs

@ -78,6 +78,19 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -78,6 +78,19 @@ namespace ICSharpCode.Decompiler.Disassembler
{
return syntax => {
output.Write("method ");
if (signature.Header.IsInstance)
{
output.Write("instance ");
}
if (signature.Header.HasExplicitThis)
{
output.Write("explicit ");
}
if (signature.Header.CallingConvention != SignatureCallingConvention.Default)
{
output.Write(signature.Header.CallingConvention.ToILSyntax());
output.Write(' ');
}
signature.ReturnType(syntax);
output.Write(" *(");
for (int i = 0; i < signature.ParameterTypes.Length; i++)
@ -268,4 +281,4 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -268,4 +281,4 @@ namespace ICSharpCode.Decompiler.Disassembler
return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext);
}
}
}
}

14
ICSharpCode.Decompiler/SRMExtensions.cs

@ -575,5 +575,19 @@ namespace ICSharpCode.Decompiler @@ -575,5 +575,19 @@ namespace ICSharpCode.Decompiler
return default;
}
}
public static string ToILSyntax(this SignatureCallingConvention callConv)
{
return callConv switch
{
SignatureCallingConvention.Default => "default",
SignatureCallingConvention.CDecl => "unmanaged cdecl",
SignatureCallingConvention.StdCall => "unmanaged stdcall",
SignatureCallingConvention.ThisCall => "unmanaged thiscall",
SignatureCallingConvention.FastCall => "unmanaged fastcall",
SignatureCallingConvention.VarArgs => "vararg",
_ => callConv.ToString().ToLowerInvariant()
};
}
}
}

5
ILSpy/Languages/Language.cs

@ -257,6 +257,11 @@ namespace ICSharpCode.ILSpy @@ -257,6 +257,11 @@ namespace ICSharpCode.ILSpy
public override IType VisitFunctionPointerType(FunctionPointerType type)
{
builder.Append("method ");
if (type.CallingConvention != SignatureCallingConvention.Default)
{
builder.Append(type.CallingConvention.ToILSyntax());
builder.Append(' ');
}
type.ReturnType.AcceptVisitor(this);
builder.Append(" *(");
bool first = true;

Loading…
Cancel
Save