Browse Source

Merge pull request #2150 from icsharpcode/fnptr

Support for C# 9 function pointers
pull/2160/head
Daniel Grunwald 5 years ago committed by GitHub
parent
commit
9d590e4c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 20
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs
  4. 140
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs
  5. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
  6. 8
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs
  7. 12
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  8. 68
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  9. 11
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  10. 17
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  11. 31
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  12. 48
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  13. 6
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  14. 20
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs
  15. 6
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  16. 45
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  17. 28
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  18. 26
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  19. 22
      ICSharpCode.Decompiler/DecompilerSettings.cs
  20. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  21. 18
      ICSharpCode.Decompiler/IL/ILReader.cs
  22. 4
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  23. 42
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  24. 4
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  25. 39
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  26. 11
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  27. 185
      ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs
  28. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  29. 13
      ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs
  30. 5
      ICSharpCode.Decompiler/TypeSystem/TypeKind.cs
  31. 7
      ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs
  32. 11
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
  33. 3
      ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs
  34. 7
      ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs
  35. 15
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs
  36. 13
      ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs
  37. 19
      ILSpy/Languages/Language.cs
  38. 9
      ILSpy/Properties/Resources.Designer.cs
  39. 3
      ILSpy/Properties/Resources.resx

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -95,6 +95,7 @@ @@ -95,6 +95,7 @@
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\Pretty\FunctionPointers.cs" />
<None Include="TestCases\Pretty\UsingVariables.cs" />
<None Include="TestCases\Pretty\AsyncForeach.cs" />
<Compile Include="TestCases\Pretty\DeconstructionTests.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -372,6 +372,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -372,6 +372,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]
public void FunctionPointers([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]
public void NullPropagation([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{

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

@ -487,6 +487,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -487,6 +487,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return (int)(dynamic)o;
}
#if CS72
public void RefParams(ref object a, ref dynamic b, ref dynamic c)
{
}
public void RefParams2(in object a, ref dynamic b, out dynamic c)
{
c = null;
}
public ref dynamic RefReturn(ref object o)
{
return ref o;
}
public ref readonly dynamic RefReadonlyReturn(in object o)
{
return ref o;
}
#endif
}
internal static class Extension

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

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
using System;
using System.Text;
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);
}
public static string VarianceTest(object o)
{
return null;
}
public unsafe delegate*<StringBuilder, object> Variance()
{
return (delegate*<object, string>)(&VarianceTest);
}
#if TODO
public unsafe delegate*<void> AddressOfLocalFunction()
{
return &LocalFunction;
static void LocalFunction()
{
}
}
#endif
}
internal class FunctionPointersWithDynamicTypes
{
public class D<T, U>
{
}
public class A<T>
{
public class B<U>
{
}
}
public unsafe delegate*<dynamic, dynamic, dynamic> F1;
public unsafe delegate*<object, object, dynamic> F2;
public unsafe delegate*<dynamic, object, object> F3;
public unsafe delegate*<object, dynamic, object> F4;
public unsafe delegate*<object, object, object> F5;
public unsafe delegate*<object, object, ref dynamic> F6;
public unsafe delegate*<ref dynamic, object, object> F7;
public unsafe delegate*<object, ref dynamic, object> F8;
public unsafe delegate*<ref object, ref object, dynamic> F9;
public unsafe delegate*<dynamic, ref object, ref object> F10;
public unsafe delegate*<ref object, dynamic, ref object> F11;
public unsafe delegate*<object, ref readonly dynamic> F12;
public unsafe delegate*<in dynamic, object> F13;
public unsafe delegate*<out dynamic, object> F14;
public unsafe D<delegate*<dynamic>[], dynamic> F15;
public unsafe delegate*<A<object>.B<dynamic>> F16;
}
internal class FunctionPointersWithNativeIntegerTypes
{
public unsafe delegate*<nint, nint, nint> F1;
public unsafe delegate*<IntPtr, IntPtr, nint> F2;
public unsafe delegate*<nint, IntPtr, IntPtr> F3;
public unsafe delegate*<IntPtr, nint, IntPtr> F4;
public unsafe delegate*<delegate*<IntPtr, IntPtr, IntPtr>, nint> F5;
public unsafe delegate*<nint, delegate*<IntPtr, IntPtr, IntPtr>> F6;
public unsafe delegate*<delegate*<IntPtr, IntPtr, nint>, IntPtr> F7;
public unsafe delegate*<IntPtr, delegate*<IntPtr, nint, IntPtr>> F8;
}
internal class FunctionPointersWithRefParams
{
public unsafe delegate*<in byte, ref char, out float, ref readonly int> F1;
public unsafe delegate*<ref char, out float, ref int> F2;
// TODO: re-enable test after https://github.com/dotnet/roslyn/issues/47487 is fixed
//public unsafe int CallF1(byte b, char c, out float f)
//{
// return F1(1, ref c, out f);
//}
public unsafe void CallF2(byte b, char c, out float f)
{
F2(ref c, out f) = b;
}
}
// 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)
{
return (char)i;
}
public unsafe R GenericMethod<T, R>(delegate*<T, R> f, T arg)
{
return f(arg);
}
public unsafe void Call()
{
delegate*<int, char> f = &Test;
GenericMethod(f, 0);
GenericMethod((delegate*<int, char>)(&Test), 1);
GenericMethod<int, char>(null, 2);
}
}
}

15
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

@ -509,5 +509,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -509,5 +509,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
int num = value.Integers[index];
num.ToString();
}
private unsafe static void* CastToVoidPtr(IntPtr intptr)
{
return (void*)intptr;
}
private unsafe static void* CastToVoidPtr(UIntPtr intptr)
{
return (void*)intptr;
}
private unsafe static void* CastToVoidPtr(int* intptr)
{
return intptr;
}
}
}

8
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs

@ -9,9 +9,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly @@ -9,9 +9,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
internal static class NoExtensionMethods
{
[Extension]
internal static Func<T> AsFunc<T>(T value) where T : class
internal unsafe static Func<T> AsFunc<T>(T value) where T : class
{
return new Func<T>(value, __ldftn(Return));
return new Func<T>(value, (nint)(delegate*<T, T>)(&Return));
}
[Extension]
@ -25,9 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly @@ -25,9 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
return Return;
}
internal static Func<object> ExtensionMethodBoundToNull()
internal unsafe static Func<object> ExtensionMethodBoundToNull()
{
return new Func<object>(null, __ldftn(Return));
return new Func<object>(null, (nint)(delegate*<object, object>)(&Return));
}
}
}

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;
}
}
}
}

68
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2014 Daniel Grunwald
// Copyright (c) 2014-2020 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@ -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;
@ -3902,16 +3903,38 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3902,16 +3903,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);
}
@ -3921,23 +3944,36 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3921,23 +3944,36 @@ namespace ICSharpCode.Decompiler.CSharp
{
return ErrorExpression("calli with instance method signature not supportd");
}
var ty = new FunctionPointerType();
if (inst.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
var functionPointer = Translate(inst.FunctionPointer, typeHint: inst.FunctionPointerType);
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(functionPointer.Type, inst.FunctionPointerType))
{
ty.CallingConvention = inst.CallingConvention.ToString().ToLowerInvariant();
functionPointer = functionPointer.ConvertTo(inst.FunctionPointerType, this);
}
foreach (var parameterType in inst.ParameterTypes)
var fpt = (FunctionPointerType)functionPointer.Type.SkipModifiers();
var invocation = new InvocationExpression();
invocation.Target = functionPointer;
foreach (var (argInst, (paramType, paramRefKind)) in inst.Arguments.Zip(fpt.ParameterTypes.Zip(fpt.ParameterReferenceKinds)))
{
ty.TypeArguments.Add(astBuilder.ConvertType(parameterType));
var arg = Translate(argInst, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true);
if (paramRefKind != ReferenceKind.None)
{
arg = ChangeDirectionExpressionTo(arg, paramRefKind);
}
invocation.Arguments.Add(arg);
}
ty.TypeArguments.Add(astBuilder.ConvertType(inst.ReturnType));
var functionPointer = Translate(inst.FunctionPointer);
var invocation = new InvocationExpression(new CastExpression(ty, functionPointer));
foreach (var (arg, paramType) in inst.Arguments.Zip(inst.ParameterTypes))
if (fpt.ReturnType.SkipModifiers() is ByReferenceType brt)
{
var rr = new ResolveResult(brt.ElementType);
return new DirectionExpression(
FieldDirection.Ref,
invocation.WithRR(rr).WithILInstruction(inst)
).WithRR(new ByReferenceResolveResult(rr, ReferenceKind.Ref)).WithoutILInstruction();
}
else
{
invocation.Arguments.Add(Translate(arg, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true));
return invocation.WithRR(new ResolveResult(fpt.ReturnType)).WithILInstruction(inst);
}
return invocation.WithRR(new ResolveResult(inst.ReturnType)).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitDeconstructInstruction(DeconstructInstruction inst, TranslationContext context)

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
// Copyright (c) 2010-2020 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
@ -2600,17 +2600,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2600,17 +2600,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
EndNode(tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType)
{
StartNode(functionPointerType);
WriteKeyword(Roles.DelegateKeyword);
WriteToken(FunctionPointerType.PointerRole);
WriteToken(FunctionPointerAstType.PointerRole);
if (!functionPointerType.CallingConventionIdentifier.IsNull)
{
Space();
WriteIdentifier(functionPointerType.CallingConventionIdentifier);
}
WriteTypeArguments(functionPointerType.TypeArguments);
WriteToken(Roles.LChevron);
WriteCommaSeparatedList(
functionPointerType.Parameters.Concat<AstNode>(new[] { functionPointerType.ReturnType }));
WriteToken(Roles.RChevron);
EndNode(functionPointerType);
}

17
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -180,6 +180,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -180,6 +180,13 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(elementType);
}
break;
case FunctionPointerType fnPtrType:
CollectNamespacesForTypeReference(fnPtrType.ReturnType);
foreach (var paramType in fnPtrType.ParameterTypes)
{
CollectNamespacesForTypeReference(paramType);
}
break;
default:
namespaces.Add(type.Namespace);
break;
@ -341,20 +348,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -341,20 +348,16 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (sig.GetKind() == StandaloneSignatureKind.Method)
{
MethodSignature<IType> methodSig;
FunctionPointerType fpt;
try
{
methodSig = module.DecodeMethodSignature((StandaloneSignatureHandle)handle, genericContext);
(_, fpt) = module.DecodeMethodSignature((StandaloneSignatureHandle)handle, genericContext);
}
catch (BadImageFormatException)
{
break;
}
CollectNamespacesForTypeReference(methodSig.ReturnType);
foreach (var paramType in methodSig.ParameterTypes)
{
CollectNamespacesForTypeReference(paramType);
}
CollectNamespacesForTypeReference(fpt);
}
break;
}

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

@ -870,23 +870,44 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -870,23 +870,44 @@ 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;
if (fromType is FunctionPointerType fromFnPtr && toType is FunctionPointerType toFnPtr
&& fromFnPtr.CallingConvention == toFnPtr.CallingConvention
&& fromFnPtr.ParameterTypes.Length == toFnPtr.ParameterTypes.Length)
{
// Variance applies to function pointer types
const int nestingDepth = 0;
if (!(IdentityConversion(fromFnPtr.ReturnType, toFnPtr.ReturnType)
|| ImplicitReferenceConversion(fromFnPtr.ReturnType, toFnPtr.ReturnType, nestingDepth)))
{
return false;
}
foreach (var (fromPT, toPT) in fromFnPtr.ParameterTypes.Zip(toFnPtr.ParameterTypes))
{
if (!(IdentityConversion(toPT, fromPT)
|| ImplicitReferenceConversion(toPT, fromPT, nestingDepth)))
{
return false;
}
}
return true;
}
return false;
}
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);
}
}

48
ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs

@ -676,6 +676,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -676,6 +676,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
MakeExactInference(pU.GetTypeArgument(i), pV.GetTypeArgument(i));
}
Log.Unindent();
return;
}
// Handle pointer types:
if (U is PointerType ptrU && V is PointerType ptrV)
{
MakeExactInference(ptrU.ElementType, ptrV.ElementType);
return;
}
if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV)
{
MakeExactInference(fnPtrU.ReturnType, fnPtrV.ReturnType);
foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes))
{
MakeExactInference(ptU, ptV);
}
return;
}
}
@ -792,6 +808,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -792,6 +808,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
}
Log.Unindent();
return;
}
// Handle pointer types:
if (U is PointerType ptrU && V is PointerType ptrV)
{
MakeExactInference(ptrU.ElementType, ptrV.ElementType);
return;
}
if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV)
{
MakeLowerBoundInference(fnPtrU.ReturnType, fnPtrV.ReturnType);
foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes))
{
MakeUpperBoundInference(ptU, ptV);
}
return;
}
}
@ -897,6 +929,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -897,6 +929,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
}
Log.Unindent();
return;
}
// Handle pointer types:
if (U is PointerType ptrU && V is PointerType ptrV)
{
MakeExactInference(ptrU.ElementType, ptrV.ElementType);
return;
}
if (U is FunctionPointerType fnPtrU && V is FunctionPointerType fnPtrV)
{
MakeUpperBoundInference(fnPtrU.ReturnType, fnPtrV.ReturnType);
foreach (var (ptU, ptV) in fnPtrU.ParameterTypes.Zip(fnPtrV.ParameterTypes))
{
MakeLowerBoundInference(ptU, ptV);
}
return;
}
}
#endregion

6
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren(tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
public virtual void VisitFunctionPointerType(FunctionPointerAstType functionPointerType)
{
VisitChildren(functionPointerType);
}
@ -780,7 +780,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -780,7 +780,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren(tupleTypeElement);
}
public virtual T VisitFunctionPointerType(FunctionPointerType functionPointerType)
public virtual T VisitFunctionPointerType(FunctionPointerAstType functionPointerType)
{
return VisitChildren(functionPointerType);
}
@ -1448,7 +1448,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1448,7 +1448,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren(tupleTypeElement, data);
}
public virtual S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data)
public virtual S VisitFunctionPointerType(FunctionPointerAstType functionPointerType, T data)
{
return VisitChildren(functionPointerType, data);
}

20
ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs → ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs

@ -25,16 +25,13 @@ @@ -25,16 +25,13 @@
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class FunctionPointerType : AstType
public class FunctionPointerAstType : AstType
{
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<Identifier> CallingConventionRole = new Role<Identifier>("Target", Identifier.Null);
@ -50,8 +47,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -50,8 +47,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public Identifier CallingConventionIdentifier => GetChildByRole(CallingConventionRole);
public AstNodeCollection<AstType> TypeArguments {
get { return GetChildrenByRole(Roles.TypeArgument); }
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
}
public override void AcceptVisitor(IAstVisitor visitor)
@ -71,8 +73,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -71,8 +73,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other is FunctionPointerType o && MatchString(this.CallingConvention, o.CallingConvention)
&& this.TypeArguments.DoMatch(o.TypeArguments, match);
return other is FunctionPointerAstType o && MatchString(this.CallingConvention, o.CallingConvention)
&& this.Parameters.DoMatch(o.Parameters, match)
&& this.ReturnType.DoMatch(o.ReturnType, match);
}
public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null)
@ -81,4 +84,3 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -81,4 +84,3 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
}

6
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitMemberType(MemberType memberType);
void VisitTupleType(TupleAstType tupleType);
void VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
void VisitFunctionPointerType(FunctionPointerType functionPointerType);
void VisitFunctionPointerType(FunctionPointerAstType functionPointerType);
void VisitComposedType(ComposedType composedType);
void VisitArraySpecifier(ArraySpecifier arraySpecifier);
void VisitPrimitiveType(PrimitiveType primitiveType);
@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -286,7 +286,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType);
S VisitTupleType(TupleAstType tupleType);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
S VisitFunctionPointerType(FunctionPointerType functionPointerType);
S VisitFunctionPointerType(FunctionPointerAstType functionPointerType);
S VisitComposedType(ComposedType composedType);
S VisitArraySpecifier(ArraySpecifier arraySpecifier);
S VisitPrimitiveType(PrimitiveType primitiveType);
@ -432,7 +432,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -432,7 +432,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType, T data);
S VisitTupleType(TupleAstType tupleType, T data);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data);
S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data);
S VisitFunctionPointerType(FunctionPointerAstType functionPointerType, T data);
S VisitComposedType(ComposedType composedType, T data);
S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data);
S VisitPrimitiveType(PrimitiveType primitiveType, T data);

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

@ -306,6 +306,51 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -306,6 +306,51 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
return astType;
}
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();
paramDecl.ParameterModifier = fpt.ParameterReferenceKinds[i] switch
{
ReferenceKind.In => ParameterModifier.In,
ReferenceKind.Ref => ParameterModifier.Ref,
ReferenceKind.Out => ParameterModifier.Out,
_ => ParameterModifier.None,
};
IType parameterType = fpt.ParameterTypes[i];
if (paramDecl.ParameterModifier != ParameterModifier.None && parameterType is ByReferenceType brt)
{
paramDecl.Type = ConvertType(brt.ElementType);
}
else
{
paramDecl.Type = ConvertType(parameterType);
}
astType.Parameters.Add(paramDecl);
}
astType.ReturnType = ConvertType(fpt.ReturnType);
if (fpt.ReturnIsRefReadOnly && astType.ReturnType is ComposedType ct && ct.HasRefSpecifier)
{
ct.HasReadOnlySpecifier = true;
}
ITypeDefinition treatedAs = fpt.GetDefinition();
if (treatedAs != null)
{
var result = ConvertTypeHelper(treatedAs);
result.AddChild(new Comment(astType.ToString(), CommentType.MultiLine), Roles.Comment);
return result;
}
else
{
return astType;
}
}
else
{
AstType astType;

28
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return base.VisitComposedType(composedType);
}
public override bool VisitFunctionPointerType(FunctionPointerType functionPointerType)
public override bool VisitFunctionPointerType(FunctionPointerAstType functionPointerType)
{
return true;
}
@ -130,12 +130,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -130,12 +130,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var rr = memberReferenceExpression.GetResolveResult();
if (rr != null)
{
if (rr.Type is PointerType)
if (IsPointer(rr.Type))
return true;
if (rr is MemberResolveResult mrr && mrr.Member.ReturnType.Kind == TypeKind.Delegate)
{
var method = mrr.Member.ReturnType.GetDefinition()?.GetDelegateInvokeMethod();
if (method != null && (method.ReturnType is PointerType || method.Parameters.Any(p => p.Type is PointerType)))
if (method != null && (IsPointer(method.ReturnType) || method.Parameters.Any(p => IsPointer(p.Type))))
return true;
}
}
@ -149,12 +149,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -149,12 +149,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var rr = identifierExpression.GetResolveResult();
if (rr != null)
{
if (rr.Type is PointerType)
if (IsPointer(rr.Type))
return true;
if (rr is MemberResolveResult mrr && mrr.Member.ReturnType.Kind == TypeKind.Delegate)
{
var method = mrr.Member.ReturnType.GetDefinition()?.GetDelegateInvokeMethod();
if (method != null && (method.ReturnType is PointerType || method.Parameters.Any(p => p.Type is PointerType)))
if (method != null && (IsPointer(method.ReturnType) || method.Parameters.Any(p => IsPointer(p.Type))))
return true;
}
}
@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
bool result = base.VisitStackAllocExpression(stackAllocExpression);
var rr = stackAllocExpression.GetResolveResult();
if (rr?.Type is PointerType)
if (IsPointer(rr?.Type))
return true;
return result;
}
@ -175,7 +175,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -175,7 +175,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
bool result = base.VisitInvocationExpression(invocationExpression);
var rr = invocationExpression.GetResolveResult();
if (rr != null && rr.Type is PointerType)
if (IsPointer(rr?.Type))
return true;
return result;
}
@ -185,5 +185,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -185,5 +185,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
base.VisitFixedVariableInitializer(fixedVariableInitializer);
return true;
}
private bool IsPointer(IType type)
{
switch (type?.Kind)
{
case TypeKind.Pointer:
case TypeKind.FunctionPointer:
return true;
case TypeKind.ByReference:
return IsPointer(((ByReferenceType)type).ElementType);
default:
return false;
}
}
}
}

26
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -328,7 +328,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -328,7 +328,9 @@ namespace ICSharpCode.Decompiler.CSharp
// Direct cast only works correctly for IntPtr -> long.
// IntPtr -> int works correctly only in checked context.
// Everything else can be worked around by casting via long.
if (!(targetType.IsKnownType(KnownTypeCode.Int64) || targetType.Kind == TypeKind.NInt || checkForOverflow && targetType.IsKnownType(KnownTypeCode.Int32)))
if (!(targetType.IsKnownType(KnownTypeCode.Int64) || targetType.Kind == TypeKind.NInt
|| (checkForOverflow && targetType.IsKnownType(KnownTypeCode.Int32))
|| targetType.Kind.IsAnyPointer()))
{
var convertVia = expressionBuilder.settings.NativeIntegers ? SpecialType.NInt : compilation.FindType(KnownTypeCode.Int64);
return this.ConvertTo(convertVia, expressionBuilder, checkForOverflow)
@ -340,7 +342,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -340,7 +342,9 @@ namespace ICSharpCode.Decompiler.CSharp
// Direct cast only works correctly for UIntPtr -> ulong.
// UIntPtr -> uint works correctly only in checked context.
// Everything else can be worked around by casting via ulong.
if (!(targetType.IsKnownType(KnownTypeCode.UInt64) || targetType.Kind == TypeKind.NUInt || checkForOverflow && targetType.IsKnownType(KnownTypeCode.UInt32)))
if (!(targetType.IsKnownType(KnownTypeCode.UInt64) || targetType.Kind == TypeKind.NUInt
|| (checkForOverflow && targetType.IsKnownType(KnownTypeCode.UInt32))
|| targetType.Kind.IsAnyPointer()))
{
var convertVia = expressionBuilder.settings.NativeIntegers ? SpecialType.NUInt : compilation.FindType(KnownTypeCode.UInt64);
return this.ConvertTo(convertVia, expressionBuilder, checkForOverflow)
@ -370,7 +374,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -370,7 +374,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 +384,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -380,7 +384,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 +413,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -409,22 +413,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,17 +533,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -529,17 +533,17 @@ 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)
{
return new NullReferenceExpression()
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(targetType, null));
.WithRR(new ConstantResolveResult(SpecialType.NullType, null));
}
return new CastExpression(expressionBuilder.ConvertType(targetType), new NullReferenceExpression())
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(targetType, null));
.WithRR(new ConstantResolveResult(SpecialType.NullType, null));
}
if (allowImplicitConversion)
{

22
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -133,12 +133,13 @@ namespace ICSharpCode.Decompiler @@ -133,12 +133,13 @@ namespace ICSharpCode.Decompiler
{
nativeIntegers = false;
initAccessors = false;
functionPointers = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers || initAccessors)
if (nativeIntegers || initAccessors || functionPointers)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions)
@ -204,6 +205,25 @@ namespace ICSharpCode.Decompiler @@ -204,6 +205,25 @@ namespace ICSharpCode.Decompiler
}
}
bool functionPointers = true;
/// <summary>
/// Use C# 9 <c>delegate* unmanaged</c> types.
/// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.FunctionPointers")]
public bool FunctionPointers {
get { return functionPointers; }
set {
if (functionPointers != value)
{
functionPointers = value;
OnPropertyChanged();
}
}
}
bool switchExpressions = true;
/// <summary>

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
<Compile Include="CSharp\ProjectDecompiler\TargetServices.cs" />
<Compile Include="CSharp\Syntax\Expressions\DeclarationExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\SwitchExpression.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerAstType.cs" />
<Compile Include="CSharp\Syntax\VariableDesignation.cs" />
<Compile Include="IL\Transforms\DeconstructionTransform.cs" />
<Compile Include="IL\Transforms\FixLoneIsInst.cs" />
@ -250,6 +250,7 @@ @@ -250,6 +250,7 @@
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="IL\Transforms\FixRemainingIncrements.cs" />
<Compile Include="IL\Transforms\ILExtraction.cs" />
<Compile Include="TypeSystem\FunctionPointerType.cs" />
<Compile Include="TypeSystem\Implementation\LocalFunctionMethod.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />

18
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1566,24 +1566,22 @@ namespace ICSharpCode.Decompiler.IL @@ -1566,24 +1566,22 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction DecodeCallIndirect()
{
var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken();
var signature = module.DecodeMethodSignature(signatureHandle, genericContext);
var (header, fpt) = module.DecodeMethodSignature(signatureHandle, genericContext);
var functionPointer = Pop(StackType.I);
int firstArgument = signature.Header.IsInstance ? 1 : 0;
var arguments = new ILInstruction[firstArgument + signature.ParameterTypes.Length];
for (int i = signature.ParameterTypes.Length - 1; i >= 0; i--)
int firstArgument = header.IsInstance ? 1 : 0;
var arguments = new ILInstruction[firstArgument + fpt.ParameterTypes.Length];
for (int i = fpt.ParameterTypes.Length - 1; i >= 0; i--)
{
arguments[firstArgument + i] = Pop(signature.ParameterTypes[i].GetStackType());
arguments[firstArgument + i] = Pop(fpt.ParameterTypes[i].GetStackType());
}
if (firstArgument == 1)
{
arguments[0] = Pop();
}
var call = new CallIndirect(
signature.Header.IsInstance,
signature.Header.HasExplicitThis,
signature.Header.CallingConvention,
signature.ReturnType,
signature.ParameterTypes,
header.IsInstance,
header.HasExplicitThis,
fpt,
functionPointer,
arguments
);

4
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -16,8 +16,6 @@ @@ -16,8 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
@ -163,7 +161,7 @@ namespace ICSharpCode.Decompiler.IL @@ -163,7 +161,7 @@ namespace ICSharpCode.Decompiler.IL
case CallVirt callVirt:
return callVirt.Method.ReturnType;
case CallIndirect calli:
return calli.ReturnType;
return calli.FunctionPointerType.ReturnType;
case UserDefinedLogicOperator logicOp:
return logicOp.Method.ReturnType;
case LdObj ldobj:

42
ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

@ -16,9 +16,7 @@ @@ -16,9 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
@ -37,17 +35,8 @@ namespace ICSharpCode.Decompiler.IL @@ -37,17 +35,8 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<ILInstruction> Arguments;
public bool IsInstance { get; }
public bool HasExplicitThis { get; }
public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; }
public IType ReturnType { get; }
public ImmutableArray<IType> ParameterTypes { get; }
/// <summary>
/// A 'final instruction' that gets executed after the <c>Instructions</c> collection.
/// Provides the return value for the block.
/// </summary>
/// <remarks>
/// Blocks in containers must have 'Nop' as a final instruction.
/// </remarks>
public FunctionPointerType FunctionPointerType { get; }
public ILInstruction FunctionPointer {
get {
return functionPointer;
@ -58,14 +47,12 @@ namespace ICSharpCode.Decompiler.IL @@ -58,14 +47,12 @@ namespace ICSharpCode.Decompiler.IL
}
}
public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray<IType> parameterTypes,
public CallIndirect(bool isInstance, bool hasExplicitThis, FunctionPointerType functionPointerType,
ILInstruction functionPointer, IEnumerable<ILInstruction> arguments) : base(OpCode.CallIndirect)
{
this.IsInstance = isInstance;
this.HasExplicitThis = hasExplicitThis;
this.CallingConvention = callingConvention;
this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType));
this.ParameterTypes = parameterTypes.ToImmutableArray();
this.FunctionPointerType = functionPointerType;
this.FunctionPointer = functionPointer;
this.Arguments = new InstructionCollection<ILInstruction>(this, 1);
this.Arguments.AddRange(arguments);
@ -73,24 +60,24 @@ namespace ICSharpCode.Decompiler.IL @@ -73,24 +60,24 @@ namespace ICSharpCode.Decompiler.IL
public override ILInstruction Clone()
{
return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes,
return new CallIndirect(IsInstance, HasExplicitThis, FunctionPointerType,
functionPointer.Clone(), this.Arguments.Select(inst => inst.Clone())
).WithILRange(this);
}
public override StackType ResultType => ReturnType.GetStackType();
public override StackType ResultType => FunctionPointerType.ReturnType.GetStackType();
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Arguments.Count == ParameterTypes.Length + (IsInstance ? 1 : 0));
Debug.Assert(Arguments.Count == FunctionPointerType.ParameterTypes.Length + (IsInstance ? 1 : 0));
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
WriteILRange(output, options);
output.Write("call.indirect ");
ReturnType.WriteTo(output);
FunctionPointerType.ReturnType.WriteTo(output);
output.Write('(');
functionPointer.WriteTo(output, options);
int firstArgument = IsInstance ? 1 : 0;
@ -99,7 +86,7 @@ namespace ICSharpCode.Decompiler.IL @@ -99,7 +86,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(", ");
Arguments[0].WriteTo(output, options);
}
foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a, b) => (a, b)))
foreach (var (inst, type) in Arguments.Zip(FunctionPointerType.ParameterTypes, (a, b) => (a, b)))
{
output.Write(", ");
inst.WriteTo(output, options);
@ -161,16 +148,7 @@ namespace ICSharpCode.Decompiler.IL @@ -161,16 +148,7 @@ namespace ICSharpCode.Decompiler.IL
return false;
if (HasExplicitThis != other.HasExplicitThis)
return false;
if (CallingConvention != other.CallingConvention)
return false;
if (ParameterTypes.Length != other.ParameterTypes.Length)
return false;
for (int i = 0; i < ParameterTypes.Length; i++)
{
if (!ParameterTypes[i].Equals(other.ParameterTypes[i]))
return false;
}
return ReturnType.Equals(other.ReturnType);
return FunctionPointerType.Equals(other.FunctionPointerType);
}
}
}

4
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -459,6 +459,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -459,6 +459,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return ldloc.Variable.IsRefReadOnly;
case Call call:
return call.Method.ReturnTypeIsRefReadOnly;
case CallIndirect calli:
return calli.FunctionPointerType.ReturnIsRefReadOnly;
case AddressOf _:
// C# doesn't allow mutation of value-type temporaries
return true;
@ -557,6 +559,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -557,6 +559,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
break;
case OpCode.CallIndirect when loadInst.SlotInfo == CallIndirect.FunctionPointerSlot:
return true;
case OpCode.LdElema:
if (((LdElema)parent).WithSystemIndex)
{

39
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

@ -40,7 +40,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,7 +40,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
SRM.MetadataReader metadata,
TypeSystemOptions options,
Nullability nullableContext,
bool typeChildrenOnly = false)
bool typeChildrenOnly = false,
bool isSignatureReturnType = false)
{
bool hasDynamicAttribute = false;
bool[] dynamicAttributeData = null;
@ -133,6 +134,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -133,6 +134,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
options, tupleElementNames,
nullability, nullableAttributeData
);
if (isSignatureReturnType && hasDynamicAttribute
&& inputType.SkipModifiers().Kind == TypeKind.ByReference
&& attributes.Value.HasKnownAttribute(metadata, KnownAttribute.IsReadOnly))
{
// crazy special case: `ref readonly` return takes one dynamic index more than
// a non-readonly `ref` return.
visitor.dynamicTypeIndex++;
}
if (typeChildrenOnly)
{
return inputType.VisitChildren(visitor);
@ -309,6 +318,34 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -309,6 +318,34 @@ namespace ICSharpCode.Decompiler.TypeSystem
return new ParameterizedType(genericType, arguments);
}
public override IType VisitFunctionPointerType(FunctionPointerType type)
{
dynamicTypeIndex++;
if (type.ReturnIsRefReadOnly)
{
dynamicTypeIndex++;
}
var returnType = type.ReturnType.AcceptVisitor(this);
bool changed = type.ReturnType != returnType;
var parameters = new IType[type.ParameterTypes.Length];
for (int i = 0; i < parameters.Length; i++)
{
dynamicTypeIndex += type.ParameterReferenceKinds[i] switch
{
ReferenceKind.None => 1,
ReferenceKind.Ref => 1,
ReferenceKind.Out => 2, // in/out also count the modreq
ReferenceKind.In => 2,
_ => throw new NotSupportedException()
};
parameters[i] = type.ParameterTypes[i].AcceptVisitor(this);
changed = changed || parameters[i] != type.ParameterTypes[i];
}
if (!changed)
return type;
return type.WithSignature(returnType, parameters.ToImmutableArray());
}
public override IType VisitTypeDefinition(ITypeDefinition type)
{
IType newType = type;

11
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -18,8 +18,6 @@ @@ -18,8 +18,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.Metadata;
@ -116,11 +114,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -116,11 +114,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
NativeIntegers = 0x1000,
/// <summary>
/// Allow function pointer types. If this option is not enabled, function pointers are
/// replaced with the 'IntPtr' type.
/// </summary>
FunctionPointers = 0x2000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers
| NativeIntegers | FunctionPointers
}
/// <summary>
@ -154,6 +157,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -154,6 +157,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.ReadOnlyMethods;
if (settings.NativeIntegers)
typeSystemOptions |= TypeSystemOptions.NativeIntegers;
if (settings.FunctionPointers)
typeSystemOptions |= TypeSystemOptions.FunctionPointers;
return typeSystemOptions;
}

185
ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs

@ -0,0 +1,185 @@ @@ -0,0 +1,185 @@
// Copyright (c) 2020 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem
{
public class FunctionPointerType : AbstractType
{
public static FunctionPointerType FromSignature(MethodSignature<IType> signature, MetadataModule module)
{
IType returnType = signature.ReturnType;
bool returnIsRefReadOnly = false;
if (returnType is ModifiedType modReturn && modReturn.Modifier.IsKnownType(KnownAttribute.In))
{
returnType = modReturn.ElementType;
returnIsRefReadOnly = true;
}
var parameterTypes = ImmutableArray.CreateBuilder<IType>(signature.ParameterTypes.Length);
var parameterReferenceKinds = ImmutableArray.CreateBuilder<ReferenceKind>(signature.ParameterTypes.Length);
foreach (var p in signature.ParameterTypes)
{
IType paramType = p;
ReferenceKind kind = ReferenceKind.None;
if (p is ModifiedType modreq)
{
if (modreq.Modifier.IsKnownType(KnownAttribute.In))
{
kind = ReferenceKind.In;
paramType = modreq.ElementType;
}
else if (modreq.Modifier.IsKnownType(KnownAttribute.Out))
{
kind = ReferenceKind.Out;
paramType = modreq.ElementType;
}
}
if (paramType.Kind == TypeKind.ByReference)
{
if (kind == ReferenceKind.None)
kind = ReferenceKind.Ref;
}
else
{
kind = ReferenceKind.None;
}
parameterTypes.Add(paramType);
parameterReferenceKinds.Add(kind);
}
return new FunctionPointerType(
module, signature.Header.CallingConvention,
returnType, returnIsRefReadOnly,
parameterTypes.MoveToImmutable(), parameterReferenceKinds.MoveToImmutable());
}
private readonly MetadataModule module;
public readonly SignatureCallingConvention CallingConvention;
public readonly IType ReturnType;
public readonly bool ReturnIsRefReadOnly;
public readonly ImmutableArray<IType> ParameterTypes;
public readonly ImmutableArray<ReferenceKind> ParameterReferenceKinds;
public FunctionPointerType(MetadataModule module, SignatureCallingConvention callingConvention,
IType returnType, bool returnIsRefReadOnly,
ImmutableArray<IType> parameterTypes, ImmutableArray<ReferenceKind> parameterReferenceKinds)
{
this.module = module;
this.CallingConvention = callingConvention;
this.ReturnType = returnType;
this.ReturnIsRefReadOnly = returnIsRefReadOnly;
this.ParameterTypes = parameterTypes;
this.ParameterReferenceKinds = parameterReferenceKinds;
Debug.Assert(parameterTypes.Length == parameterReferenceKinds.Length);
}
public override string Name => "delegate*";
public override bool? IsReferenceType => false;
public override TypeKind Kind => ((module.TypeSystemOptions & TypeSystemOptions.FunctionPointers) != 0) ? TypeKind.FunctionPointer : TypeKind.Struct;
public override ITypeDefinition GetDefinition()
{
if ((module.TypeSystemOptions & TypeSystemOptions.FunctionPointers) != 0)
{
return null;
}
else
{
// If FunctionPointers are not enabled in the TS, we still use FunctionPointerType instances;
// but have them act as if they were aliases for UIntPtr.
return module.Compilation.FindType(KnownTypeCode.UIntPtr).GetDefinition();
}
}
public override IType AcceptVisitor(TypeVisitor visitor)
{
return visitor.VisitFunctionPointerType(this);
}
public override IType VisitChildren(TypeVisitor visitor)
{
IType r = ReturnType.AcceptVisitor(visitor);
// Keep ta == null as long as no elements changed, allocate the array only if necessary.
IType[] pt = (r != ReturnType) ? new IType[ParameterTypes.Length] : null;
for (int i = 0; i < ParameterTypes.Length; i++)
{
IType p = ParameterTypes[i].AcceptVisitor(visitor);
if (p == null)
throw new NullReferenceException("TypeVisitor.Visit-method returned null");
if (pt == null && p != ParameterTypes[i])
{
// we found a difference, so we need to allocate the array
pt = new IType[ParameterTypes.Length];
for (int j = 0; j < i; j++)
{
pt[j] = ParameterTypes[j];
}
}
if (pt != null)
pt[i] = p;
}
if (pt == null)
return this;
else
return new FunctionPointerType(
module, CallingConvention,
r, ReturnIsRefReadOnly,
pt != null ? pt.ToImmutableArray() : ParameterTypes,
ParameterReferenceKinds);
}
public override bool Equals(IType other)
{
return other is FunctionPointerType fpt
&& CallingConvention == fpt.CallingConvention
&& ReturnType.Equals(fpt.ReturnType)
&& ReturnIsRefReadOnly == fpt.ReturnIsRefReadOnly
&& ParameterTypes.SequenceEqual(fpt.ParameterTypes)
&& ParameterReferenceKinds.SequenceEqual(fpt.ParameterReferenceKinds);
}
public override int GetHashCode()
{
unchecked
{
int hash = ReturnType.GetHashCode();
foreach (var (p, k) in ParameterTypes.Zip(ParameterReferenceKinds))
{
hash ^= p.GetHashCode() ^ k.GetHashCode();
hash *= 8310859;
}
return hash;
}
}
internal IType WithSignature(IType returnType, ImmutableArray<IType> parameterTypes)
{
return new FunctionPointerType(this.module, this.CallingConvention, returnType,
this.ReturnIsRefReadOnly, parameterTypes, this.ParameterReferenceKinds);
}
}
}

6
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -185,7 +185,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -185,7 +185,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext);
(returnType, parameters, mod) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
(returnType, parameters, mod) = DecodeSignature(module, this, signature,
methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
}
catch (BadImageFormatException)
{
@ -263,7 +264,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -263,7 +264,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
Debug.Assert(i == parameters.Length);
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext);
module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext,
isSignatureReturnType: true);
return (returnType, parameters, signature.ReturnType as ModifiedType);
}
#endregion

13
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -689,21 +689,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -689,21 +689,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
#endregion
#region Decode Standalone Signature
public MethodSignature<IType> DecodeMethodSignature(StandaloneSignatureHandle handle, GenericContext genericContext)
public (SignatureHeader, FunctionPointerType) DecodeMethodSignature(StandaloneSignatureHandle handle, GenericContext genericContext)
{
var standaloneSignature = metadata.GetStandaloneSignature(handle);
if (standaloneSignature.GetKind() != StandaloneSignatureKind.Method)
throw new BadImageFormatException("Expected Method signature");
var sig = standaloneSignature.DecodeMethodSignature(TypeProvider, genericContext);
return new MethodSignature<IType>(
sig.Header,
IntroduceTupleTypes(sig.ReturnType),
sig.RequiredParameterCount,
sig.GenericParameterCount,
ImmutableArray.CreateRange(
sig.ParameterTypes, IntroduceTupleTypes
)
);
var fpt = FunctionPointerType.FromSignature(sig, this);
return (sig.Header, (FunctionPointerType)IntroduceTupleTypes(fpt));
}
public ImmutableArray<IType> DecodeLocalSignature(StandaloneSignatureHandle handle, GenericContext genericContext)

5
ICSharpCode.Decompiler/TypeSystem/TypeKind.cs

@ -103,5 +103,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -103,5 +103,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// C# 9 <c>nuint</c>
/// </summary>
NUInt,
/// <summary>
/// C# 9 <c>delegate*</c>
/// </summary>
FunctionPointer,
}
}

7
ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs

@ -62,7 +62,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -62,7 +62,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
public IType GetFunctionPointerType(SRM.MethodSignature<IType> signature)
{
return compilation.FindType(KnownTypeCode.IntPtr);
if (signature.Header.IsInstance)
{
// pointers to member functions are not supported even in C# 9
return compilation.FindType(KnownTypeCode.IntPtr);
}
return FunctionPointerType.FromSignature(signature, module);
}
public IType GetGenericInstantiation(IType genericType, ImmutableArray<IType> typeArguments)

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.

3
ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

@ -273,6 +273,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -273,6 +273,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
case TypeKind.Pointer:
case TypeKind.NInt:
case TypeKind.NUInt:
case TypeKind.FunctionPointer:
return StackType.I;
case TypeKind.TypeParameter:
// Type parameters are always considered StackType.O, even
@ -340,6 +341,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -340,6 +341,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
case TypeKind.Pointer:
case TypeKind.NUInt:
case TypeKind.FunctionPointer:
return Sign.Unsigned;
case TypeKind.NInt:
return Sign.Signed;
@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
case TypeKind.ByReference:
return PrimitiveType.Ref;
case TypeKind.NInt:
case TypeKind.FunctionPointer:
return PrimitiveType.I;
case TypeKind.NUInt:
return PrimitiveType.U;

7
ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs

@ -16,8 +16,6 @@ @@ -16,8 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.TypeSystem
@ -81,5 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -81,5 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return type.VisitChildren(this);
}
public virtual IType VisitFunctionPointerType(FunctionPointerType type)
{
return type.VisitChildren(this);
}
}
}

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.

13
ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs

@ -16,13 +16,9 @@ @@ -16,13 +16,9 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
@ -266,13 +262,8 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -266,13 +262,8 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
break;
case HandleKind.StandaloneSignature:
var signature = module.DecodeMethodSignature((StandaloneSignatureHandle)member, genericContext);
foreach (var type in signature.ParameterTypes)
{
type.AcceptVisitor(visitor);
}
signature.ReturnType.AcceptVisitor(visitor);
var (_, fpt) = module.DecodeMethodSignature((StandaloneSignatureHandle)member, genericContext);
fpt.AcceptVisitor(visitor);
if (visitor.Found)
return;

19
ILSpy/Languages/Language.cs

@ -254,6 +254,25 @@ namespace ICSharpCode.ILSpy @@ -254,6 +254,25 @@ namespace ICSharpCode.ILSpy
return type;
}
public override IType VisitFunctionPointerType(FunctionPointerType type)
{
builder.Append("method ");
type.ReturnType.AcceptVisitor(this);
builder.Append(" *(");
bool first = true;
foreach (var p in type.ParameterTypes)
{
if (first)
first = false;
else
builder.Append(", ");
p.AcceptVisitor(this);
}
builder.Append(')');
return type;
}
public override IType VisitOtherType(IType type)
{
WriteType(type);

9
ILSpy/Properties/Resources.Designer.cs generated

@ -909,6 +909,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -909,6 +909,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Function pointers.
/// </summary>
public static string DecompilerSettings_FunctionPointers {
get {
return ResourceManager.GetString("DecompilerSettings.FunctionPointers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Include XML documentation comments in the decompiled code.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -333,6 +333,9 @@ Are you sure you want to continue?</value> @@ -333,6 +333,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.ForStatement" xml:space="preserve">
<value>Transform to for, if possible</value>
</data>
<data name="DecompilerSettings.FunctionPointers" xml:space="preserve">
<value>Function pointers</value>
</data>
<data name="DecompilerSettings.IncludeXMLDocumentationCommentsInTheDecompiledCode" xml:space="preserve">
<value>Include XML documentation comments in the decompiled code</value>
</data>

Loading…
Cancel
Save