Browse Source

Convert TransformAssignment into a statement transform and add support for inline property assignments.

pull/960/head
Daniel Grunwald 8 years ago
parent
commit
4c5f0b7e9c
  1. 21
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs
  2. 118
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il
  3. 94
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il
  4. 7
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  5. 76
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  6. 15
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 62
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  8. 2
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  9. 250
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

21
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs

@ -28,6 +28,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -28,6 +28,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private int[] field3;
private short field4;
public int InstanceProperty {
get;
set;
}
public static int StaticProperty {
get;
set;
}
public void SimpleInlineWithLocals()
{
int value;
@ -56,7 +65,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -56,7 +65,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
this.UseShort(this.field4 = this.UseShort(0));
Console.WriteLine(this.field4);
}
public short UseShort(short s)
{
Console.WriteLine(s);
@ -112,5 +121,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -112,5 +121,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return this.GetArray()[this.GetIndex()] = this.GetValue(this.GetIndex());
}
public int StaticPropertyTest()
{
return InlineAssignmentTest.StaticProperty = this.GetIndex();
}
public int InstancePropertyTest()
{
return this.InstanceProperty = this.GetIndex();
}
}
}

118
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly w1xqybg1
.assembly dcbsz5ie
{
.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 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module w1xqybg1.dll
// MVID: {1BE5829A-0D5E-43DC-8C02-E3012B1780A2}
.module dcbsz5ie.dll
// MVID: {CFA464E9-510B-40EC-AD90-3FB26B76D1A6}
.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: 0x033B0000
// Image base: 0x00690000
// =============== CLASS MEMBERS DECLARATION ===================
@ -40,6 +40,64 @@ @@ -40,6 +40,64 @@
.field private static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest field2
.field private int32[] field3
.field private int16 field4
.field private int32 '<InstanceProperty>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.field private static int32 '<StaticProperty>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname instance int32
get_InstanceProperty() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 11 (0xb)
.maxstack 1
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<InstanceProperty>k__BackingField'
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // end of method InlineAssignmentTest::get_InstanceProperty
.method public hidebysig specialname instance void
set_InstanceProperty(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<InstanceProperty>k__BackingField'
IL_0007: ret
} // end of method InlineAssignmentTest::set_InstanceProperty
.method public hidebysig specialname static
int32 get_StaticProperty() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 10 (0xa)
.maxstack 1
.locals init (int32 V_0)
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<StaticProperty>k__BackingField'
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
} // end of method InlineAssignmentTest::get_StaticProperty
.method public hidebysig specialname static
void set_StaticProperty(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<StaticProperty>k__BackingField'
IL_0006: ret
} // end of method InlineAssignmentTest::set_StaticProperty
.method public hidebysig instance void
SimpleInlineWithLocals() cil managed
{
@ -378,6 +436,48 @@ @@ -378,6 +436,48 @@
IL_0021: ret
} // end of method InlineAssignmentTest::ArrayUsageWithMethods
.method public hidebysig instance int32
StaticPropertyTest() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::GetIndex()
IL_0007: dup
IL_0008: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_StaticProperty(int32)
IL_000d: nop
IL_000e: stloc.0
IL_000f: br.s IL_0011
IL_0011: ldloc.0
IL_0012: ret
} // end of method InlineAssignmentTest::StaticPropertyTest
.method public hidebysig instance int32
InstancePropertyTest() cil managed
{
// Code size 22 (0x16)
.maxstack 3
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::GetIndex()
IL_0008: dup
IL_0009: stloc.1
IL_000a: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_InstanceProperty(int32)
IL_000f: nop
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: br.s IL_0014
IL_0014: ldloc.0
IL_0015: ret
} // end of method InlineAssignmentTest::InstancePropertyTest
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
@ -388,6 +488,16 @@ @@ -388,6 +488,16 @@
IL_0006: ret
} // end of method InlineAssignmentTest::.ctor
.property instance int32 InstanceProperty()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::get_InstanceProperty()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_InstanceProperty(int32)
} // end of property InlineAssignmentTest::InstanceProperty
.property int32 StaticProperty()
{
.get int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::get_StaticProperty()
.set void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_StaticProperty(int32)
} // end of property InlineAssignmentTest::StaticProperty
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest

94
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly f1ibwsev
.assembly idzuatf2
{
.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 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module f1ibwsev.dll
// MVID: {8DB4E6AA-A0C2-49F3-8E9C-A28CE30F145A}
.module idzuatf2.dll
// MVID: {0E6B135E-0CD7-486D-88EB-95340860F028}
.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: 0x01400000
// Image base: 0x02A70000
// =============== CLASS MEMBERS DECLARATION ===================
@ -40,6 +40,54 @@ @@ -40,6 +40,54 @@
.field private static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest field2
.field private int32[] field3
.field private int16 field4
.field private int32 '<InstanceProperty>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.field private static int32 '<StaticProperty>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname instance int32
get_InstanceProperty() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<InstanceProperty>k__BackingField'
IL_0006: ret
} // end of method InlineAssignmentTest::get_InstanceProperty
.method public hidebysig specialname instance void
set_InstanceProperty(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<InstanceProperty>k__BackingField'
IL_0007: ret
} // end of method InlineAssignmentTest::set_InstanceProperty
.method public hidebysig specialname static
int32 get_StaticProperty() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 6 (0x6)
.maxstack 8
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<StaticProperty>k__BackingField'
IL_0005: ret
} // end of method InlineAssignmentTest::get_StaticProperty
.method public hidebysig specialname static
void set_StaticProperty(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::'<StaticProperty>k__BackingField'
IL_0006: ret
} // end of method InlineAssignmentTest::set_StaticProperty
.method public hidebysig instance void
SimpleInlineWithLocals() cil managed
{
@ -309,6 +357,34 @@ @@ -309,6 +357,34 @@
IL_001c: ret
} // end of method InlineAssignmentTest::ArrayUsageWithMethods
.method public hidebysig instance int32
StaticPropertyTest() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::GetIndex()
IL_0006: dup
IL_0007: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_StaticProperty(int32)
IL_000c: ret
} // end of method InlineAssignmentTest::StaticPropertyTest
.method public hidebysig instance int32
InstancePropertyTest() cil managed
{
// Code size 16 (0x10)
.maxstack 3
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::GetIndex()
IL_0007: dup
IL_0008: stloc.0
IL_0009: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_InstanceProperty(int32)
IL_000e: ldloc.0
IL_000f: ret
} // end of method InlineAssignmentTest::InstancePropertyTest
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
@ -319,6 +395,16 @@ @@ -319,6 +395,16 @@
IL_0006: ret
} // end of method InlineAssignmentTest::.ctor
.property instance int32 InstanceProperty()
{
.get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::get_InstanceProperty()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_InstanceProperty(int32)
} // end of property InlineAssignmentTest::InstanceProperty
.property int32 StaticProperty()
{
.get int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::get_StaticProperty()
.set void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::set_StaticProperty(int32)
} // end of property InlineAssignmentTest::StaticProperty
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest

7
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -109,9 +109,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -109,9 +109,6 @@ namespace ICSharpCode.Decompiler.CSharp
// CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform
// and must run before NullCoalescingTransform
new CachedDelegateInitialization(),
new ILInlining(),
new TransformAssignment(), // must run before CopyPropagation
new CopyPropagation(),
new StatementTransform(
// per-block transforms that depend on each other, and thus need to
// run interleaved (statement by statement).
@ -121,11 +118,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -121,11 +118,13 @@ namespace ICSharpCode.Decompiler.CSharp
// Inlining must be first, because it doesn't trigger re-runs.
// Any other transform that opens up new inlining opportunities should call RequestRerun().
new ExpressionTransforms(),
new TransformAssignment(), // inline and compound assignments
new NullCoalescingTransform(),
new NullableLiftingStatementTransform(),
new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers()
)
),
new CopyPropagation()
}
},
new ProxyCallReplacer(),

76
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -47,28 +47,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -47,28 +47,32 @@ namespace ICSharpCode.Decompiler.CSharp
public TranslatedExpression Build(CallInstruction inst)
{
IMethod method = inst.Method;
if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj, true)) {
return HandleDelegateConstruction(newobj);
}
return Build(inst.OpCode, inst.Method, inst.Arguments).WithILInstruction(inst);
}
public ExpressionWithResolveResult Build(OpCode callOpCode, IMethod method, IReadOnlyList<ILInstruction> callArguments)
{
// Used for Call, CallVirt and NewObj
TranslatedExpression target;
if (inst.OpCode == OpCode.NewObj) {
if (IL.Transforms.DelegateConstruction.IsDelegateConstruction((NewObj)inst, true)) {
return HandleDelegateConstruction(inst);
}
if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else {
target = expressionBuilder.TranslateTarget(method, inst.Arguments.FirstOrDefault(), inst.OpCode == OpCode.Call);
target = expressionBuilder.TranslateTarget(method, callArguments.FirstOrDefault(), callOpCode == OpCode.Call);
}
int firstParamIndex = (method.IsStatic || inst.OpCode == OpCode.NewObj) ? 0 : 1;
int firstParamIndex = (method.IsStatic || callOpCode == OpCode.NewObj) ? 0 : 1;
// Translate arguments to the expected parameter types
var arguments = new List<TranslatedExpression>(method.Parameters.Count);
Debug.Assert(inst.Arguments.Count == firstParamIndex + method.Parameters.Count);
Debug.Assert(callArguments.Count == firstParamIndex + method.Parameters.Count);
var expectedParameters = method.Parameters.ToList();
bool isExpandedForm = false;
for (int i = 0; i < method.Parameters.Count; i++) {
var parameter = expectedParameters[i];
var arg = expressionBuilder.Translate(inst.Arguments[firstParamIndex + i]);
var arg = expressionBuilder.Translate(callArguments[firstParamIndex + i]);
if (parameter.IsParams && i + 1 == method.Parameters.Count) {
// Parameter is marked params
// If the argument is an array creation, inline all elements into the call and add missing default values.
@ -90,7 +94,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -90,7 +94,7 @@ namespace ICSharpCode.Decompiler.CSharp
expandedArguments.Add(expressionBuilder.GetDefaultValueExpression(elementType).WithoutILInstruction());
}
}
if (IsUnambiguousCall(inst, target, method, Array.Empty<IType>(), expandedArguments) == OverloadResolutionErrors.None) {
if (IsUnambiguousCall(callOpCode, target, method, Array.Empty<IType>(), expandedArguments) == OverloadResolutionErrors.None) {
isExpandedForm = true;
expectedParameters = expandedParameters;
arguments = expandedArguments.SelectList(a => new TranslatedExpression(a.Expression.Detach()));
@ -124,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -124,7 +128,7 @@ namespace ICSharpCode.Decompiler.CSharp
ResolveResult rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults, isExpandedForm: isExpandedForm);
if (inst.OpCode == OpCode.NewObj) {
if (callOpCode == OpCode.NewObj) {
if (settings.AnonymousTypes && method.DeclaringType.IsAnonymousType()) {
var argumentExpressions = arguments.SelectArray(arg => arg.Expression);
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
@ -140,11 +144,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -140,11 +144,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
return atce
.WithILInstruction(inst)
.WithRR(rr);
} else {
if (IsUnambiguousCall(inst, target, method, Array.Empty<IType>(), arguments) != OverloadResolutionErrors.None) {
if (IsUnambiguousCall(callOpCode, target, method, Array.Empty<IType>(), arguments) != OverloadResolutionErrors.None) {
for (int i = 0; i < arguments.Count; i++) {
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) {
if (arguments[i].Expression is LambdaExpression lambda) {
@ -155,20 +158,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -155,20 +158,20 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
}
return new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), arguments.SelectArray(arg => arg.Expression))
.WithILInstruction(inst).WithRR(rr);
return new ObjectCreateExpression(expressionBuilder.ConvertType(method.DeclaringType), arguments.SelectArray(arg => arg.Expression))
.WithRR(rr);
}
} else {
int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0);
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) {
return HandleAccessorCall(inst, target, method, arguments.ToList());
return HandleAccessorCall(callOpCode == OpCode.CallVirt, target, method, arguments.ToList());
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate) {
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithILInstruction(inst).WithRR(rr);
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithRR(rr);
} else if (IsDelegateEqualityComparison(method, arguments)) {
return HandleDelegateEqualityComparison(method, arguments)
.WithILInstruction(inst).WithRR(rr);
.WithRR(rr);
} else if (method.IsOperator && method.Name == "op_Implicit" && arguments.Count == 1) {
return HandleImplicitConversion(inst, arguments[0]);
return HandleImplicitConversion(method, arguments[0]);
} else {
bool requireTypeArguments = false;
bool targetCasted = false;
@ -176,7 +179,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -176,7 +179,7 @@ namespace ICSharpCode.Decompiler.CSharp
IType[] typeArguments = Array.Empty<IType>();
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(inst, target, method, typeArguments, arguments)) != OverloadResolutionErrors.None) {
while ((errors = IsUnambiguousCall(callOpCode, target, method, typeArguments, arguments)) != OverloadResolutionErrors.None) {
switch (errors) {
case OverloadResolutionErrors.TypeInferenceFailed:
case OverloadResolutionErrors.WrongNumberOfTypeArguments:
@ -213,7 +216,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -213,7 +216,7 @@ namespace ICSharpCode.Decompiler.CSharp
Expression targetExpr = target.Expression;
string methodName = method.Name;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
if (inst.Method.IsExplicitInterfaceImplementation && targetExpr is ThisReferenceExpression) {
if (method.IsExplicitInterfaceImplementation && targetExpr is ThisReferenceExpression) {
targetExpr = new CastExpression(expressionBuilder.ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType), targetExpr);
methodName = method.ImplementedInterfaceMembers[0].Name;
}
@ -221,7 +224,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -221,7 +224,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (requireTypeArguments && (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType())))
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
var argumentExpressions = arguments.Select(arg => arg.Expression);
return new InvocationExpression(mre, argumentExpressions).WithILInstruction(inst).WithRR(rr);
return new InvocationExpression(mre, argumentExpressions).WithRR(rr);
}
}
}
@ -271,28 +274,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -271,28 +274,27 @@ namespace ICSharpCode.Decompiler.CSharp
);
}
private TranslatedExpression HandleImplicitConversion(CallInstruction call, TranslatedExpression argument)
private ExpressionWithResolveResult HandleImplicitConversion(IMethod method, TranslatedExpression argument)
{
var conversions = CSharpConversions.Get(expressionBuilder.compilation);
IType targetType = call.Method.ReturnType;
IType targetType = method.ReturnType;
var conv = conversions.ImplicitConversion(argument.Type, targetType);
if (!(conv.IsUserDefined && conv.Method.Equals(call.Method))) {
if (!(conv.IsUserDefined && conv.Method.Equals(method))) {
// implicit conversion to targetType isn't directly possible, so first insert a cast to the argument type
argument = argument.ConvertTo(call.Method.Parameters[0].Type, expressionBuilder);
argument = argument.ConvertTo(method.Parameters[0].Type, expressionBuilder);
conv = conversions.ImplicitConversion(argument.Type, targetType);
}
return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
.WithILInstruction(call)
.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, conv));
}
OverloadResolutionErrors IsUnambiguousCall(ILInstruction inst, TranslatedExpression target, IMethod method, IType[] typeArguments, IList<TranslatedExpression> arguments)
OverloadResolutionErrors IsUnambiguousCall(OpCode callOpCode, TranslatedExpression target, IMethod method, IType[] typeArguments, IList<TranslatedExpression> arguments)
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var or = new OverloadResolution(resolver.Compilation, arguments.SelectArray(a => a.ResolveResult), typeArguments: typeArguments);
if (inst is NewObj newObj) {
foreach (IMethod ctor in newObj.Method.DeclaringType.GetConstructors()) {
if (lookup.IsAccessible(ctor, allowProtectedAccess: resolver.CurrentTypeDefinition == newObj.Method.DeclaringTypeDefinition)) {
if (callOpCode == OpCode.NewObj) {
foreach (IMethod ctor in method.DeclaringType.GetConstructors()) {
if (lookup.IsAccessible(ctor, allowProtectedAccess: resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition)) {
or.AddCandidate(ctor);
}
}
@ -304,7 +306,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -304,7 +306,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (or.BestCandidateErrors != OverloadResolutionErrors.None)
return or.BestCandidateErrors;
if (!IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), inst.OpCode == OpCode.CallVirt))
if (!IsAppropriateCallTarget(method, or.GetBestCandidateWithSubstitutedTypeArguments(), callOpCode == OpCode.CallVirt))
return OverloadResolutionErrors.AmbiguousMatch;
return OverloadResolutionErrors.None;
}
@ -327,12 +329,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -327,12 +329,12 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
}
TranslatedExpression HandleAccessorCall(ILInstruction inst, TranslatedExpression target, IMethod method, IList<TranslatedExpression> arguments)
ExpressionWithResolveResult HandleAccessorCall(bool isVirtCall, TranslatedExpression target, IMethod method, IList<TranslatedExpression> arguments)
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.AccessorOwner.Name, EmptyList<IType>.Instance, isInvocation: false);
if (result.IsError || (result is MemberResolveResult && !IsAppropriateCallTarget(method.AccessorOwner, ((MemberResolveResult)result).Member, inst.OpCode == OpCode.CallVirt)))
if (result.IsError || (result is MemberResolveResult && !IsAppropriateCallTarget(method.AccessorOwner, ((MemberResolveResult)result).Member, isVirtCall)))
target = target.ConvertTo(method.AccessorOwner.DeclaringType, expressionBuilder);
var rr = new MemberResolveResult(target.ResolveResult, method.AccessorOwner);
@ -356,12 +358,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -356,12 +358,12 @@ namespace ICSharpCode.Decompiler.CSharp
op = AssignmentOperatorType.Subtract;
}
}
return new AssignmentExpression(expr, op, value.Expression).WithILInstruction(inst).WithRR(new TypeResolveResult(method.AccessorOwner.ReturnType));
return new AssignmentExpression(expr, op, value.Expression).WithRR(new TypeResolveResult(method.AccessorOwner.ReturnType));
} else {
if (arguments.Count == 0)
return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name).WithILInstruction(inst).WithRR(rr);
return new MemberReferenceExpression(target.Expression, method.AccessorOwner.Name).WithRR(rr);
else
return new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)).WithILInstruction(inst).WithRR(rr);
return new IndexerExpression(target.Expression, arguments.Select(a => a.Expression)).WithRR(rr);
}
}

15
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1694,11 +1694,26 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1694,11 +1694,26 @@ namespace ICSharpCode.Decompiler.CSharp
return TranslateObjectAndCollectionInitializer(block);
case BlockType.PostfixOperator:
return TranslatePostfixOperator(block);
case BlockType.CallInlineAssign:
return TranslateSetterCallAssignment(block);
default:
return ErrorExpression("Unknown block type: " + block.Type);
}
}
private TranslatedExpression TranslateSetterCallAssignment(Block block)
{
if (!block.MatchInlineAssignBlock(out var call, out var value)) {
// should never happen unless the ILAst is invalid
return ErrorExpression("Error: MatchInlineAssignBlock() returned false");
}
var arguments = call.Arguments.ToList();
arguments[arguments.Count - 1] = value;
return new CallBuilder(this, typeSystem, settings)
.Build(call.OpCode, call.Method, arguments)
.WithILInstruction(call);
}
TranslatedExpression TranslateObjectAndCollectionInitializer(Block block)
{
var stloc = block.Instructions.FirstOrDefault() as StLoc;

62
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -108,8 +108,13 @@ namespace ICSharpCode.Decompiler.IL @@ -108,8 +108,13 @@ namespace ICSharpCode.Decompiler.IL
// only the last instruction may have an unreachable endpoint
Debug.Assert(!Instructions[i].HasFlag(InstructionFlags.EndPointUnreachable));
}
if (this.Type == BlockType.ControlFlow) {
Debug.Assert(finalInstruction.OpCode == OpCode.Nop);
switch (this.Type) {
case BlockType.ControlFlow:
Debug.Assert(finalInstruction.OpCode == OpCode.Nop);
break;
case BlockType.CallInlineAssign:
Debug.Assert(MatchInlineAssignBlock(out _, out _));
break;
}
}
@ -251,13 +256,62 @@ namespace ICSharpCode.Decompiler.IL @@ -251,13 +256,62 @@ namespace ICSharpCode.Decompiler.IL
}
return inst;
}
public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value)
{
call = null;
value = null;
if (this.Type != BlockType.CallInlineAssign)
return false;
if (this.Instructions.Count != 1)
return false;
call = this.Instructions[0] as CallInstruction;
if (call == null || call.Arguments.Count == 0)
return false;
if (!call.Arguments.Last().MatchStLoc(out var tmp, out value))
return false;
if (!(tmp.IsSingleDefinition && tmp.LoadCount == 1))
return false;
return this.FinalInstruction.MatchLdLoc(tmp);
}
}
public enum BlockType {
public enum BlockType
{
/// <summary>
/// Block is used for control flow.
/// All blocks in block containers must have this type.
/// Control flow blocks cannot evaluate to a value (FinalInstruction must be Nop).
/// </summary>
ControlFlow,
/// <summary>
/// Block is used for array initializers, e.g. `new int[] { expr1, expr2 }`.
/// </summary>
ArrayInitializer,
CollectionInitializer,
ObjectInitializer,
PostfixOperator
/// <summary>
/// Block is used for postfix operator on local variable.
/// </summary>
/// <remarks>
/// Postfix operators on non-locals use CompoundAssignmentInstruction with CompoundAssignmentType.EvaluatesToOldValue.
/// </remarks>
PostfixOperator,
/// <summary>
/// Block is used for using the result of a property setter inline.
/// Example: <code>Use(this.Property = value);</code>
/// This is only for inline assignments to property or indexers; other inline assignments work
/// by using the result value of the stloc/stobj instructions.
///
/// Constructed by TransformAssignment.
/// Can be deconstructed using Block.MatchInlineAssignBlock().
/// </summary>
/// <example>
/// Block {
/// call setter(..., stloc s(...))
/// final: ldloc s
/// }
/// </example>
CallInlineAssign
}
}

2
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -107,7 +107,7 @@ namespace ICSharpCode.Decompiler.IL @@ -107,7 +107,7 @@ namespace ICSharpCode.Decompiler.IL
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
{
switch (inst.OpCode) {
case OpCode.LdLoc:
// case OpCode.LdLoc: -- not valid -- does not mark the variable as written to
case OpCode.LdObj:
return true;
case OpCode.Call:

250
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -21,43 +21,49 @@ using System.Diagnostics; @@ -21,43 +21,49 @@ using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Constructs compound assignments and inline assignments.
/// </summary>
public class TransformAssignment : IBlockTransform
public class TransformAssignment : IStatementTransform
{
BlockTransformContext context;
StatementTransformContext context;
void IBlockTransform.Run(Block block, BlockTransformContext context)
void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
{
this.context = context;
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (TransformPostIncDecOperatorOnAddress(block, i) || TransformPostIncDecOnStaticField(block, i) || TransformCSharp4PostIncDecOperatorOnAddress(block, i)) {
block.Instructions.RemoveAt(i);
continue;
}
if (TransformPostIncDecOperator(block, i)) {
block.Instructions.RemoveAt(i);
continue;
}
if (TransformInlineAssignmentStObj(block, i) || TransformInlineAssignmentLocal(block, i))
continue;
if (TransformInlineCompoundAssignmentCall(block, i))
continue;
if (TransformRoslynCompoundAssignmentCall(block, i))
continue;
if (TransformRoslynPostIncDecOperatorOnAddress(block, i))
continue;
/*if (TransformPostIncDecOperatorOnAddress(block, i) || TransformPostIncDecOnStaticField(block, i) || TransformCSharp4PostIncDecOperatorOnAddress(block, i)) {
block.Instructions.RemoveAt(i);
continue;
}
if (TransformPostIncDecOperator(block, i)) {
block.Instructions.RemoveAt(i);
continue;
}*/
if (TransformInlineAssignmentStObjOrCall(block, pos) || TransformInlineAssignmentLocal(block, pos)) {
// both inline assignments create a top-level stloc which might affect inlining
context.RequestRerun();
return;
}
/*
TransformInlineCompoundAssignmentCall(block, pos);
TransformRoslynCompoundAssignmentCall(block, pos);
// TODO: post-increment on local
// post-increment on address (e.g. field or array element)
TransformPostIncDecOperatorOnAddress(block, pos);
TransformRoslynPostIncDecOperatorOnAddress(block, pos);
// TODO: post-increment on call
*/
}
/// <code>
/// stloc s(value)
/// stloc l(ldloc s)
/// stobj(..., ldloc s)
/// where ... is pure and does not use s or l
/// -->
/// stloc l(stobj (..., value))
/// </code>
@ -72,7 +78,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -72,7 +78,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// stloc s(stobj (..., value))
/// </code>
/// e.g. used for inline assignment to static field
bool TransformInlineAssignmentStObj(Block block, int pos)
///
/// -or-
///
/// <code>
/// stloc s(value)
/// call set_Property(..., ldloc s)
/// where the '...' arguments are free of side-effects and not using 's'
/// -->
/// stloc s(Block InlineAssign { call set_Property(..., stloc i(value)); final: ldloc i })
/// </code>
bool TransformInlineAssignmentStObjOrCall(Block block, int pos)
{
var inst = block.Instructions[pos] as StLoc;
// in some cases it can be a compiler-generated local
@ -96,22 +112,75 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -96,22 +112,75 @@ namespace ICSharpCode.Decompiler.IL.Transforms
localStore = null;
nextPos = pos + 1;
}
if (!(block.Instructions[nextPos] is StObj stobj))
return false;
if (!stobj.Value.MatchLdLoc(inst.Variable) || inst.Variable.IsUsedWithin(stobj.Target))
return false;
if (IsImplicitTruncation(inst.Value, stobj.Type)) {
// 'stloc s' is implicitly truncating the value
if (block.Instructions[nextPos] is StObj stobj) {
if (!stobj.Value.MatchLdLoc(inst.Variable))
return false;
if (!SemanticHelper.IsPure(stobj.Target.Flags) || inst.Variable.IsUsedWithin(stobj.Target))
return false;
if (IsImplicitTruncation(inst.Value, stobj.Type)) {
// 'stobj' is implicitly truncating the value
return false;
}
context.Step("Inline assignment stobj", stobj);
block.Instructions.Remove(localStore);
block.Instructions.Remove(stobj);
stobj.Value = inst.Value;
inst.ReplaceWith(new StLoc(local, stobj));
return true;
} else if (block.Instructions[nextPos] is CallInstruction call) {
// call must be a setter call:
if (!(call.OpCode == OpCode.Call || call.OpCode == OpCode.CallVirt))
return false;
if (call.ResultType != StackType.Void || call.Arguments.Count == 0)
return false;
if (!call.Method.Equals((call.Method.AccessorOwner as IProperty)?.Setter))
return false;
if (!call.Arguments.Last().MatchLdLoc(inst.Variable))
return false;
foreach (var arg in call.Arguments.SkipLast(1)) {
if (!SemanticHelper.IsPure(arg.Flags) || inst.Variable.IsUsedWithin(arg))
return false;
}
if (IsImplicitTruncation(inst.Value, call.Method.Parameters.Last().Type)) {
// setter call is implicitly truncating the value
return false;
}
// stloc s(Block InlineAssign { call set_Property(..., stloc i(value)); final: ldloc i })
context.Step("Inline assignment call", call);
block.Instructions.Remove(localStore);
block.Instructions.Remove(call);
var newVar = context.Function.RegisterVariable(VariableKind.StackSlot, call.Method.Parameters.Last().Type);
call.Arguments[call.Arguments.Count - 1] = new StLoc(newVar, inst.Value);
inst.ReplaceWith(new StLoc(local, new Block(BlockType.CallInlineAssign) {
Instructions = { call },
FinalInstruction = new LdLoc(newVar)
}));
return true;
} else {
return false;
}
context.Step("Inline assignment stobj", stobj);
block.Instructions.Remove(localStore);
block.Instructions.Remove(stobj);
stobj.Value = inst.Value;
inst.ReplaceWith(new StLoc(local, stobj));
return true;
}
static ILInstruction UnwrapSmallIntegerConv(ILInstruction inst, out Conv conv)
{
conv = inst as Conv;
if (conv != null && conv.Kind == ConversionKind.Truncate && conv.TargetType.IsSmallIntegerType()) {
// for compound assignments to small integers, the compiler emits a "conv" instruction
return conv.Argument;
} else {
return inst;
}
}
static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType)
{
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, targetType))
return false;
if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
return false; // conv does not match binary operation
return true;
}
/// <code>
/// stloc s(binary(callvirt(getter), value))
/// callvirt (setter, ldloc s)
@ -125,14 +194,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -125,14 +194,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// in some cases it can be a compiler-generated local
if (mainStLoc == null || (mainStLoc.Variable.Kind != VariableKind.StackSlot && mainStLoc.Variable.Kind != VariableKind.Local))
return false;
ILInstruction value = mainStLoc.Value;
if (value is Conv conv && conv.Kind == ConversionKind.Truncate && conv.TargetType.IsSmallIntegerType()) {
// for compound assignments to small integers, the compiler emits a "conv" instruction
value = conv.Argument;
} else {
conv = null;
}
BinaryNumericInstruction binary = value as BinaryNumericInstruction;
BinaryNumericInstruction binary = UnwrapSmallIntegerConv(mainStLoc.Value, out var conv) as BinaryNumericInstruction;
ILVariable localVariable = mainStLoc.Variable;
if (!localVariable.IsSingleDefinition)
return false;
@ -151,10 +213,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -151,10 +213,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1)
return false;
IType targetType = getterCall.Method.ReturnType;
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, targetType))
if (!ValidateCompoundAssign(binary, conv, targetType))
return false;
if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
return false; // conv does not match binary operation
context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
block.Instructions.RemoveAt(i + 1); // remove setter call
mainStLoc.Value = new CompoundAssignmentInstruction(
@ -267,14 +327,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -267,14 +327,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </remarks>
internal static bool HandleStObjCompoundAssign(StObj inst, ILTransformContext context)
{
ILInstruction value = inst.Value;
if (value is Conv conv && conv.Kind == ConversionKind.Truncate && conv.TargetType.IsSmallIntegerType()) {
// for compound assignments to small integers, the compiler emits a "conv" instruction
value = conv.Argument;
} else {
conv = null;
}
if (!(value is BinaryNumericInstruction binary))
if (!(UnwrapSmallIntegerConv(inst.Value, out var conv) is BinaryNumericInstruction binary))
return false;
if (!(binary.Left is LdObj ldobj))
return false;
@ -293,10 +346,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -293,10 +346,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else {
targetType = ldobj.Type;
}
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, targetType))
if (!ValidateCompoundAssign(binary, conv, targetType))
return false;
if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
return false; // conv does not match binary operation
context.Step("compound assignment", inst);
inst.ReplaceWith(new CompoundAssignmentInstruction(
binary, binary.Left, binary.Right,
@ -310,16 +361,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -310,16 +361,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// -->
/// stloc s(stloc l(value))
/// </code>
bool TransformInlineAssignmentLocal(Block block, int i)
bool TransformInlineAssignmentLocal(Block block, int pos)
{
var inst = block.Instructions[i] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
var inst = block.Instructions[pos] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(pos + 1) as StLoc;
if (inst == null || nextInst == null)
return false;
if (inst.Variable.Kind != VariableKind.StackSlot)
return false;
Debug.Assert(!inst.Variable.Type.IsSmallIntegerType());
if (nextInst.Variable.Kind != VariableKind.Local || !nextInst.Value.MatchLdLoc(inst.Variable))
if (!(nextInst.Variable.Kind == VariableKind.Local || nextInst.Variable.Kind == VariableKind.Parameter))
return false;
if (!nextInst.Value.MatchLdLoc(inst.Variable))
return false;
if (IsImplicitTruncation(inst.Value, nextInst.Variable.Type)) {
// 'stloc l' is implicitly truncating the stack value
@ -329,7 +382,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -329,7 +382,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var value = inst.Value;
var var = nextInst.Variable;
var stackVar = inst.Variable;
block.Instructions.RemoveAt(i);
block.Instructions.RemoveAt(pos);
nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value)));
return true;
}
@ -408,48 +461,49 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -408,48 +461,49 @@ namespace ICSharpCode.Decompiler.IL.Transforms
nextInst.ReplaceWith(new StLoc(inst.Variable, assignment));
return true;
}
/// ldaddress ::= ldelema | ldflda | ldsflda;
/// <code>
/// stloc s(ldaddress)
/// stloc l(ldobj(ldloc s))
/// stobj(ldloc s, binary.op(ldloc l, ldc.i4 1))
/// stobj(target, binary.add(stloc l(ldobj(target)), ldc.i4 1))
/// where target is pure and does not use 'l'
/// -->
/// stloc l(compound.op.old(ldobj(ldaddress), ldc.i4 1))
/// stloc l(compound.op.old(ldobj(target), ldc.i4 1))
/// </code>
bool TransformPostIncDecOperatorOnAddress(Block block, int i)
{
var inst = block.Instructions[i] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
var stobj = block.Instructions.ElementAtOrDefault(i + 2) as StObj;
if (inst == null || nextInst == null || stobj == null)
if (!(block.Instructions[i] is StObj stobj))
return false;
if (!inst.Variable.IsSingleDefinition || inst.Variable.LoadCount != 2)
var binary = UnwrapSmallIntegerConv(stobj.Value, out var conv) as BinaryNumericInstruction;
if (binary == null || !binary.Right.MatchLdcI4(1))
return false;
if (!(inst.Value is LdElema || inst.Value is LdFlda || inst.Value is LdsFlda))
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
ILInstruction target;
IType targetType;
if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdObj(out target, out targetType) || !target.MatchLdLoc(inst.Variable))
if (!(binary.Left is StLoc stloc))
return false;
if (!stobj.Target.MatchLdLoc(inst.Variable))
if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot))
return false;
var binary = stobj.Value as BinaryNumericInstruction;
if (binary == null || !binary.Left.MatchLdLoc(nextInst.Variable) || !binary.Right.MatchLdcI4(1)
|| (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
if (!(stloc.Value is LdObj ldobj))
return false;
context.Step($"TransformPostIncDecOperator", inst);
var assignment = new CompoundAssignmentInstruction(binary, new LdObj(inst.Value, targetType), binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue);
stobj.ReplaceWith(new StLoc(nextInst.Variable, assignment));
block.Instructions.RemoveAt(i + 1);
if (!SemanticHelper.IsPure(ldobj.Target.Flags))
return false;
if (!ldobj.Target.Match(stobj.Target).Success)
return false;
if (stloc.Variable.IsUsedWithin(ldobj.Target))
return false;
IType targetType = ldobj.Type;
if (!ValidateCompoundAssign(binary, conv, targetType))
return false;
context.Step("TransformPostIncDecOperatorOnAddress", stobj);
block.Instructions[i] = new StLoc(stloc.Variable, new CompoundAssignmentInstruction(
binary, ldobj, binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue));
return true;
}
/// <code>
/// stloc l(ldobj(ldflda(target)))
/// stobj(ldflda(target), binary.op(ldloc l, ldc.i4 1))
/// stloc l(ldobj(target))
/// stobj(target, binary.op(ldloc l, ldc.i4 1))
/// target is pure and does not use 'l'
/// -->
/// compound.op.old(ldobj(ldflda(target)), ldc.i4 1)
/// stloc l(compound.op.old(ldobj(target), ldc.i4 1))
/// </code>
bool TransformRoslynPostIncDecOperatorOnAddress(Block block, int i)
{
@ -457,21 +511,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -457,21 +511,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var stobj = block.Instructions.ElementAtOrDefault(i + 1) as StObj;
if (inst == null || stobj == null)
return false;
if (!inst.Variable.IsSingleDefinition || inst.Variable.LoadCount != 1)
if (!(inst.Value is LdObj ldobj))
return false;
if (!inst.Value.MatchLdObj(out var loadTarget, out var loadType) || !loadTarget.MatchLdFlda(out var fieldTarget, out var field))
if (!SemanticHelper.IsPure(ldobj.Target.Flags))
return false;
if (!stobj.Target.MatchLdFlda(out var fieldTarget2, out var field2))
if (!ldobj.Target.Match(stobj.Target).Success)
return false;
if (!fieldTarget.Match(fieldTarget2).Success || !field.Equals(field2))
if (inst.Variable.IsUsedWithin(ldobj.Target))
return false;
var binary = stobj.Value as BinaryNumericInstruction;
if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1)
|| (binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub))
var binary = UnwrapSmallIntegerConv(stobj.Value, out var conv) as BinaryNumericInstruction;
if (binary == null || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1))
return false;
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
context.Step("TransformRoslynPostIncDecOperator", inst);
stobj.ReplaceWith(new CompoundAssignmentInstruction(binary, inst.Value, binary.Right, loadType, CompoundAssignmentType.EvaluatesToOldValue));
block.Instructions.RemoveAt(i);
var targetType = ldobj.Type;
if (!ValidateCompoundAssign(binary, conv, targetType))
return false;
context.Step("TransformRoslynPostIncDecOperatorOnAddress", inst);
inst.Value = new CompoundAssignmentInstruction(binary, inst.Value, binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue);
block.Instructions.RemoveAt(i + 1);
return true;
}

Loading…
Cancel
Save