diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
index 26b33c0f6..0510b8c87 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
@@ -321,6 +321,27 @@ namespace ICSharpCode.Decompiler.IL
}
}
+ ///
+ /// Matches 'comp(arg == ldnull)'
+ ///
+ public bool MatchCompEqualsNull(out ILInstruction arg)
+ {
+ if (!MatchCompEquals(out var left, out var right)) {
+ arg = null;
+ return false;
+ }
+ if (right.MatchLdNull()) {
+ arg = left;
+ return true;
+ } else if (left.MatchLdNull()) {
+ arg = right;
+ return true;
+ } else {
+ arg = null;
+ return false;
+ }
+ }
+
///
/// Matches comp(left != right) or logic.not(comp(left == right)).
///
diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
index ddc9ab01d..c37b8fe15 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
@@ -80,44 +80,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
List<(string, Block)> values = new List<(string, Block)>();
ILInstruction switchValue = null;
- bool extraLoad = false;
- bool keepAssignmentBefore = false;
- // Roslyn: match call to operator ==(string, string)
- if (MatchStringEqualityComparison(condition, out var switchValueVar, out string value)) {
- values.Add((value, firstBlock));
- if(!instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) {
- switchValue = new LdLoc(switchValueVar);
- }
- } else {
- // match null check with different variable:
- // this is used by the old C# compiler.
- if (MatchCompEqualsNull(condition, out var otherSwitchValueVar)) {
- values.Add((null, firstBlock));
- } else {
- return false;
- }
+ // match call to operator ==(string, string)
+ if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue))
+ return false;
+ values.Add((firstBlockValue, firstBlock));
+ bool extraLoad = false;
+ if (instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) {
+ // stloc switchValueVar(switchValue)
+ // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock
+ } else if (instructions[i - 1] is StLoc stloc && stloc.Value.MatchLdLoc(switchValueVar)) {
// in case of optimized legacy code there are two stlocs:
// stloc otherSwitchValueVar(ldloc switchValue)
// stloc switchValueVar(ldloc otherSwitchValueVar)
- // if (comp(ldloc otherSwitchValueVar == ldnull)) br nullCase
- if (i > 1 && otherSwitchValueVar != null && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue)
- && instructions[i - 1].MatchStLoc(out switchValueVar, out var switchValueCopyInst)
- && switchValueCopyInst.MatchLdLoc(otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) {
+ // if (call op_Equality(ldloc otherSwitchValueVar, ldstr value)) br firstBlock
+ var otherSwitchValueVar = switchValueVar;
+ switchValueVar = stloc.Variable;
+ if (i >= 2 && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue)
+ && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2)
+ {
extraLoad = true;
- } else if (instructions[i - 1].MatchStLoc(out switchValueVar, out switchValue)) {
- // unoptimized legacy switch
+ } else {
+ switchValue = new LdLoc(otherSwitchValueVar);
}
+ } else {
+ switchValue = new LdLoc(switchValueVar);
}
-
// if instruction must be followed by a branch to the next case
if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump))
return false;
// extract all cases and add them to the values list.
Block currentCaseBlock = nextCaseJump.TargetBlock;
Block nextCaseBlock;
- while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out value, out Block block)) != null) {
+ while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) {
values.Add((value, block));
currentCaseBlock = nextCaseBlock;
}
@@ -125,6 +121,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (values.Count < 3)
return false;
// if the switchValueVar is used in other places as well, do not eliminate the store.
+ bool keepAssignmentBefore = false;
if (switchValueVar.LoadCount > values.Count) {
keepAssignmentBefore = true;
switchValue = new LdLoc(switchValueVar);
@@ -187,31 +184,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!currentBlock.Instructions[1].MatchBranch(out nextBlock))
return null;
}
- if (!MatchStringEqualityComparison(condition, out var v, out value)) {
- if (MatchCompEqualsNull(condition, out v)) {
- value = null;
- } else {
+ if (!MatchStringEqualityComparison(condition, switchVariable, out value)) {
return null;
}
- }
- if (v != switchVariable)
- return null;
return nextBlock;
}
-
- ///
- /// Returns true if is only assigned once and the initialization is done by copying .
- ///
- bool IsInitializedBy(ILVariable left, ILVariable right)
- {
- if (!left.IsSingleDefinition)
- return false;
- var storeInst = left.StoreInstructions.OfType().SingleOrDefault();
- if (storeInst == null)
- return false;
- return storeInst.Value.MatchLdLoc(right);
- }
-
+
///
/// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>.
///
@@ -475,8 +453,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!bodyBranch.MatchBranch(out Block body))
return false;
- if (!MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out string stringValue)) {
- if (condition.MatchLogicNot(out condition) && MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out stringValue)) {
+ if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) {
+ if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) {
if (!target.Instructions[1].MatchBranch(out Block exit))
return false;
body = exit;
@@ -523,39 +501,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
- bool MatchStringEqualityOrNullComparison(ILInstruction condition, ILVariable variable, out string stringValue)
+ ///
+ /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
+ /// or 'comp(ldloc(variable) == ldnull)'
+ ///
+ bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue)
{
- if (!MatchStringEqualityComparison(condition, out var v, out stringValue)) {
- if (!MatchCompEqualsNull(condition, out v))
- return false;
- stringValue = null;
+ return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable;
}
- return v == variable;
- }
-
+ ///
+ /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
+ /// or 'comp(ldloc(variable) == ldnull)'
+ ///
bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue)
{
stringValue = null;
variable = null;
ILInstruction left, right;
- if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality" && c.Arguments.Count == 2) {
+ if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality"
+ && c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2)
+ {
left = c.Arguments[0];
right = c.Arguments[1];
- if (!right.MatchLdStr(out stringValue))
- return false;
+ return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue);
+ } else if (condition.MatchCompEqualsNull(out var arg)) {
+ stringValue = null;
+ return arg.MatchLdLoc(out variable);
} else {
return false;
}
- return left.MatchLdLoc(out variable);
}
-
- bool MatchCompEqualsNull(ILInstruction condition, out ILVariable variable)
- {
- variable = null;
- if (!condition.MatchCompEquals(out var left, out var right))
- return false;
- return right.MatchLdNull() && left.MatchLdLoc(out variable);
}
}
-}