Browse Source

Do not re-emit initializer stores of propagated display-class fields

A field that is propagated to the variable it was initialized from is
replaced by that variable, so re-emitting its initializer assigns the
variable to itself. Where the field was initialized from 'this' the
result does not even compile ('this = this'). The store is dropped
instead, which is what VisitStObj already does for initializer stores
that are not part of an object-initializer block; the insertion position
has to be tracked separately from the loop index, because skipping a
store would otherwise push the following ones past the end of the block.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3943/head
Siegfried Pammer 2 months ago committed by Siegfried Pammer
parent
commit
e81e22ff6a
  1. 7
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
  2. 10
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs
  3. 13
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

7
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs

@ -184,5 +184,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly @@ -184,5 +184,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
Interlocked.Exchange(ref thisField, new Program());
Console.WriteLine("{0} {1}", this, thisField);
}
public void Test11()
{
int field1 = 1;
string field2 = "Hello World!";
Console.WriteLine("{0} {1} {2}", this, field1, field2);
}
}
}

10
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs

@ -202,5 +202,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly @@ -202,5 +202,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
Interlocked.Exchange(ref displayClass.thisField, new Program());
Console.WriteLine("{0} {1}", this, displayClass.thisField);
}
public void Test11()
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = 1,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1} {2}", displayClass.thisField, displayClass.field1, displayClass.field2);
}
}
}

13
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -820,12 +820,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -820,12 +820,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
context.Step($"Remove initializer of {inst.Variable.Name}", inst);
ILInstruction firstInlinedStore = null;
// Stores are appended after the initializer, in source order; a store that
// is dropped must not leave a gap, so the position is tracked separately
// from the loop index.
int insertionIndex = inst.ChildIndex;
for (int i = 1; i < initBlock.Instructions.Count; i++)
{
var stobj = (StObj)initBlock.Instructions[i];
var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition];
// A propagated field is replaced by the variable it was initialized from,
// so keeping its initializer would assign that variable to itself - and
// where the variable is 'this', the result is not even valid C#. Stores
// outside an object-initializer block are dropped in VisitStObj under the
// same condition.
if (variable.CanPropagate && variable.Initializers.Contains(stobj))
continue;
var inlinedStore = new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj);
parentBlock.Instructions.Insert(inst.ChildIndex + i, inlinedStore);
parentBlock.Instructions.Insert(++insertionIndex, inlinedStore);
firstInlinedStore ??= inlinedStore;
}
context.EndStep(firstInlinedStore);

Loading…
Cancel
Save