Skip to content

Commit fae6f57

Browse files
Fix #3671: TransformCollectionAndObjectInitializers mistakenly included trailing variable initialization.
1 parent c813c5f commit fae6f57

2 files changed

Lines changed: 40 additions & 13 deletions

File tree

ICSharpCode.Decompiler/IL/Instructions/Block.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,25 @@ internal override void CheckInvariant(ILPhase phase)
185185
}
186186
for (int i = 1; i < Instructions.Count; i++)
187187
{
188-
Debug.Assert(Instructions[i] is StLoc || AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != AccessPathKind.Invalid);
188+
if (Instructions[i] is StLoc { Variable: var v })
189+
{
190+
foreach (var inst in v.LoadInstructions)
191+
{
192+
Debug.Assert(inst.IsDescendantOf(this));
193+
}
194+
foreach (var inst in v.AddressInstructions)
195+
{
196+
Debug.Assert(inst.IsDescendantOf(this));
197+
}
198+
foreach (ILInstruction inst in v.StoreInstructions)
199+
{
200+
Debug.Assert(inst.IsDescendantOf(this));
201+
}
202+
}
203+
else
204+
{
205+
Debug.Assert(AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != AccessPathKind.Invalid);
206+
}
189207
}
190208
break;
191209
case BlockKind.DeconstructionConversions:

ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int
242242
possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value));
243243
return true;
244244
}
245-
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, context.CSharpResolver, possibleIndexVariables);
245+
(var kind, var newPath, var values, var targetVariable, var usedIndices) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, context.CSharpResolver);
246246
if (kind == AccessPathKind.Invalid || target != targetVariable)
247247
return false;
248248
// Treat last element separately:
@@ -274,6 +274,7 @@ bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int
274274
isCollection = true;
275275
if (pathStack.Peek().Count != 0)
276276
return false;
277+
MarkUsedIndices();
277278
return true;
278279
case AccessPathKind.Setter:
279280
if (isCollection || !pathStack.Peek().Add(lastElement))
@@ -283,10 +284,22 @@ bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int
283284
if (blockKind != BlockKind.ObjectInitializer && blockKind != BlockKind.WithInitializer)
284285
blockKind = BlockKind.ObjectInitializer;
285286
initializerContainsInitOnlyItems |= lastElement.Member is IProperty { Setter.IsInitOnly: true };
287+
MarkUsedIndices();
286288
return true;
287289
default:
288290
return false;
289291
}
292+
293+
void MarkUsedIndices()
294+
{
295+
foreach (var index in usedIndices)
296+
{
297+
if (possibleIndexVariables.TryGetValue(index, out var item))
298+
{
299+
possibleIndexVariables[index] = (-1, item.Value);
300+
}
301+
}
302+
}
290303
}
291304

292305
bool IsValidObjectInitializerTarget(List<AccessPathElement> path)
@@ -330,17 +343,17 @@ public AccessPathElement(OpCode opCode, IMember member, ILInstruction[]? indices
330343

331344
public override string ToString() => $"[{Member}, {Indices}]";
332345

333-
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction>? Values, ILVariable? Target) GetAccessPath(
346+
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction>? Values, ILVariable? Target, List<ILVariable> UsedIndexVariables) GetAccessPath(
334347
ILInstruction instruction, IType rootType, DecompilerSettings? settings = null,
335-
CSharpResolver? resolver = null,
336-
Dictionary<ILVariable, (int Index, ILInstruction Value)>? possibleIndexVariables = null)
348+
CSharpResolver? resolver = null)
337349
{
338350
List<AccessPathElement> path = new List<AccessPathElement>();
339351
ILVariable? target = null;
340352
AccessPathKind kind = AccessPathKind.Invalid;
341353
List<ILInstruction>? values = null;
342354
IMethod method;
343355
ILInstruction? inst = instruction;
356+
List<ILVariable> usedIndexVariables = new();
344357
while (inst != null)
345358
{
346359
switch (inst)
@@ -368,14 +381,10 @@ public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruc
368381
var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray();
369382
if (indices.Length > 0 && settings?.DictionaryInitializers == false)
370383
goto default;
371-
if (possibleIndexVariables != null)
384+
// Mark all index variables as used
385+
foreach (var index in indices.OfType<IInstructionWithVariableOperand>())
372386
{
373-
// Mark all index variables as used
374-
foreach (var index in indices.OfType<IInstructionWithVariableOperand>())
375-
{
376-
if (possibleIndexVariables.TryGetValue(index.Variable, out var info))
377-
possibleIndexVariables[index.Variable] = (-1, info.Value);
378-
}
387+
usedIndexVariables.Add(index.Variable);
379388
}
380389
path.Insert(0, new AccessPathElement(call.OpCode, method.AccessorOwner, indices));
381390
}
@@ -460,7 +469,7 @@ public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruc
460469
}
461470
if (kind != AccessPathKind.Invalid && values != null && values.SelectMany(v => v.Descendants).OfType<IInstructionWithVariableOperand>().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca)))
462471
kind = AccessPathKind.Invalid;
463-
return (kind, path, values, target);
472+
return (kind, path, values, target, usedIndexVariables);
464473
}
465474

466475
private static bool CanBeUsedInInitializer(IProperty property, ITypeResolveContext? resolveContext, AccessPathKind kind)

0 commit comments

Comments
 (0)