From f4b00e310c1bf3cf8611f8c96de150cf0eceea00 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 10 Mar 2018 20:48:34 +0100 Subject: [PATCH] Avoid redundant casts around 'as' conversions of type parameters. --- .../TestCases/Pretty/Generics.cs | 30 +++++ .../TestCases/Pretty/Generics.il | 116 +++++++++++++++++- .../TestCases/Pretty/Generics.opt.il | 80 +++++++++++- .../TestCases/Pretty/Generics.opt.roslyn.il | 74 ++++++++++- .../TestCases/Pretty/Generics.roslyn.il | 110 ++++++++++++++++- .../CSharp/ExpressionBuilder.cs | 12 ++ 6 files changed, 408 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs index b8b3be8d3..8fdd4dacc 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs @@ -41,6 +41,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return (T)(BaseClass)d; } + public TTarget GenericAsGeneric(TSource source) where TTarget : class + { + return source as TTarget; + } + + public TTarget? GenericAsNullable(TSource source) where TTarget : struct + { + return source as TTarget?; + } + + public TTarget ObjectAsGeneric(object source) where TTarget : class + { + return source as TTarget; + } + + public TTarget? ObjectAsNullable(object source) where TTarget : struct + { + return source as TTarget?; + } + + public TTarget IntAsGeneric(int source) where TTarget : class + { + return source as TTarget; + } + + public TTarget? IntAsNullable(int source) where TTarget : struct + { + return source as TTarget?; + } + public T New() where T : new() { return new T(); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il index ec77c5fe2..c835b44a6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly xjme13tr +.assembly hdkktc2j { .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 xjme13tr.dll -// MVID: {46E33A68-F4FB-4A9A-BF39-6043E66E8EF6} +.module hdkktc2j.dll +// MVID: {E1E1D350-F9F6-4498-A05D-8F728F6F95CE} .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: 0x04990000 +// Image base: 0x04410000 // =============== CLASS MEMBERS DECLARATION =================== @@ -108,6 +108,112 @@ IL_000b: ret } // end of method Generics::CastToTypeParameter + .method public hidebysig instance !!TTarget + GenericAsGeneric(!!TSource source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box !!TSource + IL_0007: isinst !!TTarget + IL_000c: unbox.any !!TTarget + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::GenericAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + GenericAsNullable(!!TSource source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box !!TSource + IL_0007: isinst valuetype [mscorlib]System.Nullable`1 + IL_000c: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::GenericAsNullable + + .method public hidebysig instance !!TTarget + ObjectAsGeneric(object source) cil managed + { + // Code size 17 (0x11) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: isinst !!TTarget + IL_0007: unbox.any !!TTarget + IL_000c: stloc.0 + IL_000d: br.s IL_000f + + IL_000f: ldloc.0 + IL_0010: ret + } // end of method Generics::ObjectAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + ObjectAsNullable(object source) cil managed + { + // Code size 17 (0x11) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: isinst valuetype [mscorlib]System.Nullable`1 + IL_0007: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_000c: stloc.0 + IL_000d: br.s IL_000f + + IL_000f: ldloc.0 + IL_0010: ret + } // end of method Generics::ObjectAsNullable + + .method public hidebysig instance !!TTarget + IntAsGeneric(int32 source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box [mscorlib]System.Int32 + IL_0007: isinst !!TTarget + IL_000c: unbox.any !!TTarget + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::IntAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + IntAsNullable(int32 source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box [mscorlib]System.Int32 + IL_0007: isinst valuetype [mscorlib]System.Nullable`1 + IL_000c: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::IntAsNullable + .method public hidebysig instance !!T New<.ctor T>() cil managed { // Code size 39 (0x27) @@ -204,4 +310,4 @@ // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** -// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Generics.res +// WARNING: Created Win32 resource file C:\work\ILSpy\ICSharpCode.Decompiler.Tests\bin\Debug\net46\../../../TestCases/Pretty\Generics.res diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il index e743e9945..9ec6d12fe 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '43fwrtdk' +.assembly qmjsj0bc { .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 '43fwrtdk.dll' -// MVID: {9D3BB32A-C2A7-4153-BBA3-FDE1FA41BC2E} +.module qmjsj0bc.dll +// MVID: {A13EDBC1-FA83-4831-BCEB-F1A730B462DC} .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: 0x03A50000 +// Image base: 0x03640000 // =============== CLASS MEMBERS DECLARATION =================== @@ -101,6 +101,76 @@ IL_0006: ret } // end of method Generics::CastToTypeParameter + .method public hidebysig instance !!TTarget + GenericAsGeneric(!!TSource source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box !!TSource + IL_0006: isinst !!TTarget + IL_000b: unbox.any !!TTarget + IL_0010: ret + } // end of method Generics::GenericAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + GenericAsNullable(!!TSource source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box !!TSource + IL_0006: isinst valuetype [mscorlib]System.Nullable`1 + IL_000b: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0010: ret + } // end of method Generics::GenericAsNullable + + .method public hidebysig instance !!TTarget + ObjectAsGeneric(object source) cil managed + { + // Code size 12 (0xc) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: isinst !!TTarget + IL_0006: unbox.any !!TTarget + IL_000b: ret + } // end of method Generics::ObjectAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + ObjectAsNullable(object source) cil managed + { + // Code size 12 (0xc) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: isinst valuetype [mscorlib]System.Nullable`1 + IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_000b: ret + } // end of method Generics::ObjectAsNullable + + .method public hidebysig instance !!TTarget + IntAsGeneric(int32 source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box [mscorlib]System.Int32 + IL_0006: isinst !!TTarget + IL_000b: unbox.any !!TTarget + IL_0010: ret + } // end of method Generics::IntAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + IntAsNullable(int32 source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box [mscorlib]System.Int32 + IL_0006: isinst valuetype [mscorlib]System.Nullable`1 + IL_000b: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0010: ret + } // end of method Generics::IntAsNullable + .method public hidebysig instance !!T New<.ctor T>() cil managed { // Code size 32 (0x20) @@ -173,4 +243,4 @@ // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** -// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Generics.opt.res +// WARNING: Created Win32 resource file C:\work\ILSpy\ICSharpCode.Decompiler.Tests\bin\Debug\net46\../../../TestCases/Pretty\Generics.opt.res diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il index 6691faba4..07fc1ad34 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Generics.dll -// MVID: {3BDC6EEC-5C6F-4665-B558-CFF1997F2B0C} +// MVID: {DCA885B4-F72F-4C1C-9E2F-88C7BDB5DA96} .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: 0x03340000 +// Image base: 0x04850000 // =============== CLASS MEMBERS DECLARATION =================== @@ -105,6 +105,76 @@ IL_0006: ret } // end of method Generics::CastToTypeParameter + .method public hidebysig instance !!TTarget + GenericAsGeneric(!!TSource source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box !!TSource + IL_0006: isinst !!TTarget + IL_000b: unbox.any !!TTarget + IL_0010: ret + } // end of method Generics::GenericAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + GenericAsNullable(!!TSource source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box !!TSource + IL_0006: isinst valuetype [mscorlib]System.Nullable`1 + IL_000b: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0010: ret + } // end of method Generics::GenericAsNullable + + .method public hidebysig instance !!TTarget + ObjectAsGeneric(object source) cil managed + { + // Code size 12 (0xc) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: isinst !!TTarget + IL_0006: unbox.any !!TTarget + IL_000b: ret + } // end of method Generics::ObjectAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + ObjectAsNullable(object source) cil managed + { + // Code size 12 (0xc) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: isinst valuetype [mscorlib]System.Nullable`1 + IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_000b: ret + } // end of method Generics::ObjectAsNullable + + .method public hidebysig instance !!TTarget + IntAsGeneric(int32 source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box [mscorlib]System.Int32 + IL_0006: isinst !!TTarget + IL_000b: unbox.any !!TTarget + IL_0010: ret + } // end of method Generics::IntAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + IntAsNullable(int32 source) cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: box [mscorlib]System.Int32 + IL_0006: isinst valuetype [mscorlib]System.Nullable`1 + IL_000b: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0010: ret + } // end of method Generics::IntAsNullable + .method public hidebysig instance !!T New<.ctor T>() cil managed { // Code size 6 (0x6) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il index d7b3ad497..4ffcb7e35 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Generics.dll -// MVID: {90883852-759A-4D56-85B7-2BCDAEFC00E9} +// MVID: {80E8D6C6-7E47-4ECE-9DF6-2DB001269AB9} .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: 0x02FD0000 +// Image base: 0x038F0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -115,6 +115,112 @@ IL_000b: ret } // end of method Generics::CastToTypeParameter + .method public hidebysig instance !!TTarget + GenericAsGeneric(!!TSource source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box !!TSource + IL_0007: isinst !!TTarget + IL_000c: unbox.any !!TTarget + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::GenericAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + GenericAsNullable(!!TSource source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box !!TSource + IL_0007: isinst valuetype [mscorlib]System.Nullable`1 + IL_000c: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::GenericAsNullable + + .method public hidebysig instance !!TTarget + ObjectAsGeneric(object source) cil managed + { + // Code size 17 (0x11) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: isinst !!TTarget + IL_0007: unbox.any !!TTarget + IL_000c: stloc.0 + IL_000d: br.s IL_000f + + IL_000f: ldloc.0 + IL_0010: ret + } // end of method Generics::ObjectAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + ObjectAsNullable(object source) cil managed + { + // Code size 17 (0x11) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: isinst valuetype [mscorlib]System.Nullable`1 + IL_0007: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_000c: stloc.0 + IL_000d: br.s IL_000f + + IL_000f: ldloc.0 + IL_0010: ret + } // end of method Generics::ObjectAsNullable + + .method public hidebysig instance !!TTarget + IntAsGeneric(int32 source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (!!TTarget V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box [mscorlib]System.Int32 + IL_0007: isinst !!TTarget + IL_000c: unbox.any !!TTarget + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::IntAsGeneric + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + IntAsNullable(int32 source) cil managed + { + // Code size 22 (0x16) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: box [mscorlib]System.Int32 + IL_0007: isinst valuetype [mscorlib]System.Nullable`1 + IL_000c: unbox.any valuetype [mscorlib]System.Nullable`1 + IL_0011: stloc.0 + IL_0012: br.s IL_0014 + + IL_0014: ldloc.0 + IL_0015: ret + } // end of method Generics::IntAsNullable + .method public hidebysig instance !!T New<.ctor T>() cil managed { // Code size 11 (0xb) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 23695e14c..798f37bf4 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -210,6 +210,14 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitIsInst(IsInst inst, TranslationContext context) { var arg = Translate(inst.Argument); + if (arg.Expression is CastExpression cast + && arg.Type.IsKnownType(KnownTypeCode.Object) + && arg.ResolveResult is ConversionResolveResult crr + && crr.Conversion.IsBoxingConversion) { + // When 'as' used with value type or type parameter, + // the C# compiler implicitly boxes the input. + arg = arg.UnwrapChild(cast.Expression); + } return new AsExpression(arg.Expression, ConvertType(inst.Type)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast)); @@ -1703,6 +1711,10 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitUnboxAny(UnboxAny inst, TranslationContext context) { var arg = Translate(inst.Argument); + if (arg.Type.Equals(inst.Type) && inst.Argument.OpCode == OpCode.IsInst) { + // isinst followed by unbox.any of the same type is used for as-casts to generic types + return arg.WithILInstruction(inst); + } if (arg.Type.IsReferenceType != true) { // ensure we treat the input as a reference type arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this);