Browse Source

Add support for decoding function pointer types in signatures.

pull/2150/head
Daniel Grunwald 5 years ago
parent
commit
cabb02b5fd
  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. 59
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs
  5. 10
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  6. 11
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  7. 6
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  8. 20
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerAstType.cs
  9. 6
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  10. 23
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  11. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  12. 22
      ICSharpCode.Decompiler/DecompilerSettings.cs
  13. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  14. 41
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  15. 11
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  16. 113
      ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs
  17. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  18. 5
      ICSharpCode.Decompiler/TypeSystem/TypeKind.cs
  19. 50
      ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs
  20. 7
      ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs
  21. 19
      ILSpy/Languages/Language.cs
  22. 9
      ILSpy/Properties/Resources.Designer.cs
  23. 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

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

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
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;
}
// 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;
//}
}

10
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
@ -3888,16 +3888,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3888,16 +3888,18 @@ namespace ICSharpCode.Decompiler.CSharp
{
return ErrorExpression("calli with instance method signature not supportd");
}
var ty = new FunctionPointerType();
var ty = new FunctionPointerAstType();
if (inst.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default)
{
ty.CallingConvention = inst.CallingConvention.ToString().ToLowerInvariant();
}
foreach (var parameterType in inst.ParameterTypes)
{
ty.TypeArguments.Add(astBuilder.ConvertType(parameterType));
ty.Parameters.Add(new ParameterDeclaration {
Type = astBuilder.ConvertType(parameterType)
});
}
ty.TypeArguments.Add(astBuilder.ConvertType(inst.ReturnType));
ty.ReturnType = 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))

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

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

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

@ -306,6 +306,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -306,6 +306,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
return astType;
}
else if (type is FunctionPointerType fpt)
{
var astType = new FunctionPointerAstType();
for (int i = 0; i < fpt.ParameterTypes.Length; i++)
{
astType.Parameters.Add(new ParameterDeclaration {
ParameterModifier = fpt.ParameterReferenceKinds[i] switch
{
ReferenceKind.In => ParameterModifier.In,
ReferenceKind.Ref => ParameterModifier.Ref,
ReferenceKind.Out => ParameterModifier.Out,
_ => ParameterModifier.None,
},
Type = ConvertType(fpt.ParameterTypes[i])
});
}
astType.ReturnType = ConvertType(fpt.ReturnType);
if (fpt.ReturnIsRefReadOnly && astType.ReturnType is ComposedType ct && ct.HasRefSpecifier)
{
ct.HasReadOnlySpecifier = true;
}
return astType;
}
else
{
AstType astType;

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

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" />

41
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,36 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -309,6 +318,36 @@ 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 => 2,
ReferenceKind.Out => 3,
ReferenceKind.In => 3,
_ => throw new NotSupportedException()
};
parameters[i] = type.ParameterTypes[i].AcceptVisitor(this);
changed = changed || parameters[i] != type.ParameterTypes[i];
}
if (!changed)
return type;
return new FunctionPointerType(type.CallingConvention,
returnType, type.ReturnIsRefReadOnly,
parameters.ToImmutableArray(), type.ParameterReferenceKinds);
}
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;
}

113
ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
// 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;
namespace ICSharpCode.Decompiler.TypeSystem
{
public class FunctionPointerType : AbstractType
{
public readonly SignatureCallingConvention CallingConvention;
public readonly IType ReturnType;
public readonly bool ReturnIsRefReadOnly;
public readonly ImmutableArray<IType> ParameterTypes;
public readonly ImmutableArray<ReferenceKind> ParameterReferenceKinds;
public FunctionPointerType(SignatureCallingConvention callingConvention,
IType returnType, bool returnIsRefReadOnly,
ImmutableArray<IType> parameterTypes, ImmutableArray<ReferenceKind> parameterReferenceKinds)
{
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 => TypeKind.FunctionPointer;
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(CallingConvention,
r, ReturnIsRefReadOnly,
pt != null ? pt.ToImmutableArray() : ParameterTypes,
ParameterReferenceKinds);
}
public override bool Equals(IType other)
{
return other is FunctionPointerType fpt && 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 in ParameterTypes)
{
hash ^= p.GetHashCode();
hash *= 8310859;
}
return hash;
}
}
}
}

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

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

50
ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs

@ -62,7 +62,55 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -62,7 +62,55 @@ namespace ICSharpCode.Decompiler.TypeSystem
public IType GetFunctionPointerType(SRM.MethodSignature<IType> signature)
{
return compilation.FindType(KnownTypeCode.IntPtr);
if ((module.TypeSystemOptions & TypeSystemOptions.FunctionPointers) == 0)
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);
}
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 is ByReferenceType brt)
{
paramType = brt.ElementType;
if (kind == ReferenceKind.None)
kind = ReferenceKind.Ref;
}
else
{
kind = ReferenceKind.None;
}
parameterTypes.Add(paramType);
parameterReferenceKinds.Add(kind);
}
return new FunctionPointerType(signature.Header.CallingConvention,
returnType, returnIsRefReadOnly,
parameterTypes.MoveToImmutable(), parameterReferenceKinds.MoveToImmutable());
}
public IType GetGenericInstantiation(IType genericType, ImmutableArray<IType> typeArguments)

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

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