diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs index 2436ad5ef..10bef3a73 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs @@ -215,6 +215,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return a == null; } + public bool CompareStringsByValue(string a, string b) + { + return a == b; + } + + public bool CompareStringsByReference(string a, string b) + { + return (object)a == b; + } + + public bool CompareStringWithNull(string a) + { + return a == null; + } + public decimal ImplicitConversionToDecimal(byte v) { return v; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il index 19e483a77..49adfa795 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly t2movxx2 +.assembly fpugcpej { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module t2movxx2.dll -// MVID: {F28F6C6C-94AA-4D5E-B9EE-907881B3E7E4} +.module fpugcpej.dll +// MVID: {F42443CE-6C18-4149-B04E-77437B350B2C} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00690000 +// Image base: 0x01650000 // =============== CLASS MEMBERS DECLARATION =================== @@ -735,6 +735,60 @@ IL_0009: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance bool + CompareStringsByValue(string a, + string b) cil managed + { + // Code size 13 (0xd) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0008: stloc.0 + IL_0009: br.s IL_000b + + IL_000b: ldloc.0 + IL_000c: ret + } // end of method TypeAnalysisTests::CompareStringsByValue + + .method public hidebysig instance bool + CompareStringsByReference(string a, + string b) cil managed + { + // Code size 10 (0xa) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ceq + IL_0005: stloc.0 + IL_0006: br.s IL_0008 + + IL_0008: ldloc.0 + IL_0009: ret + } // end of method TypeAnalysisTests::CompareStringsByReference + + .method public hidebysig instance bool + CompareStringWithNull(string a) cil managed + { + // Code size 10 (0xa) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldnull + IL_0003: ceq + IL_0005: stloc.0 + IL_0006: br.s IL_0008 + + IL_0008: ldloc.0 + IL_0009: ret + } // end of method TypeAnalysisTests::CompareStringWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal ImplicitConversionToDecimal(uint8 v) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il index 4ecf057c9..74e325a50 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '0nmkvdgs' +.assembly '31uigzt1' { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module '0nmkvdgs.dll' -// MVID: {E543BCF9-C441-4A6D-A25F-19C170B765C0} +.module '31uigzt1.dll' +// MVID: {77B7D9D4-A6DF-4CC8-B1E3-7B3B553633FF} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x00C50000 +// Image base: 0x01360000 // =============== CLASS MEMBERS DECLARATION =================== @@ -510,6 +510,42 @@ IL_0004: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance bool + CompareStringsByValue(string a, + string b) cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0007: ret + } // end of method TypeAnalysisTests::CompareStringsByValue + + .method public hidebysig instance bool + CompareStringsByReference(string a, + string b) cil managed + { + // Code size 5 (0x5) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ceq + IL_0004: ret + } // end of method TypeAnalysisTests::CompareStringsByReference + + .method public hidebysig instance bool + CompareStringWithNull(string a) cil managed + { + // Code size 5 (0x5) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldnull + IL_0002: ceq + IL_0004: ret + } // end of method TypeAnalysisTests::CompareStringWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal ImplicitConversionToDecimal(uint8 v) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il index 405008458..7856b67e0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module TypeAnalysisTests.dll -// MVID: {7D2FE161-359E-42EC-B423-26BEB23F890D} +// MVID: {6215C6B8-0FF3-4C9E-999F-BE9720986F05} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01730000 +// Image base: 0x03030000 // =============== CLASS MEMBERS DECLARATION =================== @@ -512,6 +512,42 @@ IL_0004: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance bool + CompareStringsByValue(string a, + string b) cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0007: ret + } // end of method TypeAnalysisTests::CompareStringsByValue + + .method public hidebysig instance bool + CompareStringsByReference(string a, + string b) cil managed + { + // Code size 5 (0x5) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ceq + IL_0004: ret + } // end of method TypeAnalysisTests::CompareStringsByReference + + .method public hidebysig instance bool + CompareStringWithNull(string a) cil managed + { + // Code size 5 (0x5) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldnull + IL_0002: ceq + IL_0004: ret + } // end of method TypeAnalysisTests::CompareStringWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal ImplicitConversionToDecimal(uint8 v) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il index 66f4f274d..d1476c7eb 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module TypeAnalysisTests.dll -// MVID: {6A008C4E-2BC5-45CA-93FB-93A12CD0BC69} +// MVID: {3A10F4E7-88B8-4945-8775-A3643132F64B} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x01320000 +// Image base: 0x01720000 // =============== CLASS MEMBERS DECLARATION =================== @@ -734,6 +734,60 @@ IL_0009: ret } // end of method TypeAnalysisTests::CompareDelegateWithNull + .method public hidebysig instance bool + CompareStringsByValue(string a, + string b) cil managed + { + // Code size 13 (0xd) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0008: stloc.0 + IL_0009: br.s IL_000b + + IL_000b: ldloc.0 + IL_000c: ret + } // end of method TypeAnalysisTests::CompareStringsByValue + + .method public hidebysig instance bool + CompareStringsByReference(string a, + string b) cil managed + { + // Code size 10 (0xa) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ceq + IL_0005: stloc.0 + IL_0006: br.s IL_0008 + + IL_0008: ldloc.0 + IL_0009: ret + } // end of method TypeAnalysisTests::CompareStringsByReference + + .method public hidebysig instance bool + CompareStringWithNull(string a) cil managed + { + // Code size 10 (0xa) + .maxstack 2 + .locals init (bool V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldnull + IL_0003: ceq + IL_0005: stloc.0 + IL_0006: br.s IL_0008 + + IL_0008: ldloc.0 + IL_0009: ret + } // end of method TypeAnalysisTests::CompareStringWithNull + .method public hidebysig instance valuetype [mscorlib]System.Decimal ImplicitConversionToDecimal(uint8 v) cil managed { diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index da07fc688..5ca393c28 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -527,17 +527,20 @@ namespace ICSharpCode.Decompiler.CSharp left = AdjustConstantExpressionToType(left, right.Type); right = AdjustConstantExpressionToType(right, left.Type); - if (left.Type.Kind == TypeKind.Delegate && right.Type.Kind == TypeKind.Null - || left.Type.Kind == TypeKind.Null && right.Type.Kind == TypeKind.Delegate) - { - // When comparing a delegate with null, the C# compiler generates a reference comparison. + if (IsSpecialCasedReferenceComparisonWithNull(left, right)) { + // When comparing a string/delegate with null, the C# compiler generates a reference comparison. negateOutput = false; return CreateBuiltinBinaryOperator(left, inst.Kind.ToBinaryOperatorType(), right) .WithILInstruction(inst); } - var rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) - as OperatorResolveResult; + OperatorResolveResult rr; + if (left.Type.IsKnownType(KnownTypeCode.String) && right.Type.IsKnownType(KnownTypeCode.String)) { + rr = null; // it's a string comparison by-value, which is not a reference comparison + } else { + rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult) + as OperatorResolveResult; + } if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null || NullableType.GetUnderlyingType(rr.Operands[0].Type).GetStackType() != inst.InputType) { @@ -583,6 +586,14 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(rr); } + bool IsSpecialCasedReferenceComparisonWithNull(TranslatedExpression lhs, TranslatedExpression rhs) + { + if (lhs.Type.Kind == TypeKind.Null) + ExtensionMethods.Swap(ref lhs, ref rhs); + return rhs.Type.Kind == TypeKind.Null + && (lhs.Type.Kind == TypeKind.Delegate || lhs.Type.IsKnownType(KnownTypeCode.String)); + } + ExpressionWithResolveResult CreateBuiltinBinaryOperator( TranslatedExpression left, BinaryOperatorType type, TranslatedExpression right, bool checkForOverflow = false)