Browse Source

Fix #1809: Support VB Select on string.

pull/1843/head
Daniel Grunwald 6 years ago
parent
commit
832c18f0be
  1. 23
      ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
  2. 1
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 3
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 37
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs
  5. 24
      ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb
  6. 10
      ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs
  7. 147
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

23
ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs

@ -13,14 +13,12 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var section = (AttributeSection)attribute.Parent; var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType; SimpleType type = attribute.Type as SimpleType;
if (section.AttributeTarget == "assembly" && if (section.AttributeTarget == "assembly" &&
(type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework")) (type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "PermissionSet" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable" || type.Identifier == "TargetFramework")) {
{
attribute.Remove(); attribute.Remove();
if (section.Attributes.Count == 0) if (section.Attributes.Count == 0)
section.Remove(); section.Remove();
} }
if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode") if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode") {
{
attribute.Remove(); attribute.Remove();
if (section.Attributes.Count == 0) if (section.Attributes.Count == 0)
section.Remove(); section.Remove();
@ -60,4 +58,21 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
rootNode.AcceptVisitor(this); rootNode.AcceptVisitor(this);
} }
} }
public class RemoveNamespaceMy : DepthFirstAstVisitor, IAstTransform
{
public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
{
if (namespaceDeclaration.Name == "My") {
namespaceDeclaration.Remove();
} else {
base.VisitNamespaceDeclaration(namespaceDeclaration);
}
}
public void Run(AstNode rootNode, TransformContext context)
{
rootNode.AcceptVisitor(this);
}
}
} }

1
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -482,6 +482,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings); CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings);
decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAttributes()); decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAttributes());
decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute()); decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute());
decompiler.AstTransforms.Insert(0, new RemoveNamespaceMy());
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
var syntaxTree = decompiler.DecompileWholeModuleAsSingleFile(sortTypes: true); var syntaxTree = decompiler.DecompileWholeModuleAsSingleFile(sortTypes: true);

3
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -81,6 +81,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" /> <Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" /> <Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\VBPretty\Select.cs" />
<None Include="TestCases\ILPretty\WeirdEnums.cs" /> <None Include="TestCases\ILPretty\WeirdEnums.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" /> <Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<None Include="TestCases\Pretty\AsyncStreams.cs" /> <None Include="TestCases\Pretty\AsyncStreams.cs" />
@ -91,6 +92,7 @@
<Compile Include="TestCases\Ugly\NoForEachStatement.cs" /> <Compile Include="TestCases\Ugly\NoForEachStatement.cs" />
<None Include="TestCases\Ugly\NoExtensionMethods.Expected.cs" /> <None Include="TestCases\Ugly\NoExtensionMethods.Expected.cs" />
<Compile Include="TestCases\Ugly\NoExtensionMethods.cs" /> <Compile Include="TestCases\Ugly\NoExtensionMethods.cs" />
<None Include="TestCases\VBPretty\Select.vb" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" /> <None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Include="TestCases\Pretty\ThrowExpressions.cs" /> <Compile Include="TestCases\Pretty\ThrowExpressions.cs" />
<None Include="TestCases\ILPretty\Issue1145.cs" /> <None Include="TestCases\ILPretty\Issue1145.cs" />
@ -261,6 +263,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualBasic" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

37
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.cs

@ -0,0 +1,37 @@
using Microsoft.VisualBasic.CompilerServices;
using System;
[StandardModule]
internal sealed class Program
{
public static void SelectOnString()
{
switch (Environment.CommandLine) {
case "123":
Console.WriteLine("a");
break;
case "444":
Console.WriteLine("b");
break;
case "222":
Console.WriteLine("c");
break;
case "11":
Console.WriteLine("d");
break;
case "dd":
Console.WriteLine("e");
break;
case "sss":
Console.WriteLine("f");
break;
case "aa":
Console.WriteLine("g");
break;
case null:
case "":
Console.WriteLine("empty");
break;
}
}
}

24
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/Select.vb

@ -0,0 +1,24 @@
Imports System
Module Program
Sub SelectOnString()
Select Case Environment.CommandLine
Case "123"
Console.WriteLine("a")
Case "444"
Console.WriteLine("b")
Case "222"
Console.WriteLine("c")
Case "11"
Console.WriteLine("d")
Case "dd"
Console.WriteLine("e")
Case "sss"
Console.WriteLine("f")
Case "aa"
Console.WriteLine("g")
Case ""
Console.WriteLine("empty")
End Select
End Sub
End Module

10
ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs

@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.Tests
} }
static readonly CompilerOptions[] defaultOptions = static readonly CompilerOptions[] defaultOptions =
{ {
CompilerOptions.None, CompilerOptions.None,
CompilerOptions.Optimize, CompilerOptions.Optimize,
CompilerOptions.UseRoslyn, CompilerOptions.UseRoslyn,
@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests
}; };
static readonly CompilerOptions[] roslynOnlyOptions = static readonly CompilerOptions[] roslynOnlyOptions =
{ {
CompilerOptions.UseRoslyn, CompilerOptions.UseRoslyn,
CompilerOptions.Optimize | CompilerOptions.UseRoslyn, CompilerOptions.Optimize | CompilerOptions.UseRoslyn,
}; };
@ -72,6 +72,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(options: options | CompilerOptions.Library); Run(options: options | CompilerOptions.Library);
} }
[Test]
public void Select([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
Run(options: options | CompilerOptions.Library);
}
void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null)
{ {
var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var vbFile = Path.Combine(TestCasePath, testName + ".vb");

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

@ -160,6 +160,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if (comp(ldloc switchValueVar == ldnull)) br defaultBlock // if (comp(ldloc switchValueVar == ldnull)) br defaultBlock
if (!instructions[i].MatchIfInstruction(out var condition, out var firstBlockOrDefaultJump)) if (!instructions[i].MatchIfInstruction(out var condition, out var firstBlockOrDefaultJump))
return false; return false;
var nextCaseJump = instructions[i + 1];
while (condition.MatchLogicNot(out var arg)) {
condition = arg;
ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump);
}
// match call to operator ==(string, string)
if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue, out bool isVBCompareString))
return false;
if (isVBCompareString) {
ExtensionMethods.Swap(ref firstBlockOrDefaultJump, ref nextCaseJump);
}
if (firstBlockOrDefaultJump.MatchBranch(out var firstBlock)) { if (firstBlockOrDefaultJump.MatchBranch(out var firstBlock)) {
// success // success
@ -172,11 +183,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
List<(string, ILInstruction)> values = new List<(string, ILInstruction)>(); List<(string, ILInstruction)> values = new List<(string, ILInstruction)>();
ILInstruction switchValue = null; ILInstruction switchValue = null;
if (isVBCompareString && string.IsNullOrEmpty(firstBlockValue)) {
// match call to operator ==(string, string) values.Add((null, firstBlock ?? firstBlockOrDefaultJump));
if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue)) values.Add((string.Empty, firstBlock ?? firstBlockOrDefaultJump));
return false; } else {
values.Add((firstBlockValue, firstBlock ?? firstBlockOrDefaultJump)); values.Add((firstBlockValue, firstBlock ?? firstBlockOrDefaultJump));
}
bool extraLoad = false; bool extraLoad = false;
bool keepAssignmentBefore = false; bool keepAssignmentBefore = false;
@ -215,13 +227,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switchValue = new LdLoc(switchValueVar); switchValue = new LdLoc(switchValueVar);
} }
// if instruction must be followed by a branch to the next case // if instruction must be followed by a branch to the next case
if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) if (!nextCaseJump.MatchBranch(out Block currentCaseBlock))
return false; return false;
// extract all cases and add them to the values list. // extract all cases and add them to the values list.
Block currentCaseBlock = nextCaseJump.TargetBlock;
ILInstruction nextCaseBlock; ILInstruction nextCaseBlock;
while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) { while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out bool emptyStringEqualsNull, out Block block)) != null) {
values.Add((value, block)); if (emptyStringEqualsNull && string.IsNullOrEmpty(value)) {
values.Add((null, block));
values.Add((string.Empty, block));
} else {
values.Add((value, block));
}
currentCaseBlock = nextCaseBlock as Block; currentCaseBlock = nextCaseBlock as Block;
if (currentCaseBlock == null) if (currentCaseBlock == null)
break; break;
@ -367,37 +383,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// The <paramref name="switchVariable"/> is updated if the value gets copied to a different variable. /// The <paramref name="switchVariable"/> is updated if the value gets copied to a different variable.
/// See comments below for more info. /// See comments below for more info.
/// </summary> /// </summary>
ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock) ILInstruction MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out bool emptyStringEqualsNull, out Block caseBlock)
{ {
value = null; value = null;
caseBlock = null; caseBlock = null;
emptyStringEqualsNull = false;
if (currentBlock.IncomingEdgeCount != 1 || currentBlock.Instructions.Count != 2) if (currentBlock.IncomingEdgeCount != 1 || currentBlock.Instructions.Count != 2)
return null; return null;
if (!currentBlock.Instructions[0].MatchIfInstruction(out var condition, out var caseBlockBranch)) if (!currentBlock.MatchIfAtEndOfBlock(out var condition, out var caseBlockBranch, out var nextBlockBranch))
return null;
if (!MatchStringEqualityComparison(condition, switchVariable, out value, out bool isVBCompareString)) {
return null; return null;
}
if (isVBCompareString) {
ExtensionMethods.Swap(ref caseBlockBranch, ref nextBlockBranch);
emptyStringEqualsNull = true;
}
if (!caseBlockBranch.MatchBranch(out caseBlock)) if (!caseBlockBranch.MatchBranch(out caseBlock))
return null; return null;
Block nextBlock; if (nextBlockBranch.MatchBranch(out Block nextBlock)) {
BlockContainer blockContainer = null; // success
if (condition.MatchLogicNot(out var inner)) { return nextBlock;
condition = inner; } else if (nextBlockBranch.MatchLeave(out BlockContainer blockContainer)) {
nextBlock = caseBlock; // success
if (!currentBlock.Instructions[1].MatchBranch(out caseBlock)) return blockContainer;
return null;
} else { } else {
if (currentBlock.Instructions[1].MatchBranch(out nextBlock)) {
// success
} else if (currentBlock.Instructions[1].MatchLeave(out blockContainer)) {
// success
} else {
return null;
}
}
if (!MatchStringEqualityComparison(condition, switchVariable, out value)) {
return null; return null;
} }
return nextBlock ?? (ILInstruction)blockContainer;
} }
/// <summary> /// <summary>
@ -836,8 +849,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad))) MatchComputeStringHashCall(switchBlockInstructions[switchBlockInstructionsOffset], switchValueVar, out LdLoc switchValueLoad)))
return false; return false;
var stringValues = new List<(int Index, string Value, ILInstruction TargetBlockOrLeave)>(); var stringValues = new List<(string Value, ILInstruction TargetBlockOrLeave)>();
int index = 0;
SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count());
Block exitOrDefaultBlock = null; Block exitOrDefaultBlock = null;
foreach (var section in switchInst.Sections) { foreach (var section in switchInst.Sections) {
@ -846,20 +858,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!section.Body.MatchBranch(out Block target)) if (!section.Body.MatchBranch(out Block target))
return false; return false;
string stringValue; string stringValue;
bool emptyStringEqualsNull;
if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) { if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) {
stringValue = ""; stringValue = "";
} else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue)) { emptyStringEqualsNull = false;
} else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue, out emptyStringEqualsNull)) {
return false; return false;
} }
if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock)
return false; return false;
exitOrDefaultBlock = currentExitBlock; exitOrDefaultBlock = currentExitBlock;
stringValues.Add((index++, stringValue, targetOrLeave)); if (emptyStringEqualsNull && string.IsNullOrEmpty(stringValue)) {
stringValues.Add((null, targetOrLeave));
stringValues.Add((string.Empty, targetOrLeave));
} else {
stringValues.Add((stringValue, targetOrLeave));
}
} }
if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) { if (nullValueCaseBlock != null && exitOrDefaultBlock != nullValueCaseBlock) {
stringValues.Add((index++, null, nullValueCaseBlock)); stringValues.Add((null, nullValueCaseBlock));
} }
ILInstruction switchValueInst = switchValueLoad; ILInstruction switchValueInst = switchValueLoad;
@ -914,13 +933,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
SwitchInstruction ReplaceWithSwitchInstruction(int offset) SwitchInstruction ReplaceWithSwitchInstruction(int offset)
{ {
var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); var defaultLabel = new LongSet(new LongInterval(0, stringValues.Count)).Invert();
var values = new string[stringValues.Count]; var values = new string[stringValues.Count];
var sections = new SwitchSection[stringValues.Count]; var sections = new SwitchSection[stringValues.Count];
foreach (var (idx, (label, value, bodyInstruction)) in stringValues.WithIndex()) { foreach (var (idx, (value, bodyInstruction)) in stringValues.WithIndex()) {
values[idx] = value; values[idx] = value;
var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction; var body = bodyInstruction is Block b ? new Branch(b) : bodyInstruction;
sections[idx] = new SwitchSection { Labels = new LongSet(label), Body = body }; sections[idx] = new SwitchSection { Labels = new LongSet(idx), Body = body };
} }
var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, values)); var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, values));
newSwitch.Sections.AddRange(sections); newSwitch.Sections.AddRange(sections);
@ -935,26 +954,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// if (call op_Equality(ldloc switchValueVar, stringValue)) br body /// if (call op_Equality(ldloc switchValueVar, stringValue)) br body
/// br exit /// br exit
/// </summary> /// </summary>
bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue) bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue, out bool emptyStringEqualsNull)
{ {
bodyOrLeave = null; bodyOrLeave = null;
defaultOrExitBlock = null; defaultOrExitBlock = null;
stringValue = null; stringValue = null;
emptyStringEqualsNull = false;
if (target.Instructions.Count != 2) if (target.Instructions.Count != 2)
return false; return false;
if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch))
return false; return false;
ILInstruction exitBranch; ILInstruction exitBranch = target.Instructions[1];
// Handle negated conditions first: // Handle negated conditions first:
if (condition.MatchLogicNot(out var expr)) { while (condition.MatchLogicNot(out var expr)) {
exitBranch = bodyBranch; ExtensionMethods.Swap(ref exitBranch, ref bodyBranch);
bodyBranch = target.Instructions[1];
condition = expr; condition = expr;
} else {
exitBranch = target.Instructions[1];
} }
if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue, out bool isVBCompareString))
return false; return false;
if (isVBCompareString) {
ExtensionMethods.Swap(ref exitBranch, ref bodyBranch);
emptyStringEqualsNull = true;
}
if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _))) if (!(exitBranch.MatchBranch(out defaultOrExitBlock) || exitBranch.MatchLeave(out _)))
return false; return false;
if (bodyBranch.MatchLeave(out _)) { if (bodyBranch.MatchLeave(out _)) {
@ -1054,25 +1075,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))' /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
/// or 'comp(ldloc(variable) == ldnull)' /// or 'comp(ldloc(variable) == ldnull)'
/// </summary> /// </summary>
bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue, out bool isVBCompareString)
{ {
return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable; return MatchStringEqualityComparison(condition, out var v, out stringValue, out isVBCompareString) && v == variable;
} }
/// <summary> /// <summary>
/// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))' /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))'
/// or 'comp(ldloc(variable) == ldnull)' /// or 'comp(ldloc(variable) == ldnull)'
/// </summary> /// </summary>
bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue) bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue, out bool isVBCompareString)
{ {
stringValue = null; stringValue = null;
variable = null; variable = null;
ILInstruction left, right; isVBCompareString = false;
if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality" while (condition is Comp comp && comp.Kind == ComparisonKind.Inequality && comp.Right.MatchLdcI4(0)) {
&& c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2) // if (x != 0) == if (x)
{ condition = comp.Left;
left = c.Arguments[0]; }
right = c.Arguments[1]; if (condition is Call c) {
ILInstruction left, right;
if (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];
} else if (c.Method.IsStatic && c.Method.Name == "CompareString"
&& c.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators"
&& c.Arguments.Count == 3) {
left = c.Arguments[0];
right = c.Arguments[1];
// VB CompareString(): return 0 on equality -> condition is effectively negated.
// Also, the empty string is considered equal to null.
isVBCompareString = true;
if (!c.Arguments[2].MatchLdcI4(0)) {
// Option Compare Text: case insensitive comparison is not supported in C#
return false;
}
} else {
return false;
}
return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue); return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue);
} else if (condition.MatchCompEqualsNull(out var arg)) { } else if (condition.MatchCompEqualsNull(out var arg)) {
stringValue = null; stringValue = null;

Loading…
Cancel
Save