Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into local-functions

pull/1586/head
Siegfried Pammer 6 years ago
parent
commit
8bfd17404b
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 28
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  5. 4
      ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs
  6. 1
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  7. 42
      ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs
  8. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs
  9. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  10. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs
  11. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs
  12. 7
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  13. 2
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  14. 1
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  15. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  16. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  17. 14
      ICSharpCode.Decompiler/IL/Instructions.cs
  18. 12
      ICSharpCode.Decompiler/IL/Instructions.tt
  19. 2
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  20. 10
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  21. 10
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  22. 2
      ICSharpCode.Decompiler/IL/Transforms/Stepper.cs
  23. 84
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  24. 10
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  25. 4
      ICSharpCode.Decompiler/TypeSystem/IParameter.cs
  26. 4
      ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
  27. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs
  28. 7
      ICSharpCode.Decompiler/TypeSystem/TypeKind.cs
  29. 8
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -7,7 +7,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251</NoWarn> <NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675</NoWarn>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo> <GenerateAssemblyInfo>False</GenerateAssemblyInfo>

28
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
new CustomClass().StringProp += 1; new CustomClass().StringProp += 1;
} }
#if false
public uint PreIncrementIndexer(string name) public uint PreIncrementIndexer(string name)
{ {
return ++M()[name]; return ++M()[name];
} }
#endif
public int PreIncrementByRef(ref int i) public int PreIncrementByRef(ref int i)
{ {
return ++i; return ++i;
@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return ++(*GetPointer()); return ++(*GetPointer());
} }
public unsafe int PreIncrementOfPointer(int* ptr)
{
return *(++ptr);
}
public int PreIncrement2DArray() public int PreIncrement2DArray()
{ {
return ++Array()[1, 2]; return ++Array()[1, 2];
@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return array[Environment.TickCount] *= 10; return array[Environment.TickCount] *= 10;
} }
#if false
public uint CompoundAssignIndexer(string name) 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() public int CompoundAssignIncrement2DArray()
{ {
return Array()[1, 2] %= 10; return Array()[1, 2] %= 10;
@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return i <<= 2; return i <<= 2;
} }
public unsafe int* CompoundAssignOfPointer(int* ptr)
{
return ptr += 10;
}
public unsafe double CompoundAssignByPointer(double* ptr) public unsafe double CompoundAssignByPointer(double* ptr)
{ {
return *ptr /= 1.5; return *ptr /= 1.5;
@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
return array[pos]--; return array[pos]--;
} }
#if false
public uint PostIncrementIndexer(string name) public uint PostIncrementIndexer(string name)
{ {
return M()[name]++; return M()[name]++;
} }
#if false
public unsafe int PostIncrementOfPointer(int* ptr) public unsafe int PostIncrementOfPointer(int* ptr)
{ {
return *(ptr++); return *(ptr++);
} }
#endif #endif
public int PostDecrementInstanceField() public int PostDecrementInstanceField()
{ {
return M().Field--; return M().Field--;

2
ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs

@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
/// <summary> /// <summary>
/// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, /// The K&amp;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. /// is commonly used in C. It is less common for C++, C#, and others.
/// </summary> /// </summary>
public static CSharpFormattingOptions CreateKRStyle() public static CSharpFormattingOptions CreateKRStyle()

2
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -647,7 +647,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{ {
TypeKind kind = type.Kind; TypeKind kind = type.Kind;
return kind == TypeKind.Class && type.GetDefinition().IsSealed return kind == TypeKind.Class && type.GetDefinition().IsSealed
|| kind == TypeKind.Delegate || kind == TypeKind.Anonymous; || kind == TypeKind.Delegate;
} }
#endregion #endregion

4
ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs

@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
return extensionMethods ?? Enumerable.Empty<IEnumerable<IMethod>>(); return extensionMethods ?? Enumerable.Empty<IEnumerable<IMethod>>();
} }
/// <summary> /// <summary>
/// Gets the eligible extension methods. /// Gets the eligible extension methods.
/// </summary> /// </summary>
/// <param name="substituteInferredTypes"> /// <param name="substituteInferredTypes">
/// Specifies whether to produce a <see cref="SpecializedMethod"/> /// Specifies whether to produce a <c>SpecializedMethod</c>
/// when type arguments could be inferred from <see cref="TargetType"/>. /// when type arguments could be inferred from <see cref="TargetType"/>.
/// This setting is only used for inferred types and has no effect if the type parameters are /// This setting is only used for inferred types and has no effect if the type parameters are
/// specified explicitly. /// specified explicitly.

1
ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs

@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// <param name="initializerStatements"> /// <param name="initializerStatements">
/// Statements for Objects/Collections initializer. /// Statements for Objects/Collections initializer.
/// <see cref="InvocationResolveResult.InitializerStatements"/> /// <see cref="InvocationResolveResult.InitializerStatements"/>
/// </param>
/// <param name="returnTypeOverride"> /// <param name="returnTypeOverride">
/// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result.
/// </param> /// </param>

42
ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs

@ -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
{
/// <summary>
/// Arguments for the callback of <see cref="FindReferences.RenameReferencesInFile"/>.
/// </summary>
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;
}
}
}

6
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs

@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Divide, Divide,
/// <summary>left %= right</summary> /// <summary>left %= right</summary>
Modulus, Modulus,
/// <summary>left <<= right</summary> /// <summary>left &lt;&lt;= right</summary>
ShiftLeft, ShiftLeft,
/// <summary>left >>= right</summary> /// <summary>left >>= right</summary>
ShiftRight, ShiftRight,
/// <summary>left &= right</summary> /// <summary>left &amp;= right</summary>
BitwiseAnd, BitwiseAnd,
/// <summary>left |= right</summary> /// <summary>left |= right</summary>
BitwiseOr, BitwiseOr,

2
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
PostDecrement, PostDecrement,
/// <summary>Dereferencing (*a)</summary> /// <summary>Dereferencing (*a)</summary>
Dereference, Dereference,
/// <summary>Get address (&a)</summary> /// <summary>Get address (&amp;a)</summary>
AddressOf, AddressOf,
/// <summary>C# 5.0 await</summary> /// <summary>C# 5.0 await</summary>
Await, Await,

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs

@ -28,7 +28,7 @@
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
/// <summary> /// <summary>
/// extern alias <Identifier>; /// extern alias IDENTIFIER;
/// </summary> /// </summary>
public class ExternAliasDeclaration : AstNode public class ExternAliasDeclaration : AstNode
{ {

4
ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs

@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// A line/column position. /// A line/column position.
/// Text editor lines/columns are counted started from one. /// Text editor lines/columns are counted started from one.
/// </summary> /// </summary>
/// <remarks>
/// The document provides the methods <see cref="IDocument.GetLocation"/> and
/// <see cref="IDocument.GetOffset(ICSharpCode.Decompiler.CSharp.Syntax.TextLocation)"/> to convert between offsets and TextLocations.
/// </remarks>
[Serializable] [Serializable]
[TypeConverter(typeof(TextLocationConverter))] [TypeConverter(typeof(TextLocationConverter))]
public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation> public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation>

7
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -16,7 +16,7 @@
<EnableDefaultItems>false</EnableDefaultItems> <EnableDefaultItems>false</EnableDefaultItems>
<LangVersion>7.2</LangVersion> <LangVersion>7.2</LangVersion>
<DocumentationFile>$(TargetDir)ICSharpCode.Decompiler.xml</DocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>True</SignAssembly> <SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
@ -48,6 +48,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591;1573</NoWarn>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.2.0" /> <PackageReference Include="Humanizer.Core" Version="2.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" /> <PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
@ -224,7 +228,6 @@
<Compile Include="CSharp\Resolver\OverloadResolution.cs" /> <Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" /> <Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="TypeSystem\Implementation\LocalFunctionMethod.cs" /> <Compile Include="TypeSystem\Implementation\LocalFunctionMethod.cs" />
<Compile Include="CSharp\Resolver\RenameCallbackArguments.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" /> <Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" /> <Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" /> <Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" />

2
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary> /// <summary>
/// if (cond) { if (nestedCond) { nestedThen... } } /// if (cond) { if (nestedCond) { nestedThen... } }
/// -> /// ->
/// if (cond && nestedCond) { nestedThen... } /// if (cond &amp;&amp; nestedCond) { nestedThen... }
/// </summary> /// </summary>
private void IntroduceShortCircuit(IfInstruction ifInst) private void IntroduceShortCircuit(IfInstruction ifInst)
{ {

1
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -458,7 +458,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// or that leave the block Container. /// or that leave the block Container.
/// </summary> /// </summary>
/// <param name="loopHead">Entry point of the loop.</param> /// <param name="loopHead">Entry point of the loop.</param>
/// <param name="isSwitch">Whether to ignore branches that map to C# 'continue' statements.</param>
/// <param name="exitNodeArity">out: The number of different CFG nodes. /// <param name="exitNodeArity">out: The number of different CFG nodes.
/// Possible values: /// Possible values:
/// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits);

2
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// s c /// s c
/// ///
/// where: /// where:
/// p|n: if (a && b) goto c; goto s; /// p|n: if (a &amp;&amp; b) goto c; goto s;
/// ///
/// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression /// 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) /// has a target (c) with no corresponding block (leave)

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <remarks>Set in AnalyzeCurrentProperty()</remarks> /// <remarks>Set in AnalyzeCurrentProperty()</remarks>
IField currentField; IField currentField;
/// <summary>The disposing field of the compiler-generated enumerator class./summary> /// <summary>The disposing field of the compiler-generated enumerator class.</summary>
/// <remarks>Set in ConstructExceptionTable() for assembly compiled with Mono</remarks> /// <remarks>Set in ConstructExceptionTable() for assembly compiled with Mono</remarks>
IField disposingField; IField disposingField;

14
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).</summary> /// 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).</summary>
NullableUnwrap, NullableUnwrap,
/// <summary>Serves as jump target for the nullable.unwrap instruction. /// <summary>Serves as jump target for the nullable.unwrap instruction.
/// 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.</summary> /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; 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.</summary>
NullableRewrap, NullableRewrap,
/// <summary>Loads a constant string.</summary> /// <summary>Loads a constant string.</summary>
LdStr, LdStr,
@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL
StringToInt, StringToInt,
/// <summary>ILAst representation of Expression.Convert.</summary> /// <summary>ILAst representation of Expression.Convert.</summary>
ExpressionTreeCast, ExpressionTreeCast,
/// <summary>Use of user-defined && or || operator.</summary> /// <summary>Use of user-defined &amp;&amp; or || operator.</summary>
UserDefinedLogicOperator, UserDefinedLogicOperator,
/// <summary>ILAst representation of a short-circuiting binary operator inside a dynamic expression.</summary> /// <summary>ILAst representation of a short-circuiting binary operator inside a dynamic expression.</summary>
DynamicLogicOperatorInstruction, DynamicLogicOperatorInstruction,
@ -1063,7 +1063,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as NumericCompoundAssign; 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) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as UserDefinedCompoundAssign; 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) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as DynamicCompoundAssign; 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 namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Serves as jump target for the nullable.unwrap instruction. /// <summary>Serves as jump target for the nullable.unwrap instruction.
/// 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.</summary> /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; 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.</summary>
public sealed partial class NullableRewrap : UnaryInstruction public sealed partial class NullableRewrap : UnaryInstruction
{ {
public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument)
@ -4910,7 +4910,7 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Use of user-defined && or || operator.</summary> /// <summary>Use of user-defined &amp;&amp; or || operator.</summary>
public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand
{ {
public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator) public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator)

12
ICSharpCode.Decompiler/IL/Instructions.tt

@ -75,19 +75,23 @@
CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags,
MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), 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("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")), MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("user.compound", "Common instruction for user-defined compound assignments.", new OpCode("user.compound", "Common instruction for user-defined compound assignments.",
CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor,
MayThrow, SideEffect, CustomWriteTo, MayThrow, SideEffect, CustomWriteTo,
MatchCondition("this.Method.Equals(o.Method)"), 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("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")), MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.",
CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"),
MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), 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("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")), MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), 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).", + "returns the managed reference unmodified (if the value is non-null).",
Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")),
new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine 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<T> if the input is a non-nullable value type)." + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; 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," + "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.", + "the nullable.rewrap instruction evaluates to null.",
Unary, CustomComputeFlags), Unary, CustomComputeFlags),
@ -282,7 +286,7 @@
CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"),
MatchCondition("this.IsChecked == o.IsChecked")), 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 &amp;&amp; or || operator.",
CustomClassName("UserDefinedLogicOperator"), CustomClassName("UserDefinedLogicOperator"),
HasMethodOperand, ResultType("O"), HasMethodOperand, ResultType("O"),
CustomChildren(new []{ CustomChildren(new []{

2
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.IL
/// ///
/// IfInstruction is also used to represent logical operators: /// IfInstruction is also used to represent logical operators:
/// "a || b" ==> if (a) (ldc.i4 1) else (b) /// "a || b" ==> if (a) (ldc.i4 1) else (b)
/// "a && b" ==> if (a) (b) else (ldc.i4 0) /// "a &amp;&amp; b" ==> if (a) (b) else (ldc.i4 0)
/// "a ? b : c" ==> if (a) (b) else (c) /// "a ? b : c" ==> if (a) (b) else (c)
/// </remarks> /// </remarks>
partial class IfInstruction : ILInstruction partial class IfInstruction : ILInstruction

10
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. /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary> /// </summary>
/// <param name="next">The next top-level expression</param> /// <param name="next">The next top-level expression</param>
/// <param name="loadInst">The load within 'next'</param> /// <param name="v">The variable being eliminated by inlining.</param>
/// <param name="inlinedExpression">The expression being inlined</param> /// <param name="inlinedExpression">The expression being inlined</param>
static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) 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.ArrayToPointer:
case OpCode.LocAllocSpan: case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span 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: // decide based on the top-level target instruction into which we are inlining:
switch (next.OpCode) { switch (next.OpCode) {

10
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// VS2017.8 / Roslyn 2.9 started optimizing some cases of /// VS2017.8 / Roslyn 2.9 started optimizing some cases of
/// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// "a.GetValueOrDefault() == b.GetValueOrDefault() &amp;&amp; (a.HasValue &amp; b.HasValue)"
/// to /// to
/// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) &amp; (a.HasValue &amp; b.HasValue)"
/// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. /// so this secondary entry point analyses logic.and as-if it was a short-circuting &amp;&amp;.
/// </summary> /// </summary>
public bool Run(BinaryNumericInstruction bni) public bool Run(BinaryNumericInstruction bni)
{ {
@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public bool RunStatements(Block block, int pos) public bool RunStatements(Block block, int pos)
{ {
/// e.g.: // e.g.:
// if (!condition) Block { // if (!condition) Block {
// leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // leave IL_0000 (default.value System.Nullable`1[[System.Int64]])
// } // }
@ -541,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Performs nullable lifting. /// Performs nullable lifting.
/// ///
/// Produces a lifted instruction with semantics equivalent to: /// Produces a lifted instruction with semantics equivalent to:
/// (v1 != null && ... && vn != null) ? trueInst : falseInst, /// (v1 != null &amp;&amp; ... &amp;&amp; vn != null) ? trueInst : falseInst,
/// where the v1,...,vn are the <c>this.nullableVars</c>. /// where the v1,...,vn are the <c>this.nullableVars</c>.
/// If lifting fails, returns <c>null</c>. /// If lifting fails, returns <c>null</c>.
/// </summary> /// </summary>

2
ICSharpCode.Decompiler/IL/Transforms/Stepper.cs

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
/// <summary> /// <summary>
/// Exception thrown when an IL transform runs into the <see cref="Stepper.MaxStepCount"/> limit. /// Exception thrown when an IL transform runs into the <see cref="Stepper.StepLimit"/>.
/// </summary> /// </summary>
public class StepLimitReachedException : Exception public class StepLimitReachedException : Exception
{ {

84
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 // because the ExpressionTransforms don't look into inline blocks, manually trigger HandleCallCompoundAssign
if (HandleCompoundAssign(call, context)) { if (HandleCompoundAssign(call, context)) {
// if we did construct a compound assignment, it should have made our inline block redundant: // 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(!inlineBlock.IsConnected);
Debug.Assert(newVar.IsSingleDefinition && newVar.LoadCount == 1);
inlineBlock.ReplaceWith(compoundAssign);
}
} }
return true; return true;
} else { } else {
@ -205,8 +202,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall) static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall, out Action<ILTransformContext> finalizeMatch)
{ {
finalizeMatch = null;
if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner))
return false; return false;
if (setterCall.OpCode != getterCall.OpCode) if (setterCall.OpCode != getterCall.OpCode)
@ -218,12 +216,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
// Ensure that same arguments are passed to getterCall and setterCall: // Ensure that same arguments are passed to getterCall and setterCall:
for (int j = 0; j < getterCall.Arguments.Count; j++) { 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)) if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags))
return false; return false;
if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success)
return false; return false;
} }
return true; 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;
}
}
}
} }
/// <summary> /// <summary>
@ -275,18 +294,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
ILInstruction newInst; ILInstruction newInst;
if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { 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; return false;
if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
return false; return false;
context.Step($"Compound assignment (binary.numeric)", compoundStore); context.Step($"Compound assignment (binary.numeric)", compoundStore);
finalizeMatch?.Invoke(context);
newInst = new NumericCompoundAssign( newInst = new NumericCompoundAssign(
binary, target, targetKind, binary.Right, binary, target, targetKind, binary.Right,
targetType, CompoundEvalMode.EvaluatesToNewValue); targetType, CompoundEvalMode.EvaluatesToNewValue);
} else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) {
if (operatorCall.Arguments.Count == 0) if (operatorCall.Arguments.Count == 0)
return false; 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; return false;
ILInstruction rhs; ILInstruction rhs;
if (operatorCall.Arguments.Count == 2) { if (operatorCall.Arguments.Count == 2) {
@ -304,12 +324,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (operatorCall.IsLifted) if (operatorCall.IsLifted)
return false; // TODO: add tests and think about whether nullables need special considerations return false; // TODO: add tests and think about whether nullables need special considerations
context.Step($"Compound assignment (user-defined binary)", compoundStore); context.Step($"Compound assignment (user-defined binary)", compoundStore);
finalizeMatch?.Invoke(context);
newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue,
target, targetKind, rhs); target, targetKind, rhs);
} else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { } 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; return false;
context.Step($"Compound assignment (dynamic binary)", compoundStore); 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); 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)) { } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) {
// setterValue is a string.Concat() invocation // 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 return false; // for now we only support binary compound assignments
if (!targetType.IsKnownType(KnownTypeCode.String)) if (!targetType.IsKnownType(KnownTypeCode.String))
return false; 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; return false;
context.Step($"Compound assignment (string concatenation)", compoundStore); context.Step($"Compound assignment (string concatenation)", compoundStore);
finalizeMatch?.Invoke(context);
newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue,
target, targetKind, concatCall.Arguments[1]); target, targetKind, concatCall.Arguments[1]);
} else { } else {
@ -332,6 +355,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.RequestRerun(); // moving stloc to top-level might trigger inlining context.RequestRerun(); // moving stloc to top-level might trigger inlining
} }
compoundStore.ReplaceWith(newInst); 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; return true;
} }
@ -468,6 +502,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
foreach (var arg in call.Arguments.SkipLast(1)) { 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)) { if (!SemanticHelper.IsPure(arg.Flags)) {
return false; return false;
} }
@ -484,13 +521,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
/// <summary>
/// Checks whether 'load' and 'store' both access the same store, and can be combined to a compound assignment.
/// </summary>
/// <param name="load">The load instruction to test.</param>
/// <param name="store">The compound store to test against. Must have previously been tested via IsCompoundStore()</param>
/// <param name="target">The target to use for the compound assignment instruction.</param>
/// <param name="targetKind">The target kind to use for the compound assignment instruction.</param>
/// <param name="finalizeMatch">If set to a non-null value, call this delegate to fix up minor mismatches between getter and setter.</param>
/// <param name="forbiddenVariable">
/// 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.
/// </param>
static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store,
out ILInstruction target, out CompoundTargetKind targetKind, out ILInstruction target, out CompoundTargetKind targetKind,
ILFunction contextFunction = null, out Action<ILTransformContext> finalizeMatch,
ILVariable forbiddenVariable = null) ILVariable forbiddenVariable = null)
{ {
target = null; target = null;
targetKind = 0; targetKind = 0;
finalizeMatch = null;
if (load is LdObj ldobj && store is StObj stobj) { if (load is LdObj ldobj && store is StObj stobj) {
Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags));
if (!SemanticHelper.IsPure(ldobj.Target.Flags)) if (!SemanticHelper.IsPure(ldobj.Target.Flags))
@ -500,7 +551,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
target = ldobj.Target; target = ldobj.Target;
targetKind = CompoundTargetKind.Address; targetKind = CompoundTargetKind.Address;
return ldobj.Target.Match(stobj.Target).Success; 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)) if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load))
return false; return false;
target = load; 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)) { } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) {
if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable))
return false; return false;
if (contextFunction == null)
return false; // locals only supported for the callers that specify the context
target = new LdLoca(ldloc.Variable).WithILRange(ldloc); target = new LdLoca(ldloc.Variable).WithILRange(ldloc);
targetKind = CompoundTargetKind.Address; targetKind = CompoundTargetKind.Address;
contextFunction.RecombineVariables(ldloc.Variable, stloc.Variable); finalizeMatch = context => context.Function.RecombineVariables(ldloc.Variable, stloc.Variable);
return true; return true;
} else { } else {
return false; return false;
@ -566,11 +615,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot))
return false; 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; return false;
if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem))
return false; return false;
context.Step("TransformPostIncDecOperatorWithInlineStore", store); context.Step("TransformPostIncDecOperatorWithInlineStore", store);
finalizeMatch?.Invoke(context);
if (binary != null) { if (binary != null) {
block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign(
binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue));
@ -614,7 +664,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// 'stloc tmp' is implicitly truncating the value // 'stloc tmp' is implicitly truncating the value
return false; 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; return false;
if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) {
if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1))
@ -624,6 +674,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!ValidateCompoundAssign(binary, conv, targetType)) if (!ValidateCompoundAssign(binary, conv, targetType))
return false; return false;
context.Step("TransformPostIncDecOperator (builtin)", inst); context.Step("TransformPostIncDecOperator (builtin)", inst);
finalizeMatch?.Invoke(context);
inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right,
targetType, CompoundEvalMode.EvaluatesToOldValue); targetType, CompoundEvalMode.EvaluatesToOldValue);
} else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { } 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) if (operatorCall.IsLifted)
return false; // TODO: add tests and think about whether nullables need special considerations return false; // TODO: add tests and think about whether nullables need special considerations
context.Step("TransformPostIncDecOperator (user-defined)", inst); context.Step("TransformPostIncDecOperator (user-defined)", inst);
finalizeMatch?.Invoke(context);
inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, inst.Value = new UserDefinedCompoundAssign(operatorCall.Method,
CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1));
} else { } else {

10
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -78,10 +78,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
VisitILFunction(f); VisitILFunction(f);
} }
} }
context.Step($"Remove instructions", function); if (instructionsToRemove.Count > 0) {
foreach (var store in instructionsToRemove) { context.Step($"Remove instructions", function);
if (store.Parent is Block containingBlock) foreach (var store in instructionsToRemove) {
containingBlock.Instructions.Remove(store); if (store.Parent is Block containingBlock)
containingBlock.Instructions.Remove(store);
}
} }
} finally { } finally {
instructionsToRemove.Clear(); instructionsToRemove.Clear();

4
ICSharpCode.Decompiler/TypeSystem/IParameter.cs

@ -47,10 +47,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets whether this parameter is a C# 'params' parameter. /// Gets whether this parameter is a C# 'params' parameter.
/// </summary> /// </summary>
bool IsParams { get; } bool IsParams { get; }
/// <summary> /// <summary>
/// Gets whether this parameter is optional. /// Gets whether this parameter is optional.
/// The default value is given by the <see cref="IVariable.ConstantValue"/> property. /// The default value is given by the <see cref="IVariable.GetConstantValue"/> function.
/// </summary> /// </summary>
bool IsOptional { get; } bool IsOptional { get; }

4
ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs

@ -30,10 +30,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
/// <returns>SymbolKind.TypeDefinition or SymbolKind.Method</returns> /// <returns>SymbolKind.TypeDefinition or SymbolKind.Method</returns>
SymbolKind OwnerType { get; } SymbolKind OwnerType { get; }
/// <summary> /// <summary>
/// Gets the owning method/class. /// Gets the owning method/class.
/// This property may return null (for example for the dummy type parameters used by <see cref="ParameterListComparer.NormalizeMethodTypeParameters"/>). /// This property may return null (for example for the dummy type parameters used by <see cref="NormalizeTypeVisitor.ReplaceMethodTypeParametersWithDummy"/>).
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// For "class Outer&lt;T&gt; { class Inner {} }", /// For "class Outer&lt;T&gt; { class Inner {} }",

2
ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs

@ -19,7 +19,7 @@
namespace ICSharpCode.Decompiler.TypeSystem.Implementation namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{ {
/// <summary> /// <summary>
/// Constants used instead of <see cref="bool?"/> /// Constants used instead of <c>bool?</c>
/// in multithreaded code, as <c>bool?</c> might produce torn reads. /// in multithreaded code, as <c>bool?</c> might produce torn reads.
/// </summary> /// </summary>
static class ThreeState static class ThreeState

7
ICSharpCode.Decompiler/TypeSystem/TypeKind.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
Enum, Enum,
/// <summary>The <c>System.Void</c> type.</summary> /// <summary>The <c>System.Void</c> type.</summary>
/// <see cref="KnownTypeReference.Void"/> /// <see cref="KnownTypeCode.Void"/>
Void, Void,
/// <summary>Type used for invalid expressions and for types whose definition could not be found.</summary> /// <summary>Type used for invalid expressions and for types whose definition could not be found.</summary>
@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
Dynamic, Dynamic,
/// <summary>Represents missing type arguments in partially parameterized types.</summary> /// <summary>Represents missing type arguments in partially parameterized types.</summary>
/// <see cref="SpecialType.UnboundTypeArgument"/> /// <see cref="SpecialType.UnboundTypeArgument"/>
/// <see cref="IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions)"/> /// <see cref="IType">IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions)</see>
UnboundTypeArgument, UnboundTypeArgument,
/// <summary>The type is a type parameter.</summary> /// <summary>The type is a type parameter.</summary>
@ -74,9 +74,6 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>A managed reference type</summary> /// <summary>A managed reference type</summary>
/// <see cref="ByReferenceType"/> /// <see cref="ByReferenceType"/>
ByReference, ByReference,
/// <summary>An anonymous type</summary>
/// <see cref="AnonymousType"/>
Anonymous,
/// <summary>Intersection of several types</summary> /// <summary>Intersection of several types</summary>
/// <see cref="IntersectionType"/> /// <see cref="IntersectionType"/>

8
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -384,9 +384,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// (if the given <paramref name="entity"/> in an <c>override</c>) /// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned. /// should be returned.
/// </param> /// </param>
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;
} }
/// <summary> /// <summary>
@ -445,9 +445,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
/// <param name="parameter">The parameter on which the attributes are declared.</param> /// <param name="parameter">The parameter on which the attributes are declared.</param>
/// <param name="attributeType">The attribute type to look for.</param> /// <param name="attributeType">The attribute type to look for.</param>
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;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save