Browse Source

Add support for mcs 2.6.4 switch-on-string

pull/1087/head
Siegfried Pammer 7 years ago
parent
commit
7544eac5b4
  1. 3
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 4
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs
  3. 11
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 26
      ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs
  5. 25
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

3
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -115,9 +115,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -115,9 +115,6 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void Switch([ValueSource("defaultOptions")] CompilerOptions options)
{
if (options.HasFlag(CompilerOptions.UseMcs)) {
Assert.Ignore("Decompiler bug with mono!");
}
RunCS(options: options);
}

4
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs

@ -25,7 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -25,7 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static void Main()
{
TestCase(SparseIntegerSwitch, -100, 1, 2, 3, 4);
#if !MCS
TestCase(ShortSwitchOverString, "First case", "Else");
#endif
TestCase(ShortSwitchOverString2, "First case", "Second case", "Third case", "Else");
TestCase(ShortSwitchOverStringNoExplicitDefault, "First case", "Second case", "Third case", "Else");
TestCase(SwitchOverString1, "First case", "Second case", "2nd case", "Third case", "Fourth case", "Fifth case", "Sixth case", null, "default", "else");
@ -65,6 +67,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -65,6 +67,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
}
#if !MCS
public static string ShortSwitchOverString(string text)
{
Console.WriteLine("ShortSwitchOverString: " + text);
@ -75,6 +78,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -75,6 +78,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return "Default";
}
}
#endif
public static string ShortSwitchOverString2(string text)
{

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -83,7 +83,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -83,7 +83,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new IfElseStatement(condition, trueStatement, falseStatement);
}
ConstantResolveResult CreateTypedCaseLabel(long i, IType type, string[] map = null)
IEnumerable<ConstantResolveResult> CreateTypedCaseLabel(long i, IType type, List<(string Key, int Value)> map = null)
{
object value;
// unpack nullable type, if necessary:
@ -92,14 +92,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,14 +92,17 @@ namespace ICSharpCode.Decompiler.CSharp
if (type.IsKnownType(KnownTypeCode.Boolean)) {
value = i != 0;
} else if (type.IsKnownType(KnownTypeCode.String) && map != null) {
value = map[i];
var keys = map.Where(entry => entry.Value == i).Select(entry => entry.Key);
foreach (var key in keys)
yield return new ConstantResolveResult(type, key);
yield break;
} else if (type.Kind == TypeKind.Enum) {
var enumType = type.GetDefinition().EnumUnderlyingType;
value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(enumType), i, false);
} else {
value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, false);
}
return new ConstantResolveResult(type, value);
yield return new ConstantResolveResult(type, value);
}
protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst)
@ -143,7 +146,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -143,7 +146,7 @@ namespace ICSharpCode.Decompiler.CSharp
astSection.CaseLabels.Add(new CaseLabel());
firstValueResolveResult = null;
} else {
var values = section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map)).ToArray();
var values = section.Labels.Values.SelectMany(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map)).ToArray();
if (section.HasNullLabel) {
astSection.CaseLabels.Add(new CaseLabel(new NullReferenceExpression()));
firstValueResolveResult = new ConstantResolveResult(SpecialType.NullType, null);

26
ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs

@ -17,28 +17,46 @@ @@ -17,28 +17,46 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.IL
{
partial class StringToInt
{
public string[] Map { get; }
public List<(string Key, int Value)> Map { get; }
public StringToInt(ILInstruction argument, string[] map)
public StringToInt(ILInstruction argument, List<(string Key, int Value)> map)
: base(OpCode.StringToInt)
{
this.Argument = argument;
this.Map = map;
}
public StringToInt(ILInstruction argument, string[] map)
: this(argument, ArrayToDictionary(map))
{
}
static List<(string Key, int Value)> ArrayToDictionary(string[] map)
{
var dict = new List<(string Key, int Value)>();
for (int i = 0; i < map.Length; i++) {
dict.Add((map[i], i));
}
return dict;
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write("string.to.int (");
Argument.WriteTo(output, options);
output.Write(", { ");
for (int i = 0; i < Map.Length; i++) {
int i = 0;
foreach (var entry in Map) {
if (i > 0) output.Write(", ");
output.Write($"[{i}] = \"{Map[i]}\"");
output.Write($"[\"{entry.Key}\"] = {entry.Value}");
i++;
}
output.Write(" })");
}

25
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -245,7 +245,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -245,7 +245,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!defaultBlockJump.MatchBranch(out var defaultBlock))
return false;
if (!(condition.MatchLogicNot(out var arg) && arg is Call c && c.Method.Name == "TryGetValue" &&
if (!(condition.MatchLogicNot(out var arg) && arg is CallInstruction c && c.Method.Name == "TryGetValue" &&
MatchDictionaryFieldLoad(c.Arguments[0], IsStringToIntDictionary, out var dictField2, out _) && dictField2.Equals(dictField)))
return false;
if (!c.Arguments[1].MatchLdLoc(switchValueVar) || !c.Arguments[2].MatchLdLoca(out var switchIndexVar))
@ -274,7 +274,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -274,7 +274,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switchValue = new LdLoc(switchValueVar);
keepAssignmentBefore = true;
}
var stringToInt = new StringToInt(switchValue, stringValues.ToArray());
var stringToInt = new StringToInt(switchValue, stringValues);
var inst = new SwitchInstruction(stringToInt);
inst.Sections.AddRange(sections);
instructions[i + 1].ReplaceWith(inst);
@ -290,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -290,7 +290,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
private bool AddNullSection(List<SwitchSection> sections, List<string> stringValues, Block nullValueCaseBlock)
bool AddNullSection(List<SwitchSection> sections, List<(string, int)> stringValues, Block nullValueCaseBlock)
{
var label = new LongSet(sections.Count);
var possibleConflicts = sections.Where(sec => sec.Labels.Overlaps(label)).ToArray();
@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; // cannot remove only label
possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label);
}
stringValues.Add(null);
stringValues.Add((null, (int)label.Values.First()));
sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) });
return true;
}
@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -322,7 +322,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Matches and extracts values from Add-call sequences.
/// </summary>
bool ExtractStringValuesFromInitBlock(Block block, out List<string> values, Block targetBlock, IType dictionaryType, IField dictionaryField)
bool ExtractStringValuesFromInitBlock(Block block, out List<(string, int)> values, Block targetBlock, IType dictionaryType, IField dictionaryField)
{
values = null;
// stloc dictVar(newobj Dictionary..ctor(ldc.i4 valuesLength))
@ -342,10 +342,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -342,10 +342,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!newObj.Arguments[0].MatchLdcI4(out valuesLength))
return false;
}
values = new List<string>(valuesLength);
values = new List<(string, int)>(valuesLength);
int i = 0;
while (MatchAddCall(dictionaryType, block.Instructions[i + 1], dictVar, i, out var value)) {
values.Add(value);
while (MatchAddCall(dictionaryType, block.Instructions[i + 1], dictVar, out var index, out var value)) {
values.Add((value, index));
i++;
}
// final store to compiler-generated variable:
@ -362,16 +362,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -362,16 +362,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// -or-
/// call Add(ldloc dictVar, ldstr value, box System.Int32(ldc.i4 index))
/// </summary>
bool MatchAddCall(IType dictionaryType, ILInstruction inst, ILVariable dictVar, int index, out string value)
bool MatchAddCall(IType dictionaryType, ILInstruction inst, ILVariable dictVar, out int index, out string value)
{
value = null;
if (!(inst is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3))
index = -1;
if (!(inst is CallInstruction c && c.Method.Name == "Add" && c.Arguments.Count == 3))
return false;
if (!(c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value)))
return false;
if (!(c.Method.DeclaringType.Equals(dictionaryType) && !c.Method.IsStatic))
return false;
return (c.Arguments[2].MatchLdcI4(index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(index)));
return (c.Arguments[2].MatchLdcI4(out index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(out index)));
}
bool IsStringToIntDictionary(IType dictionaryType)
@ -474,7 +475,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -474,7 +475,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
}
var stringToInt = new StringToInt(switchValue, stringValues.ToArray());
var stringToInt = new StringToInt(switchValue, stringValues);
var inst = new SwitchInstruction(stringToInt);
inst.Sections.AddRange(sections);
instructions[i + 1].ReplaceWith(inst);

Loading…
Cancel
Save