Browse Source

Support `ldftn` in the context of a function pointer.

pull/2150/head
Daniel Grunwald 5 years ago
parent
commit
d13a8bb64d
  1. 26
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs
  2. 12
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 31
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 10
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  5. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  6. 14
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  7. 11
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
  8. 15
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs

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

@ -2,6 +2,31 @@ @@ -2,6 +2,31 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class FunctionPointerAddressOf
{
public static void Overloaded()
{
}
public static void Overloaded(int a)
{
}
public unsafe delegate*<void> GetAddress()
{
return &Overloaded;
}
public unsafe IntPtr GetAddressAsIntPtr()
{
return (IntPtr)(delegate*<void>)(&Overloaded);
}
public unsafe void* GetAddressAsVoidPtr()
{
return (delegate*<int, void>)(&Overloaded);
}
}
internal class FunctionPointersWithDynamicTypes
{
public class D<T, U>
@ -68,4 +93,5 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -68,4 +93,5 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
// public delegate* unmanaged<void> unmanaged;
// public delegate* unmanaged[Cdecl]<void> cdecl;
//}
}

12
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1197,6 +1197,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1197,6 +1197,16 @@ namespace ICSharpCode.Decompiler.CSharp
{
IType argType = NullableType.GetUnderlyingType(arguments[0].Type);
operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name);
if (method.Name == "op_Explicit")
{
// For casts, also consider candidates from the target type we are casting to.
var hashSet = new HashSet<IParameterizedMember>(operatorCandidates);
IType targetType = NullableType.GetUnderlyingType(method.ReturnType);
hashSet.UnionWith(
resolver.GetUserDefinedOperatorCandidates(targetType, method.Name)
);
operatorCandidates = hashSet;
}
}
else if (arguments.Length == 2)
{
@ -1825,4 +1835,4 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1825,4 +1835,4 @@ namespace ICSharpCode.Decompiler.CSharp
return false;
}
}
}
}

31
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using ICSharpCode.Decompiler.CSharp.Resolver;
@ -3869,16 +3870,38 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3869,16 +3870,38 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdFtn(LdFtn inst, TranslationContext context)
{
ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: false);
return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithILInstruction(inst);
if (!inst.Method.IsStatic)
{
// C# 9 function pointers don't support instance methods
return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef)
.WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void))))
.WithILInstruction(inst);
}
// C# 9 function pointer
var ftp = new FunctionPointerType(
typeSystem.MainModule,
SignatureCallingConvention.Default, // TODO
inst.Method.ReturnType, inst.Method.ReturnTypeIsRefReadOnly,
inst.Method.Parameters.SelectImmutableArray(p => p.Type),
inst.Method.Parameters.SelectImmutableArray(p => p.ReferenceKind)
);
ExpressionWithResolveResult addressOf = new UnaryOperatorExpression(
UnaryOperatorType.AddressOf,
delegateRef
).WithRR(new ResolveResult(SpecialType.NoType)).WithILInstruction(inst);
var conversion = Conversion.MethodGroupConversion(
inst.Method, isVirtualMethodLookup: false, delegateCapturesFirstArgument: false);
return new CastExpression(ConvertType(ftp), addressOf)
.WithRR(new ConversionResolveResult(ftp, addressOf.ResolveResult, conversion))
.WithoutILInstruction();
}
protected internal override TranslatedExpression VisitLdVirtFtn(LdVirtFtn inst, TranslationContext context)
{
// C# 9 function pointers don't support instance methods
ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: true);
return new InvocationExpression(new IdentifierExpression("__ldvirtftn"), delegateRef)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithRR(new ResolveResult(new PointerType(compilation.FindType(KnownTypeCode.Void))))
.WithILInstruction(inst);
}

10
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -870,9 +870,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -870,9 +870,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool ImplicitPointerConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §18.4 Pointer conversions
if (fromType is PointerType && toType is PointerType && toType.ReflectionName == "System.Void*")
if (fromType.Kind.IsAnyPointer() && toType is PointerType && toType.ReflectionName == "System.Void*")
return true;
if (fromType.Kind == TypeKind.Null && toType is PointerType)
if (fromType.Kind == TypeKind.Null && toType.Kind.IsAnyPointer())
return true;
return false;
}
@ -880,13 +880,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -880,13 +880,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool ExplicitPointerConversion(IType fromType, IType toType)
{
// C# 4.0 spec: §18.4 Pointer conversions
if (fromType.Kind == TypeKind.Pointer)
if (fromType.Kind.IsAnyPointer())
{
return toType.Kind == TypeKind.Pointer || IsIntegerType(toType);
return toType.Kind.IsAnyPointer() || IsIntegerType(toType);
}
else
{
return toType.Kind == TypeKind.Pointer && IsIntegerType(fromType);
return toType.Kind.IsAnyPointer() && IsIntegerType(fromType);
}
}

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

@ -309,6 +309,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -309,6 +309,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
else if (type is FunctionPointerType fpt)
{
var astType = new FunctionPointerAstType();
if (fpt.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
{
astType.CallingConvention = fpt.CallingConvention.ToString().ToLowerInvariant();
}
for (int i = 0; i < fpt.ParameterTypes.Length; i++)
{
var paramDecl = new ParameterDeclaration();

14
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -370,7 +370,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -370,7 +370,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
}
else if (type.Kind != TypeKind.Pointer)
else if (!type.Kind.IsAnyPointer())
{
// If overflow-checking is disabled, the only way to truncate to native size
// without throwing an exception in 32-bit mode is to use a pointer type.
@ -380,7 +380,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -380,7 +380,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
else if (targetUType.IsKnownType(KnownTypeCode.UIntPtr))
{ // Conversion to UIntPtr
if (type.IsKnownType(KnownTypeCode.UInt32) || type.Kind == TypeKind.Pointer || type.Kind == TypeKind.NUInt)
if (type.IsKnownType(KnownTypeCode.UInt32) || type.Kind.IsAnyPointer() || type.Kind == TypeKind.NUInt)
{
// normal casts work for uint/nuint and pointers (both in checked and unchecked context)
}
@ -409,22 +409,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -409,22 +409,22 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
if (targetType.Kind == TypeKind.Pointer && type.Kind == TypeKind.Enum)
if (targetType.Kind.IsAnyPointer() && type.Kind == TypeKind.Enum)
{
// enum to pointer: C# doesn't allow such casts
// -> convert via underlying type
return this.ConvertTo(type.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
else if (targetUType.Kind == TypeKind.Enum && type.Kind == TypeKind.Pointer)
else if (targetUType.Kind == TypeKind.Enum && type.Kind.IsAnyPointer())
{
// pointer to enum: C# doesn't allow such casts
// -> convert via underlying type
return this.ConvertTo(targetUType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
if (targetType.Kind == TypeKind.Pointer && type.IsKnownType(KnownTypeCode.Char)
|| targetUType.IsKnownType(KnownTypeCode.Char) && type.Kind == TypeKind.Pointer)
if (targetType.Kind.IsAnyPointer() && type.IsKnownType(KnownTypeCode.Char)
|| targetUType.IsKnownType(KnownTypeCode.Char) && type.Kind.IsAnyPointer())
{
// char <-> pointer: C# doesn't allow such casts
// -> convert via ushort
@ -529,7 +529,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -529,7 +529,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
}
if (targetType.Kind == TypeKind.Pointer && (0.Equals(ResolveResult.ConstantValue) || 0u.Equals(ResolveResult.ConstantValue)))
if (targetType.Kind.IsAnyPointer() && (0.Equals(ResolveResult.ConstantValue) || 0u.Equals(ResolveResult.ConstantValue)))
{
if (allowImplicitConversion)
{

11
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -20,7 +20,6 @@ using System; @@ -20,7 +20,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
@ -294,6 +293,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -294,6 +293,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
return accessor.ThisIsRefReadOnly && accessor.DeclaringTypeDefinition?.IsReadOnly == false;
}
public static bool IsAnyPointer(this TypeKind typeKind)
{
return typeKind switch
{
TypeKind.Pointer => true,
TypeKind.FunctionPointer => true,
_ => false
};
}
#region GetType/Member
/// <summary>
/// Gets all type definitions in the compilation.

15
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace ICSharpCode.Decompiler.Util
@ -137,6 +138,20 @@ namespace ICSharpCode.Decompiler.Util @@ -137,6 +138,20 @@ namespace ICSharpCode.Decompiler.Util
return result;
}
/// <summary>
/// Equivalent to <code>collection.Select(func).ToImmutableArray()</code>, but more efficient as it makes
/// use of the input collection's known size.
/// </summary>
public static ImmutableArray<U> SelectImmutableArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func)
{
var builder = ImmutableArray.CreateBuilder<U>(collection.Count);
foreach (var element in collection)
{
builder.Add(func(element));
}
return builder.MoveToImmutable();
}
/// <summary>
/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
/// use of the input collection's known size.

Loading…
Cancel
Save