diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs index a84c50cc7..39e03712a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/FunctionPointers.cs @@ -47,6 +47,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty internal class FunctionPointersWithRefParams { public unsafe delegate* F1; + public unsafe delegate* 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 diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c227aa2c2..899757466 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3888,25 +3888,36 @@ namespace ICSharpCode.Decompiler.CSharp { return ErrorExpression("calli with instance method signature not supportd"); } - var ty = new FunctionPointerAstType(); - if (inst.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default) + + var functionPointer = Translate(inst.FunctionPointer, typeHint: inst.FunctionPointerType); + if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(functionPointer.Type, inst.FunctionPointerType)) + { + functionPointer = functionPointer.ConvertTo(inst.FunctionPointerType, this); + } + 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.CallingConvention = inst.CallingConvention.ToString().ToLowerInvariant(); + var arg = Translate(argInst, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true); + if (paramRefKind != ReferenceKind.None) + { + arg = ChangeDirectionExpressionTo(arg, paramRefKind); + } + invocation.Arguments.Add(arg); } - foreach (var parameterType in inst.ParameterTypes) + if (fpt.ReturnType.SkipModifiers() is ByReferenceType brt) { - ty.Parameters.Add(new ParameterDeclaration { - Type = astBuilder.ConvertType(parameterType) - }); + var rr = new ResolveResult(brt.ElementType); + return new DirectionExpression( + FieldDirection.Ref, + invocation.WithRR(rr).WithILInstruction(inst) + ).WithRR(new ByReferenceResolveResult(rr, ReferenceKind.Ref)).WithoutILInstruction(); } - 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)) + 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) diff --git a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs index 3f3a363de..b8428fa7f 100644 --- a/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs +++ b/ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs @@ -341,20 +341,16 @@ namespace ICSharpCode.Decompiler.CSharp } if (sig.GetKind() == StandaloneSignatureKind.Method) { - MethodSignature 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; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 52ee0cf71..66c3df0cb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -311,23 +311,41 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax 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]) - }); + 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; } - return astType; + 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 { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs index a94eee76e..d814221f2 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs @@ -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 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 { 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 { 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 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; + } + } } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 2f015c77e..d4d2498a8 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -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 ); diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index fef012cac..feac446f4 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -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 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: diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index c4da53a0c..a12592d06 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -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 public readonly InstructionCollection Arguments; public bool IsInstance { get; } public bool HasExplicitThis { get; } - public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; } - public IType ReturnType { get; } - public ImmutableArray ParameterTypes { get; } - - /// - /// A 'final instruction' that gets executed after the Instructions collection. - /// Provides the return value for the block. - /// - /// - /// Blocks in containers must have 'Nop' as a final instruction. - /// + public FunctionPointerType FunctionPointerType { get; } + public ILInstruction FunctionPointer { get { return functionPointer; @@ -58,14 +47,12 @@ namespace ICSharpCode.Decompiler.IL } } - public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, + public CallIndirect(bool isInstance, bool hasExplicitThis, FunctionPointerType functionPointerType, ILInstruction functionPointer, IEnumerable 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(this, 1); this.Arguments.AddRange(arguments); @@ -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 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 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); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index f505a04d3..ca144927e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -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 return true; } break; + case OpCode.CallIndirect when loadInst.SlotInfo == CallIndirect.FunctionPointerSlot: + return true; case OpCode.LdElema: if (((LdElema)parent).WithSystemIndex) { diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index cbf8771cf..fee4a6d2d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -333,9 +333,9 @@ namespace ICSharpCode.Decompiler.TypeSystem dynamicTypeIndex += type.ParameterReferenceKinds[i] switch { ReferenceKind.None => 1, - ReferenceKind.Ref => 2, - ReferenceKind.Out => 3, - ReferenceKind.In => 3, + 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); @@ -343,9 +343,7 @@ namespace ICSharpCode.Decompiler.TypeSystem } if (!changed) return type; - return new FunctionPointerType(type.CallingConvention, - returnType, type.ReturnIsRefReadOnly, - parameters.ToImmutableArray(), type.ParameterReferenceKinds); + return type.WithSignature(returnType, parameters.ToImmutableArray()); } public override IType VisitTypeDefinition(ITypeDefinition type) diff --git a/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs b/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs index 866d81202..36ba05bd7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/FunctionPointerType.cs @@ -23,21 +23,70 @@ 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 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(signature.ParameterTypes.Length); + var parameterReferenceKinds = ImmutableArray.CreateBuilder(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 ParameterTypes; public readonly ImmutableArray ParameterReferenceKinds; - public FunctionPointerType(SignatureCallingConvention callingConvention, + public FunctionPointerType(MetadataModule module, SignatureCallingConvention callingConvention, IType returnType, bool returnIsRefReadOnly, ImmutableArray parameterTypes, ImmutableArray parameterReferenceKinds) { + this.module = module; this.CallingConvention = callingConvention; this.ReturnType = returnType; this.ReturnIsRefReadOnly = returnIsRefReadOnly; @@ -50,7 +99,21 @@ namespace ICSharpCode.Decompiler.TypeSystem public override bool? IsReferenceType => false; - public override TypeKind Kind => TypeKind.FunctionPointer; + 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) { @@ -82,7 +145,8 @@ namespace ICSharpCode.Decompiler.TypeSystem if (pt == null) return this; else - return new FunctionPointerType(CallingConvention, + return new FunctionPointerType( + module, CallingConvention, r, ReturnIsRefReadOnly, pt != null ? pt.ToImmutableArray() : ParameterTypes, ParameterReferenceKinds); @@ -90,7 +154,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public override bool Equals(IType other) { - return other is FunctionPointerType fpt && ReturnType.Equals(fpt.ReturnType) + return other is FunctionPointerType fpt + && CallingConvention == fpt.CallingConvention + && ReturnType.Equals(fpt.ReturnType) && ReturnIsRefReadOnly == fpt.ReturnIsRefReadOnly && ParameterTypes.SequenceEqual(fpt.ParameterTypes) && ParameterReferenceKinds.SequenceEqual(fpt.ParameterReferenceKinds); @@ -101,13 +167,19 @@ namespace ICSharpCode.Decompiler.TypeSystem unchecked { int hash = ReturnType.GetHashCode(); - foreach (var p in ParameterTypes) + foreach (var (p, k) in ParameterTypes.Zip(ParameterReferenceKinds)) { - hash ^= p.GetHashCode(); + hash ^= p.GetHashCode() ^ k.GetHashCode(); hash *= 8310859; } return hash; } } + + internal IType WithSignature(IType returnType, ImmutableArray parameterTypes) + { + return new FunctionPointerType(this.module, this.CallingConvention, returnType, + this.ReturnIsRefReadOnly, parameterTypes, this.ParameterReferenceKinds); + } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index 313d12a14..e02d19f2c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -689,21 +689,14 @@ namespace ICSharpCode.Decompiler.TypeSystem #endregion #region Decode Standalone Signature - public MethodSignature 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( - 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 DecodeLocalSignature(StandaloneSignatureHandle handle, GenericContext genericContext) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index 27ba4bfca..5ac4b1c50 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -62,55 +62,12 @@ namespace ICSharpCode.Decompiler.TypeSystem public IType GetFunctionPointerType(SRM.MethodSignature signature) { - 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(signature.ParameterTypes.Length); - var parameterReferenceKinds = ImmutableArray.CreateBuilder(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()); + return FunctionPointerType.FromSignature(signature, module); } public IType GetGenericInstantiation(IType genericType, ImmutableArray typeArguments) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs index 3d2856c34..d2176704a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs @@ -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 { 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 case TypeKind.ByReference: return PrimitiveType.Ref; case TypeKind.NInt: + case TypeKind.FunctionPointer: return PrimitiveType.I; case TypeKind.NUInt: return PrimitiveType.U; diff --git a/ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs b/ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs index c710921e2..66ae80887 100644 --- a/ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs +++ b/ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs @@ -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 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;