From 3314f3f37adb7e2fb5c56eb3c2f60f166a988372 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 19:13:40 +0200 Subject: [PATCH 01/16] Fix #1571: Expression transforms not running for values of inline assignments. --- .../TestCases/Pretty/InlineAssignmentTest.cs | 11 +++++++++++ .../IL/Transforms/ExpressionTransforms.cs | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) 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/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 623d69ae8..a61d99fce 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) From d841d964062d7a5ba4535f4a60bebc206963c75a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:21:51 +0200 Subject: [PATCH 02/16] Fix unnecessary casts when calling user-defined operators on nullable types. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 61494fceb..2a287a7e3 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -979,11 +979,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; From e6489d543e07f84185a0d52257bf8d48d3a30d35 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:23:15 +0200 Subject: [PATCH 03/16] Fix #1574: When re-introducing an explicit cast for an implicit user-defined conversion; ensure we use a direct cast and don't go through the code path for builtin conversions. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 + .../Pretty/UserDefinedConversions.cs | 134 ++++++++++++++++++ ICSharpCode.Decompiler/CSharp/Annotations.cs | 14 ++ .../CSharp/TranslatedExpression.cs | 23 ++- 5 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs 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/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/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) From cd8dd7af9fb60ca5939e547ed7cfbda3afc11280 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 21:54:11 +0200 Subject: [PATCH 04/16] Disassembler: Fix output of calling convention and flags in standalone method signature --- .../IL/InstructionOutputExtensions.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 3e3134d71..154885aee 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -319,13 +319,21 @@ 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); + methodSignature = standaloneSig.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 "); + } + methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write('('); - for (int i = 0; i < methodSig.ParameterTypes.Length; i++) { + for (int i = 0; i < methodSignature.ParameterTypes.Length; i++) { if (i > 0) output.Write(", "); - methodSig.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); + methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); } output.Write(')'); break; From ab892a603bfec5fcf115266011234a24ca33a156 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 22:23:37 +0200 Subject: [PATCH 05/16] Disassembler: Refactor InstructionOutputExtensions.WriteTo: Reduce code duplication; Add support for unmanaged calling conventions --- .../IL/InstructionOutputExtensions.cs | 153 +++++++----------- 1 file changed, 62 insertions(+), 91 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 154885aee..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; @@ -320,22 +254,9 @@ namespace ICSharpCode.Decompiler.IL switch (standaloneSig.GetKind()) { case StandaloneSignatureKind.Method: methodSignature = standaloneSig.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('('); - 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 StandaloneSignatureKind.LocalVariables: default: @@ -349,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) { From ed5d71b3656a99498b3828e10fb31b8c83e98274 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:38:39 +0200 Subject: [PATCH 06/16] Fix #1537: KeyNotFoundException in ReduceNestingTransform.ReduceSwitchNesting() --- ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs | 2 ++ 1 file changed, 2 insertions(+) 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; From a6def4cdf5edfe03d1ac65950df2a947aebef505 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 23:08:30 +0200 Subject: [PATCH 07/16] Fix incorrect type for numeric.compound.assign when the pointer type is incompatible with the store type. Closes #1511, #1530, #1533. --- .../IL/Instructions/CompoundAssignmentInstruction.cs | 1 + .../IL/Transforms/TransformAssignment.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) 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/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; } From d50b8d66d13b3d46b4f1f5023e30a748c9eec930 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 07:58:47 +0200 Subject: [PATCH 08/16] Fix #1373, fix #1541: add support for instance calls to CallIndirect. --- ICSharpCode.Decompiler/IL/ILReader.cs | 11 +++++++--- .../IL/Instructions/CallIndirect.cs | 22 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 4d98af2fd..d9e3a2b83 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/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) From 6338bd1b8538554471c276a7aade7b781d326624 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:13:28 +0200 Subject: [PATCH 09/16] Partially revert CheckNoNamedOrOptionalArguments. --- .../Transforms/ReplaceMethodCallsWithOperators.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) 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) From 7d95516e8526511b2f53d539d8d2fb2f41f046da Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:14:34 +0200 Subject: [PATCH 10/16] Fix #1518: Missing Detach()-call in AST manipulation. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 2a287a7e3..0a6114889 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -334,7 +334,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; From 5c9b3233cdac45061b0fb59816205d93981b521e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:50:09 +0200 Subject: [PATCH 11/16] Fix bug overwriting settings default values on first load. --- ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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; } From 688dffff2b4303963e20f08656239c8ff823157f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 14 Jul 2019 14:18:47 +0200 Subject: [PATCH 12/16] Add some string.Concat()-tests --- .../TestCases/Correctness/TrickyTypes.cs | 9 +++++++++ 1 file changed, 9 insertions(+) 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")); + } } } From dec9c83c4c85f02b886a0b4ae0c3f7068515e061 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 14 Jul 2019 14:31:15 +0200 Subject: [PATCH 13/16] Fix #1570: Handle ReflectionNameParseException when decoding invalid attribute arguments --- ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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) From 89a50e64fccda5a7d74269def436340e037c27fe Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 10:06:59 +0200 Subject: [PATCH 14/16] Extension method syntax on lambda expressions/delegates is not allowed. --- .../TestCases/Pretty/DelegateConstruction.cs | 16 ++++++++++++++++ .../Transforms/IntroduceExtensionMethods.cs | 2 ++ 2 files changed, 18 insertions(+) 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/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index e15d0d765..3b11cd87f 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; From 9f661f99f2343b88887c90a99c48afadcd95d394 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 15 Jul 2019 10:29:52 +0200 Subject: [PATCH 15/16] Add Microsoft.VisualStudio.SDK.Analyzers as per https://devblogs.microsoft.com/visualstudio/checklist-for-writing-great-visual-studio-extensions/ - Warnings not fixed --- ILSpy.AddIn/ILSpy.AddIn.csproj | 1 + 1 file changed, 1 insertion(+) 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 @@ + From ec34ffd8d61b62587abb8bf825438364a18397dc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 11:45:51 +0200 Subject: [PATCH 16/16] ILSpy-tests submodule updated --- ILSpy-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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