diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 68a1c2613..b237cdf3a 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -7,7 +7,7 @@ True - 1701;1702;1705,67,169,1058,728,1720,649,168,251 + 1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675 False diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 5244e21c0..5826fb09f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty new CustomClass().StringProp += 1; } -#if false public uint PreIncrementIndexer(string name) { return ++M()[name]; } -#endif + public int PreIncrementByRef(ref int i) { return ++i; @@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ++(*GetPointer()); } + public unsafe int PreIncrementOfPointer(int* ptr) + { + return *(++ptr); + } + public int PreIncrement2DArray() { return ++Array()[1, 2]; @@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[Environment.TickCount] *= 10; } -#if false + public uint CompoundAssignIndexer(string name) { - return M()[name] -= 2; + return M()[name] -= 2u; } -#endif + + public uint CompoundAssignIndexerComplexIndex(string name) + { + return M()[ToString()] -= 2u; + } + public int CompoundAssignIncrement2DArray() { return Array()[1, 2] %= 10; @@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return i <<= 2; } + public unsafe int* CompoundAssignOfPointer(int* ptr) + { + return ptr += 10; + } + public unsafe double CompoundAssignByPointer(double* ptr) { return *ptr /= 1.5; @@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[pos]--; } -#if false + public uint PostIncrementIndexer(string name) { return M()[name]++; } +#if false public unsafe int PostIncrementOfPointer(int* ptr) { return *(ptr++); } #endif + public int PostDecrementInstanceField() { return M().Field--; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs index 8fbeb4bbd..6391cf62f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs @@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } /// - /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, /// is commonly used in C. It is less common for C++, C#, and others. /// public static CSharpFormattingOptions CreateKRStyle() diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index ff0ff5854..021e75397 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -647,7 +647,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { TypeKind kind = type.Kind; return kind == TypeKind.Class && type.GetDefinition().IsSealed - || kind == TypeKind.Delegate || kind == TypeKind.Anonymous; + || kind == TypeKind.Delegate; } #endregion diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs index 83207005d..4f6022208 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs @@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return extensionMethods ?? Enumerable.Empty>(); } - + /// /// Gets the eligible extension methods. /// /// - /// Specifies whether to produce a + /// Specifies whether to produce a SpecializedMethod /// when type arguments could be inferred from . /// This setting is only used for inferred types and has no effect if the type parameters are /// specified explicitly. diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs index 21643562e..c2af071b9 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// /// Statements for Objects/Collections initializer. /// + /// /// /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs b/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs deleted file mode 100644 index 082291dd0..000000000 --- a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2010-2014 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using ICSharpCode.Decompiler.CSharp.Syntax; - -namespace ICSharpCode.Decompiler.CSharp.Resolver -{ - /// - /// Arguments for the callback of . - /// - public class RenameCallbackArguments - { - public AstNode NodeToReplace { get; private set; } - public AstNode NewNode { get; private set; } - - public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode) - { - if (nodeToReplace == null) - throw new ArgumentNullException("nodeToReplace"); - if (newNode == null) - throw new ArgumentNullException("newNode"); - this.NodeToReplace = nodeToReplace; - this.NewNode = newNode; - } - } -} diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index f9a9f2d95..da02b77e7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Divide, /// left %= right Modulus, - - /// left <<= right + + /// left <<= right ShiftLeft, /// left >>= right ShiftRight, - /// left &= right + /// left &= right BitwiseAnd, /// left |= right BitwiseOr, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs index 60213cc7d..31e9bd9f1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs @@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax PostDecrement, /// Dereferencing (*a) Dereference, - /// Get address (&a) + /// Get address (&a) AddressOf, /// C# 5.0 await Await, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs index e0b10c17f..b3c6419ba 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { /// - /// extern alias ; + /// extern alias IDENTIFIER; /// public class ExternAliasDeclaration : AstNode { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs index b49bbf7dd..8f9746d8d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs @@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// A line/column position. /// Text editor lines/columns are counted started from one. /// - /// - /// The document provides the methods and - /// to convert between offsets and TextLocations. - /// [Serializable] [TypeConverter(typeof(TextLocationConverter))] public struct TextLocation : IComparable, IEquatable diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 0de498900..80315ede2 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -16,7 +16,7 @@ false 7.2 - $(TargetDir)ICSharpCode.Decompiler.xml + true True ICSharpCode.Decompiler.snk @@ -48,6 +48,10 @@ true + + 1701;1702;1591;1573 + + @@ -224,7 +228,6 @@ - diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index f37cb1fed..7025208f1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// if (cond) { if (nestedCond) { nestedThen... } } /// -> - /// if (cond && nestedCond) { nestedThen... } + /// if (cond && nestedCond) { nestedThen... } /// private void IntroduceShortCircuit(IfInstruction ifInst) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 8b096630f..a8ec926a9 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -458,7 +458,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// or that leave the block Container. /// /// Entry point of the loop. - /// Whether to ignore branches that map to C# 'continue' statements. /// out: The number of different CFG nodes. /// Possible values: /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 6a4429ec4..a1fca3d89 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// s c /// /// where: - /// p|n: if (a && b) goto c; goto s; + /// p|n: if (a && b) goto c; goto s; /// /// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression /// has a target (c) with no corresponding block (leave) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index c4db8d89a..bbdafe4e1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Set in AnalyzeCurrentProperty() IField currentField; - /// The disposing field of the compiler-generated enumerator class./summary> + /// The disposing field of the compiler-generated enumerator class. /// Set in ConstructExceptionTable() for assembly compiled with Mono IField disposingField; diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index f2bb29021..cfa1c9f0a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL /// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null). NullableUnwrap, /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. NullableRewrap, /// Loads a constant string. LdStr, @@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL StringToInt, /// ILAst representation of Expression.Convert. ExpressionTreeCast, - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. UserDefinedLogicOperator, /// ILAst representation of a short-circuiting binary operator inside a dynamic expression. DynamicLogicOperatorInstruction, @@ -1063,7 +1063,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as NumericCompoundAssign; - return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1097,7 +1097,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as UserDefinedCompoundAssign; - return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1131,7 +1131,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as DynamicCompoundAssign; - return o != null && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -2714,7 +2714,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. public sealed partial class NullableRewrap : UnaryInstruction { public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) @@ -4910,7 +4910,7 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand { public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator) diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index a6c1d13f5..31407d783 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -75,19 +75,23 @@ CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("user.compound", "Common instruction for user-defined compound assignments.", CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, MayThrow, SideEffect, CustomWriteTo, MatchCondition("this.Method.Equals(o.Method)"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), @@ -190,7 +194,7 @@ + "returns the managed reference unmodified (if the value is non-null).", Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine - + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type)." + + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)." + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + "the nullable.rewrap instruction evaluates to null.", Unary, CustomComputeFlags), @@ -282,7 +286,7 @@ CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), MatchCondition("this.IsChecked == o.IsChecked")), - new OpCode("user.logic.operator", "Use of user-defined && or || operator.", + new OpCode("user.logic.operator", "Use of user-defined && or || operator.", CustomClassName("UserDefinedLogicOperator"), HasMethodOperand, ResultType("O"), CustomChildren(new []{ diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 9f22e0d34..fb28f1c7b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.IL /// /// IfInstruction is also used to represent logical operators: /// "a || b" ==> if (a) (ldc.i4 1) else (b) - /// "a && b" ==> if (a) (b) else (ldc.i4 0) + /// "a && b" ==> if (a) (b) else (ldc.i4 0) /// "a ? b : c" ==> if (a) (b) else (c) /// partial class IfInstruction : ILInstruction diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d4ec96350..46559fd4f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -373,7 +373,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// /// The next top-level expression - /// The load within 'next' + /// The variable being eliminated by inlining. /// The expression being inlined static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) { @@ -432,6 +432,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms case OpCode.ArrayToPointer: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span + case OpCode.Call: + case OpCode.CallVirt: + // Aggressive inline into property/indexer getter calls for compound assignment calls + // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter) + if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot) { + return true; + } + break; } // decide based on the top-level target instruction into which we are inlining: switch (next.OpCode) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 85e2bcc76..0b1f617d7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// VS2017.8 / Roslyn 2.9 started optimizing some cases of - /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" + /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// to - /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" - /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. + /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" + /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. /// public bool Run(BinaryNumericInstruction bni) { @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public bool RunStatements(Block block, int pos) { - /// e.g.: + // e.g.: // if (!condition) Block { // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // } @@ -541,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Performs nullable lifting. /// /// Produces a lifted instruction with semantics equivalent to: - /// (v1 != null && ... && vn != null) ? trueInst : falseInst, + /// (v1 != null && ... && vn != null) ? trueInst : falseInst, /// where the v1,...,vn are the this.nullableVars. /// If lifting fails, returns null. /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs index 87175e5c2..0645066bc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// - /// Exception thrown when an IL transform runs into the limit. + /// Exception thrown when an IL transform runs into the . /// public class StepLimitReachedException : Exception { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 1644416ec..5fe7298ce 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -174,10 +174,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // because the ExpressionTransforms don't look into inline blocks, manually trigger HandleCallCompoundAssign if (HandleCompoundAssign(call, context)) { // if we did construct a compound assignment, it should have made our inline block redundant: - if (inlineBlock.Instructions.Single().MatchStLoc(newVar, out var compoundAssign)) { - Debug.Assert(newVar.IsSingleDefinition && newVar.LoadCount == 1); - inlineBlock.ReplaceWith(compoundAssign); - } + Debug.Assert(!inlineBlock.IsConnected); } return true; } else { @@ -205,8 +202,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall) + static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall, out Action finalizeMatch) { + finalizeMatch = null; if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) return false; if (setterCall.OpCode != getterCall.OpCode) @@ -218,12 +216,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // Ensure that same arguments are passed to getterCall and setterCall: for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + if (getterCall.Arguments[j].MatchLdLoc(v)) { + // OK, setter call argument is saved in temporary that is re-used for getter call + if (finalizeMatch == null) { + finalizeMatch = AdjustArguments; + } + continue; + } + } if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags)) return false; if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) return false; } return true; + + void AdjustArguments(ILTransformContext context) + { + Debug.Assert(setterCall.Arguments.Count == getterCall.Arguments.Count + 1); + for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v, out var value)) { + Debug.Assert(v.IsSingleDefinition && v.LoadCount == 1); + Debug.Assert(getterCall.Arguments[j].MatchLdLoc(v)); + getterCall.Arguments[j] = value; + } + } + } } /// @@ -275,18 +294,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { - if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) return false; context.Step($"Compound assignment (binary.numeric)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) return false; - if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { @@ -304,12 +324,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step($"Compound assignment (user-defined binary)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, rhs); } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { - if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (dynamic binary)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation @@ -317,9 +339,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) return false; - if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (string concatenation)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, concatCall.Arguments[1]); } else { @@ -332,6 +355,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.RequestRerun(); // moving stloc to top-level might trigger inlining } compoundStore.ReplaceWith(newInst); + if (newInst.Parent is Block inlineAssignBlock && inlineAssignBlock.Kind == BlockKind.CallInlineAssign) { + // It's possible that we first replaced the instruction in an inline-assign helper block. + // In such a situation, we know from the block invariant that we're have a storeInSetter. + Debug.Assert(storeInSetter != null); + Debug.Assert(storeInSetter.Variable.IsSingleDefinition && storeInSetter.Variable.LoadCount == 1); + Debug.Assert(inlineAssignBlock.Instructions.Single() == storeInSetter); + Debug.Assert(inlineAssignBlock.FinalInstruction.MatchLdLoc(storeInSetter.Variable)); + // Block CallInlineAssign { stloc I_0(compound.op(...)); final: ldloc I_0 } + // --> compound.op(...) + inlineAssignBlock.ReplaceWith(storeInSetter.Value); + } return true; } @@ -468,6 +502,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } foreach (var arg in call.Arguments.SkipLast(1)) { + if (arg.MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + continue; // OK, IsMatchingCompoundLoad can perform an adjustment in this special case + } if (!SemanticHelper.IsPure(arg.Flags)) { return false; } @@ -484,13 +521,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + /// + /// Checks whether 'load' and 'store' both access the same store, and can be combined to a compound assignment. + /// + /// The load instruction to test. + /// The compound store to test against. Must have previously been tested via IsCompoundStore() + /// The target to use for the compound assignment instruction. + /// The target kind to use for the compound assignment instruction. + /// If set to a non-null value, call this delegate to fix up minor mismatches between getter and setter. + /// + /// If given a non-null value, this function returns false if the forbiddenVariable is used in the load/store instructions. + /// Some transforms effectively move a store around, + /// which is only valid if the variable stored to does not occur in the compound load/store. + /// static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, out ILInstruction target, out CompoundTargetKind targetKind, - ILFunction contextFunction = null, + out Action finalizeMatch, ILVariable forbiddenVariable = null) { target = null; targetKind = 0; + finalizeMatch = null; if (load is LdObj ldobj && store is StObj stobj) { Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); if (!SemanticHelper.IsPure(ldobj.Target.Flags)) @@ -500,7 +551,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms target = ldobj.Target; targetKind = CompoundTargetKind.Address; return ldobj.Target.Match(stobj.Target).Success; - } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) { + } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; target = load; @@ -509,11 +560,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) { if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) return false; - if (contextFunction == null) - return false; // locals only supported for the callers that specify the context target = new LdLoca(ldloc.Variable).WithILRange(ldloc); targetKind = CompoundTargetKind.Address; - contextFunction.RecombineVariables(ldloc.Variable, stloc.Variable); + finalizeMatch = context => context.Function.RecombineVariables(ldloc.Variable, stloc.Variable); return true; } else { return false; @@ -566,11 +615,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) return false; - if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, forbiddenVariable: stloc.Variable)) + if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: stloc.Variable)) return false; if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); + finalizeMatch?.Invoke(context); if (binary != null) { block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); @@ -614,7 +664,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 'stloc tmp' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, context.Function, forbiddenVariable: inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: inst.Variable)) return false; if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) @@ -624,6 +674,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!ValidateCompoundAssign(binary, conv, targetType)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); + finalizeMatch?.Invoke(context); inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { @@ -634,6 +685,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); + finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); } else { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 027d96722..4fb8ddb9e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -78,10 +78,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms VisitILFunction(f); } } - context.Step($"Remove instructions", function); - foreach (var store in instructionsToRemove) { - if (store.Parent is Block containingBlock) - containingBlock.Instructions.Remove(store); + if (instructionsToRemove.Count > 0) { + context.Step($"Remove instructions", function); + foreach (var store in instructionsToRemove) { + if (store.Parent is Block containingBlock) + containingBlock.Instructions.Remove(store); + } } } finally { instructionsToRemove.Clear(); diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 68f10c532..9a871d36b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -47,10 +47,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Gets whether this parameter is a C# 'params' parameter. /// bool IsParams { get; } - + /// /// Gets whether this parameter is optional. - /// The default value is given by the property. + /// The default value is given by the function. /// bool IsOptional { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs index 722b550d5..0db477fb8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs @@ -30,10 +30,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// SymbolKind.TypeDefinition or SymbolKind.Method SymbolKind OwnerType { get; } - + /// /// Gets the owning method/class. - /// This property may return null (for example for the dummy type parameters used by ). + /// This property may return null (for example for the dummy type parameters used by ). /// /// /// For "class Outer<T> { class Inner {} }", diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs index f558911e0..6b679f39e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs @@ -19,7 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// - /// Constants used instead of + /// Constants used instead of bool? /// in multithreaded code, as bool? might produce torn reads. /// static class ThreeState diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs index 9556ef249..5f107d6fa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Enum, /// The System.Void type. - /// + /// Void, /// Type used for invalid expressions and for types whose definition could not be found. @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Dynamic, /// Represents missing type arguments in partially parameterized types. /// - /// + /// IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions) UnboundTypeArgument, /// The type is a type parameter. @@ -74,9 +74,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// A managed reference type /// ByReference, - /// An anonymous type - /// - Anonymous, /// Intersection of several types /// diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 34631f0a1..78c5a1892 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -384,9 +384,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// (if the given in an override) /// should be returned. /// - public static bool HasAttribute(this IEntity entity, KnownAttribute attrType, bool inherit=false) + public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false) { - return GetAttribute(entity, attrType, inherit) != null; + return GetAttribute(entity, attributeType, inherit) != null; } /// @@ -445,9 +445,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// The parameter on which the attributes are declared. /// The attribute type to look for. - public static bool HasAttribute(this IParameter parameter, KnownAttribute attrType) + public static bool HasAttribute(this IParameter parameter, KnownAttribute attributeType) { - return GetAttribute(parameter, attrType) != null; + return GetAttribute(parameter, attributeType) != null; } ///