Browse Source

Add missing DecompilerSettings for new language features

pull/3049/head
Siegfried Pammer 2 years ago
parent
commit
688474facd
  1. 2
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 62
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 18
      ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs
  4. 2
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  5. 29
      ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs
  6. 27
      ILSpy/Properties/Resources.Designer.cs
  7. 9
      ILSpy/Properties/Resources.resx

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -4575,7 +4575,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
foreach (var subPattern in matchInstruction.SubPatterns) foreach (var subPattern in matchInstruction.SubPatterns)
{ {
if (!MatchInstruction.IsPatternMatch(subPattern, out var testedOperand)) if (!MatchInstruction.IsPatternMatch(subPattern, out var testedOperand, settings))
{ {
Debug.Fail("Invalid sub pattern"); Debug.Fail("Invalid sub pattern");
continue; continue;

62
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -130,6 +130,7 @@ namespace ICSharpCode.Decompiler
staticLocalFunctions = false; staticLocalFunctions = false;
ranges = false; ranges = false;
switchExpressions = false; switchExpressions = false;
recursivePatternMatching = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp9_0) if (languageVersion < CSharp.LanguageVersion.CSharp9_0)
{ {
@ -141,6 +142,8 @@ namespace ICSharpCode.Decompiler
withExpressions = false; withExpressions = false;
usePrimaryConstructorSyntax = false; usePrimaryConstructorSyntax = false;
covariantReturns = false; covariantReturns = false;
relationalPatterns = false;
patternCombinators = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp10_0) if (languageVersion < CSharp.LanguageVersion.CSharp10_0)
{ {
@ -166,10 +169,11 @@ namespace ICSharpCode.Decompiler
if (fileScopedNamespaces || recordStructs) if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0; return CSharp.LanguageVersion.CSharp10_0;
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension
|| recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns) || recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns
|| relationalPatterns || patternCombinators)
return CSharp.LanguageVersion.CSharp9_0; return CSharp.LanguageVersion.CSharp9_0;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions) || staticLocalFunctions || ranges || switchExpressions || recursivePatternMatching)
return CSharp.LanguageVersion.CSharp8_0; return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers
|| patternBasedFixedStatement) || patternBasedFixedStatement)
@ -1678,6 +1682,60 @@ namespace ICSharpCode.Decompiler
} }
} }
bool recursivePatternMatching = true;
/// <summary>
/// Gets/Sets whether C# 8.0 recursive patterns should be detected.
/// </summary>
[Category("C# 8.0 / VS 2019")]
[Description("DecompilerSettings.RecursivePatternMatching")]
public bool RecursivePatternMatching {
get { return recursivePatternMatching; }
set {
if (recursivePatternMatching != value)
{
recursivePatternMatching = value;
OnPropertyChanged();
}
}
}
bool patternCombinators = true;
/// <summary>
/// Gets/Sets whether C# 9.0 and, or, not patterns should be detected.
/// </summary>
[Category("C# 9.0 / VS 2019.8")]
[Description("DecompilerSettings.PatternCombinators")]
public bool PatternCombinators {
get { return patternCombinators; }
set {
if (patternCombinators != value)
{
patternCombinators = value;
OnPropertyChanged();
}
}
}
bool relationalPatterns = true;
/// <summary>
/// Gets/Sets whether C# 9.0 relational patterns should be detected.
/// </summary>
[Category("C# 9.0 / VS 2019.8")]
[Description("DecompilerSettings.RelationalPatterns")]
public bool RelationalPatterns {
get { return relationalPatterns; }
set {
if (relationalPatterns != value)
{
relationalPatterns = value;
OnPropertyChanged();
}
}
}
bool staticLocalFunctions = true; bool staticLocalFunctions = true;
/// <summary> /// <summary>

18
ICSharpCode.Decompiler/IL/Instructions/MatchInstruction.cs

@ -119,7 +119,7 @@ namespace ICSharpCode.Decompiler.IL
/// (even if the pattern fails to match!). /// (even if the pattern fails to match!).
/// The pattern matching instruction evaluates to 1 (as I4) if the pattern matches, or 0 otherwise. /// The pattern matching instruction evaluates to 1 (as I4) if the pattern matches, or 0 otherwise.
/// </summary> /// </summary>
public static bool IsPatternMatch(ILInstruction? inst, [NotNullWhen(true)] out ILInstruction? testedOperand) public static bool IsPatternMatch(ILInstruction? inst, [NotNullWhen(true)] out ILInstruction? testedOperand, DecompilerSettings? settings)
{ {
switch (inst) switch (inst)
{ {
@ -127,13 +127,23 @@ namespace ICSharpCode.Decompiler.IL
testedOperand = m.testedOperand; testedOperand = m.testedOperand;
return true; return true;
case Comp comp: case Comp comp:
if (comp.MatchLogicNot(out var operand) && IsPatternMatch(operand, out testedOperand)) if (comp.MatchLogicNot(out var operand) && IsPatternMatch(operand, out testedOperand, settings))
{ {
return true; return settings?.PatternCombinators ?? true;
} }
else else
{ {
testedOperand = comp.Left; testedOperand = comp.Left;
if (!(settings?.RelationalPatterns ?? true))
{
if (comp.Kind is not (ComparisonKind.Equality or ComparisonKind.Inequality))
return false;
}
if (!(settings?.PatternCombinators ?? true))
{
if (comp.Kind is ComparisonKind.Inequality)
return false;
}
return IsConstant(comp.Right); return IsConstant(comp.Right);
} }
case Call call when IsCallToString_op_Equality(call): case Call call when IsCallToString_op_Equality(call):
@ -201,7 +211,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(SubPatterns.Count >= NumPositionalPatterns); Debug.Assert(SubPatterns.Count >= NumPositionalPatterns);
foreach (var subPattern in SubPatterns) foreach (var subPattern in SubPatterns)
{ {
if (!IsPatternMatch(subPattern, out ILInstruction? operand)) if (!IsPatternMatch(subPattern, out ILInstruction? operand, null))
throw new InvalidOperationException("Sub-Pattern must be a valid pattern"); throw new InvalidOperationException("Sub-Pattern must be a valid pattern");
// the first child is TestedOperand // the first child is TestedOperand
int subPatternIndex = subPattern.ChildIndex - 1; int subPatternIndex = subPattern.ChildIndex - 1;

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

@ -557,7 +557,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return; return;
} }
} }
if (MatchInstruction.IsPatternMatch(inst.Condition, out _) if (MatchInstruction.IsPatternMatch(inst.Condition, out _, context.Settings)
&& inst.TrueInst.MatchLdcI4(1) && inst.FalseInst.MatchLdcI4(0)) && inst.TrueInst.MatchLdcI4(1) && inst.FalseInst.MatchLdcI4(0))
{ {
context.Step("match(x) ? true : false -> match(x)", inst); context.Step("match(x) ? true : false -> match(x)", inst);

29
ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs

@ -189,6 +189,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
private static ILInstruction DetectPropertySubPatterns(MatchInstruction parentPattern, ILInstruction trueInst, private static ILInstruction DetectPropertySubPatterns(MatchInstruction parentPattern, ILInstruction trueInst,
ILInstruction parentFalseInst, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg) ILInstruction parentFalseInst, BlockContainer container, ILTransformContext context, ref ControlFlowGraph? cfg)
{ {
if (!context.Settings.RecursivePatternMatching)
{
return trueInst;
}
while (true) while (true)
{ {
Block? trueBlock = trueInst as Block; Block? trueBlock = trueInst as Block;
@ -230,7 +234,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ExtensionMethods.Swap(ref trueInst, ref falseInst); ExtensionMethods.Swap(ref trueInst, ref falseInst);
negate = true; negate = true;
} }
if (MatchInstruction.IsPatternMatch(condition, out var operand)) if (MatchInstruction.IsPatternMatch(condition, out var operand, context.Settings))
{ {
if (!PropertyOrFieldAccess(operand, out var target, out _)) if (!PropertyOrFieldAccess(operand, out var target, out _))
{ {
@ -240,6 +244,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return null; return null;
} }
if (negate && !context.Settings.PatternCombinators)
{
return null;
}
context.Step("Move property sub pattern", condition); context.Step("Move property sub pattern", condition);
if (negate) if (negate)
{ {
@ -253,8 +261,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return null; return null;
} }
context.Step("Move property sub pattern", condition); if (!negate && !context.Settings.PatternCombinators)
parentPattern.SubPatterns.Add(new Comp(negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdcI4(0))); {
return null;
}
context.Step("Sub pattern: implicit != 0", condition);
parentPattern.SubPatterns.Add(new Comp(negate ? ComparisonKind.Equality : ComparisonKind.Inequality,
Sign.None, condition, new LdcI4(0)));
} }
else else
{ {
@ -376,7 +389,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return null; return null;
} }
if (varPattern.Variable.AddressCount == 1) if (varPattern.Variable.AddressCount == 1 && context.Settings.PatternCombinators)
{ {
context.Step("Nullable.HasValue check -> not null pattern", block); context.Step("Nullable.HasValue check -> not null pattern", block);
varPattern.ReplaceWith(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull())); varPattern.ReplaceWith(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull()));
@ -412,6 +425,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return null; return null;
} }
if (!(context.Settings.RelationalPatterns || comp.Kind is ComparisonKind.Equality or ComparisonKind.Inequality))
{
return null;
}
bool negated = false; bool negated = false;
if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst)) if (!DetectExitPoints.CompatibleExitInstruction(falseInst, parentFalseInst))
{ {
@ -426,6 +443,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
return null; return null;
} }
if (negated && !context.Settings.PatternCombinators)
{
return null;
}
context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block); context.Step("Nullable.HasValue check + Nullable.GetValueOrDefault pattern", block);
// varPattern: match (v = testedOperand) // varPattern: match (v = testedOperand)
// comp: comp.i4(call GetValueOrDefault(ldloca v) != ldc.i4 42) // comp: comp.i4(call GetValueOrDefault(ldloca v) != ldc.i4 42)

27
ILSpy/Properties/Resources.Designer.cs generated

@ -1145,6 +1145,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Pattern combinators (and, or, not).
/// </summary>
public static string DecompilerSettings_PatternCombinators {
get {
return ResourceManager.GetString("DecompilerSettings.PatternCombinators", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use pattern matching expressions. /// Looks up a localized string similar to Use pattern matching expressions.
/// </summary> /// </summary>
@ -1199,6 +1208,24 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Recursive pattern matching.
/// </summary>
public static string DecompilerSettings_RecursivePatternMatching {
get {
return ResourceManager.GetString("DecompilerSettings.RecursivePatternMatching", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Relational patterns.
/// </summary>
public static string DecompilerSettings_RelationalPatterns {
get {
return ResourceManager.GetString("DecompilerSettings.RelationalPatterns", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Remove dead and side effect free code (use with caution!). /// Looks up a localized string similar to Remove dead and side effect free code (use with caution!).
/// </summary> /// </summary>

9
ILSpy/Properties/Resources.resx

@ -405,6 +405,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.ParameterNullCheck" xml:space="preserve"> <data name="DecompilerSettings.ParameterNullCheck" xml:space="preserve">
<value>Use parameter null checking</value> <value>Use parameter null checking</value>
</data> </data>
<data name="DecompilerSettings.PatternCombinators" xml:space="preserve">
<value>Pattern combinators (and, or, not)</value>
</data>
<data name="DecompilerSettings.PatternMatching" xml:space="preserve"> <data name="DecompilerSettings.PatternMatching" xml:space="preserve">
<value>Use pattern matching expressions</value> <value>Use pattern matching expressions</value>
</data> </data>
@ -423,6 +426,12 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.RecordStructs" xml:space="preserve"> <data name="DecompilerSettings.RecordStructs" xml:space="preserve">
<value>Record structs</value> <value>Record structs</value>
</data> </data>
<data name="DecompilerSettings.RecursivePatternMatching" xml:space="preserve">
<value>Recursive pattern matching</value>
</data>
<data name="DecompilerSettings.RelationalPatterns" xml:space="preserve">
<value>Relational patterns</value>
</data>
<data name="DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution" xml:space="preserve"> <data name="DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution" xml:space="preserve">
<value>Remove dead and side effect free code (use with caution!)</value> <value>Remove dead and side effect free code (use with caution!)</value>
</data> </data>

Loading…
Cancel
Save