diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 51d24fc07..51ed72689 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -89,6 +89,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 6c66f55c3..e7786ac67 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -425,6 +425,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void Discards([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs index 83b609b2f..437c37e54 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs @@ -26,6 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { InterestingConstants(); TruncatedComp(); + StringConcat(); } static void Print(T val) @@ -92,5 +93,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Print(val1 <= val2); Print((int)val1 <= val2); } + + static void StringConcat() + { + // Some string.Concat()-cases that cannot be replaced using operator+ + Print(string.Concat("String concat:")); + Print(string.Concat(1, 2)); + Print(string.Concat(1, 2, "str")); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 1a748024d..3fd129fa1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -159,6 +159,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + public static Predicate And(this Predicate filter1, Predicate filter2) + { + if (filter1 == null) { + return filter2; + } + if (filter2 == null) { + return filter1; + } + return (T m) => filter1(m) && filter2(m); + } + public static Action ExtensionMethodUnbound() { return Test; @@ -174,6 +185,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ((string)null).Test; } + public static Predicate NoExtensionMethodOnLambda() + { + return And((int x) => x >= 0, (int x) => x <= 100); + } + public static object StaticMethod() { return new Func(ExtensionMethodBound); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs index 9620accb5..8ca021f08 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs @@ -32,11 +32,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty get; set; } + public static int StaticProperty { get; set; } + public bool BoolProperty { + get; + set; + } + public void SimpleInlineWithLocals() { int index; @@ -136,5 +142,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return InstanceProperty = GetIndex(); } + + public bool BoolPropertyTest(object x) + { + return BoolProperty = (x != null); + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs new file mode 100644 index 000000000..a91ca7a82 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2019 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. + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class T01Issue1574 + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + } + + private C ChainedConversion() + { + return (bool)default(A); + } + + public void Call_Overloaded() + { + Overloaded((bool)default(A)); + } + + private void Overloaded(A a) + { + } + + private void Overloaded(bool a) + { + } + } + + internal class T02BothDirectAndChainedConversionPossible + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + + public static implicit operator C(A a) + { + return default(C); + } + + public static bool operator ==(C a, C b) + { + return true; + } + public static bool operator !=(C a, C b) + { + return false; + } + } + + private C DirectConvert(A a) + { + return a; + } + + private C IndirectConvert(A a) + { + return (bool)a; + } + + private C? LiftedDirectConvert(A? a) + { + return a; + } + + private C? LiftedIndirectConvert(A? a) + { + return (bool?)a; + } + + private bool Compare(A a, C c) + { + return a == c; + } + + private void LiftedCompare(A? a, C? c) + { + UseBool(a == c); + UseBool(a == default(C)); + UseBool(c == default(A)); + } + + private void UseBool(bool b) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 064da15f2..ea813e554 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -240,4 +240,18 @@ namespace ICSharpCode.Decompiler.CSharp this.Leave = leave; } } + + /// + /// Annotates an expression when an implicit user-defined conversion was omitted. + /// + public class ImplicitConversionAnnotation + { + public readonly ConversionResolveResult ConversionResolveResult; + public IType TargetType => ConversionResolveResult.Type; + + public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult) + { + this.ConversionResolveResult = conversionResolveResult; + } + } } diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 7ca971359..c674d8589 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -360,7 +360,7 @@ namespace ICSharpCode.Decompiler.CSharp // settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods. if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) { var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First(); - var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression); + var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach()); methodName = interfaceMember.Name; targetExpr = new MemberReferenceExpression(castExpression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; @@ -1008,11 +1008,14 @@ namespace ICSharpCode.Decompiler.CSharp } else if (method.IsOperator) { IEnumerable operatorCandidates; if (arguments.Count == 1) { - operatorCandidates = resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name); + IType argType = NullableType.GetUnderlyingType(arguments[0].Type); + operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name); } else if (arguments.Count == 2) { + IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type); + IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type); var hashSet = new HashSet(); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name)); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[1].Type, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name)); operatorCandidates = hashSet; } else { operatorCandidates = EmptyList.Instance; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 54d9b6a58..e3bd8d7fc 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -162,6 +162,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) { + if (target is LambdaResolveResult) + return false; var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; if (rr == null) return false; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 7aea2a4a8..0486ce871 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -154,16 +154,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (arguments.Length < 2) return false; - bool valid = false; - - foreach (var argument in arguments) { - if (argument is NamedArgumentExpression) - return false; - if (argument.GetResolveResult().Type.IsKnownType(KnownTypeCode.String)) - valid = true; - } - - return valid; + return !arguments.Any(arg => arg is NamedArgumentExpression) && + (arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || + arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String)); } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index e0d3dbae6..e270f5563 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -191,7 +191,11 @@ namespace ICSharpCode.Decompiler.CSharp conversion.Input.Type, type, targetType )) { - return this.UnwrapChild(cast.Expression); + var result = this.UnwrapChild(cast.Expression); + if (conversion.Conversion.IsUserDefined) { + result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion)); + } + return result; } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) { return this.UnwrapChild(oce.Arguments.Single()); @@ -211,6 +215,18 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } + var convAnnotation = this.Expression.Annotation(); + if (convAnnotation != null) { + // If an implicit user-defined conversion was stripped from this expression; + // it needs to be re-introduced before we can apply other casts to this expression. + // This happens when the CallBuilder discovers that the conversion is necessary in + // order to choose the correct overload. + this.Expression.RemoveAnnotations(); + return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression) + .WithoutILInstruction() + .WithRR(convAnnotation.ConversionResolveResult) + .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); + } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) { @@ -231,8 +247,9 @@ namespace ICSharpCode.Decompiler.CSharp } var compilation = expressionBuilder.compilation; var conversions = Resolver.CSharpConversions.Get(compilation); - if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 && - CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) + if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 + && !conv.Conversion.IsUserDefined + && CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) { var unwrapped = this.UnwrapChild(cast2.Expression); if (allowImplicitConversion) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 2c3c8b654..552f2468d 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1401,12 +1401,17 @@ namespace ICSharpCode.Decompiler.IL var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); var signature = module.DecodeMethodSignature(signatureHandle, genericContext); var functionPointer = Pop(StackType.I); - Debug.Assert(!signature.Header.IsInstance); - var arguments = new ILInstruction[signature.ParameterTypes.Length]; + 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--) { - arguments[i] = Pop(signature.ParameterTypes[i].GetStackType()); + arguments[firstArgument + i] = Pop(signature.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, diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 3e3134d71..d1aab3e59 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -133,14 +133,7 @@ namespace ICSharpCode.Decompiler.IL case HandleKind.MethodDefinition: { var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); methodSignature = md.DecodeSignature(new DisassemblerSignatureProvider(module, output), new Metadata.GenericContext((MethodDefinitionHandle)entity, module)); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = md.GetDeclaringType(); @@ -189,13 +182,7 @@ namespace ICSharpCode.Decompiler.IL } output.Write('>'); } - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; } case HandleKind.MemberReference: @@ -204,28 +191,13 @@ namespace ICSharpCode.Decompiler.IL switch (mr.GetKind()) { case MemberReferenceKind.Method: methodSignature = mr.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, mr.Parent, genericContext, syntax); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName)); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - if (i == methodSignature.RequiredParameterCount) - output.Write("..., "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; case MemberReferenceKind.Field: var fieldSignature = mr.DecodeFieldSignature(new DisassemblerSignatureProvider(module, output), genericContext); @@ -245,14 +217,7 @@ namespace ICSharpCode.Decompiler.IL var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)ms.Method); var methodName = metadata.GetString(methodDefinition.Name); methodSignature = methodDefinition.DecodeSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = methodDefinition.GetDeclaringType(); @@ -266,52 +231,21 @@ namespace ICSharpCode.Decompiler.IL } else { output.Write(DisassemblerHelpers.Escape(methodName)); } - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; case HandleKind.MemberReference: var memberReference = metadata.GetMemberReference((MemberReferenceHandle)ms.Method); memberName = metadata.GetString(memberReference.Name); methodSignature = memberReference.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, memberReference.Parent, genericContext, syntax); output.Write("::"); output.Write(DisassemblerHelpers.Escape(memberName)); - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; } break; @@ -319,15 +253,10 @@ namespace ICSharpCode.Decompiler.IL var standaloneSig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)entity); switch (standaloneSig.GetKind()) { case StandaloneSignatureKind.Method: - var methodSig = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - methodSig.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); - output.Write('('); - for (int i = 0; i < methodSig.ParameterTypes.Length; i++) { - if (i > 0) - output.Write(", "); - methodSig.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(')'); + methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); + WriteSignatureHeader(output, methodSignature); + methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); + WriteParameterList(output, methodSignature); break; case StandaloneSignatureKind.LocalVariables: default: @@ -341,6 +270,56 @@ namespace ICSharpCode.Decompiler.IL } } + static void WriteTypeParameterList(ITextOutput output, ILNameSyntax syntax, System.Collections.Immutable.ImmutableArray> substitution) + { + output.Write('<'); + for (int i = 0; i < substitution.Length; i++) { + if (i > 0) + output.Write(", "); + substitution[i](syntax); + } + output.Write('>'); + } + + static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) + { + output.Write("("); + for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { + if (i > 0) + output.Write(", "); + if (i == methodSignature.RequiredParameterCount) + output.Write("..., "); + methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); + } + output.Write(")"); + } + + static void WriteSignatureHeader(ITextOutput output, MethodSignature> methodSignature) + { + if (methodSignature.Header.HasExplicitThis) { + output.Write("instance explicit "); + } else if (methodSignature.Header.IsInstance) { + output.Write("instance "); + } + switch (methodSignature.Header.CallingConvention) { + case SignatureCallingConvention.CDecl: + output.Write("unmanaged cdecl "); + break; + case SignatureCallingConvention.StdCall: + output.Write("unmanaged stdcall "); + break; + case SignatureCallingConvention.ThisCall: + output.Write("unmanaged thiscall "); + break; + case SignatureCallingConvention.FastCall: + output.Write("unmanaged fastcall "); + break; + case SignatureCallingConvention.VarArgs: + output.Write("vararg "); + break; + } + } + static void WriteParent(ITextOutput output, PEFile module, MetadataReader metadata, EntityHandle parentHandle, Metadata.GenericContext genericContext, ILNameSyntax syntax) { switch (parentHandle.Kind) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index 07f980fa7..e2b751218 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.IL public readonly InstructionCollection Arguments; ILInstruction functionPointer; - + public bool IsInstance { get; } + public bool HasExplicitThis { get; } public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; } public IType ReturnType { get; } public ImmutableArray ParameterTypes { get; } @@ -61,9 +62,11 @@ namespace ICSharpCode.Decompiler.IL functionPointer.ChildIndex = Arguments.Count; } - public CallIndirect(System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, + public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, IEnumerable arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect) { + this.IsInstance = isInstance; + this.HasExplicitThis = hasExplicitThis; this.CallingConvention = callingConvention; this.ReturnType = returnType ?? throw new ArgumentNullException("returnType"); this.ParameterTypes = parameterTypes.ToImmutableArray(); @@ -74,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL public override ILInstruction Clone() { - return new CallIndirect(CallingConvention, ReturnType, ParameterTypes, + return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes, this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone() ).WithILRange(this); } @@ -84,7 +87,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); - Debug.Assert(Arguments.Count == ParameterTypes.Length); + Debug.Assert(Arguments.Count == ParameterTypes.Length + (IsInstance ? 1 : 0)); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) @@ -94,7 +97,12 @@ namespace ICSharpCode.Decompiler.IL ReturnType.WriteTo(output); output.Write('('); bool first = true; - foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) { + int firstArgument = IsInstance ? 1 : 0; + if (firstArgument == 1) { + Arguments[0].WriteTo(output, options); + first = false; + } + foreach (var (inst, type) in Arguments.Skip(firstArgument).Zip(ParameterTypes, (a,b) => (a,b))) { if (first) first = false; else @@ -155,6 +163,10 @@ namespace ICSharpCode.Decompiler.IL bool EqualSignature(CallIndirect other) { + if (IsInstance != other.IsInstance) + return false; + if (HasExplicitThis != other.HasExplicitThis) + return false; if (CallingConvention != other.CallingConvention) return false; if (ParameterTypes.Length != other.ParameterTypes.Length) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index b3d3fe8ce..972be8aa0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -97,6 +97,7 @@ namespace ICSharpCode.Decompiler.IL this.AddILRange(binary); Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 08c0a965c..93575cae4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -64,8 +64,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitBlock(Block block) { - // Don't visit child blocks; since this is a block transform - // we know those were already handled previously. + if (block.Kind == BlockKind.ControlFlow) { + // Don't visit child control flow blocks; + // since this is a block transform + // we know those were already handled previously. + return; + } + base.VisitBlock(block); } protected internal override void VisitComp(Comp inst) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index 03e3297f3..cdd1822a0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -216,6 +216,8 @@ namespace ICSharpCode.Decompiler.IL var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1) return false; + if (defaultBlock.Parent != switchContainer) + return false; // tally stats for heuristic from each case block int maxStatements = 0, maxDepth = 0; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 1f337c420..f7a6bee67 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -478,9 +478,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Try to determine the real type of the object we're modifying: storeType = stobj.Target.InferType(compilation); if (storeType is ByReferenceType refType) { - storeType = refType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(refType.ElementType, stobj.Type)) { + storeType = refType.ElementType; + } else { + storeType = stobj.Type; + } } else if (storeType is PointerType pointerType) { - storeType = pointerType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(pointerType.ElementType, stobj.Type)) { + storeType = pointerType.ElementType; + } else { + storeType = stobj.Type; + } } else { storeType = stobj.Type; } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index ccf21ac9c..0ab8f4faa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -141,8 +141,12 @@ namespace ICSharpCode.Decompiler.TypeSystem if (name == null) { return null; } - return ReflectionHelper.ParseReflectionName(name) - .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + try { + return ReflectionHelper.ParseReflectionName(name) + .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + } catch (ReflectionNameParseException ex) { + throw new BadImageFormatException($"Invalid type name: \"{name}\": {ex.Message}"); + } } public IType GetTypeFromSpecification(SRM.MetadataReader reader, GenericContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) diff --git a/ILSpy-tests b/ILSpy-tests index 28f74a2a8..4d29b27bd 160000 --- a/ILSpy-tests +++ b/ILSpy-tests @@ -1 +1 @@ -Subproject commit 28f74a2a8f4050fe455a982dbd361a981841a9bd +Subproject commit 4d29b27bd7262790efe52edfcb9c100bb62a0382 diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index e61b4e2a5..1126fcf82 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -51,6 +51,7 @@ + diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index c1c734173..134f9faf0 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -54,7 +54,9 @@ namespace ICSharpCode.ILSpy.Options var properties = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false); foreach (var p in properties) { - p.SetValue(newSettings, (bool?)e.Attribute(p.Name) ?? true); + var value = (bool?)e.Attribute(p.Name); + if (value.HasValue) + p.SetValue(newSettings, value.Value); } return newSettings; }