From 461e59bd3f983563f04321baba5ca1957bc59319 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 18 Oct 2017 10:40:20 +0200 Subject: [PATCH] Clean up and documentation --- ...ransformCollectionAndObjectInitializers.cs | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 7813a84fd..12f9db004 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -46,7 +46,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction inst = body.Instructions[pos]; // Match stloc(v, newobj) if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)) { - Block initializerBlock = null; IType instType; switch (initInst) { case NewObj newObjInst: @@ -75,22 +74,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms } instType = defaultVal.Type; break; - case Block existingInitializer: - if (existingInitializer.Type == BlockType.CollectionInitializer || existingInitializer.Type == BlockType.ObjectInitializer) { - initializerBlock = existingInitializer; - var value = ((StLoc)initializerBlock.Instructions[0]).Value; - if (value is NewObj no) - instType = no.Method.DeclaringType; - else - instType = ((DefaultValue)value).Type; - break; - } - return false; default: return false; } int initializerItemsCount = 0; - var blockType = initializerBlock?.Type ?? BlockType.CollectionInitializer; + var blockType = BlockType.CollectionInitializer; possibleIndexVariables = new Dictionary(); currentPath = new List(); isCollection = false; @@ -104,24 +92,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms && IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockType)) { initializerItemsCount++; } + // Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable + // directly after the possible initializer. if (IsMethodCallOnVariable(body.Instructions[pos + initializerItemsCount + 1], v)) return false; + // Calculate the correct number of statements inside the initializer: + // All index variables that were used in the initializer have Index set to -1. + // We fetch the first unused variable from the list and remove all instructions after its + // first usage (i.e. the init store) from the initializer. var index = possibleIndexVariables.Where(info => info.Value.Index > -1).Min(info => (int?)info.Value.Index); if (index != null) { initializerItemsCount = index.Value - pos - 1; } + // The initializer would be empty, there's nothing to do here. if (initializerItemsCount <= 0) return false; context.Step("CollectionOrObjectInitializer", inst); - ILVariable finalSlot; - if (initializerBlock == null) { - initializerBlock = new Block(blockType); - finalSlot = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); - initializerBlock.FinalInstruction = new LdLoc(finalSlot); - initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst.Clone())); - } else { - finalSlot = ((LdLoc)initializerBlock.FinalInstruction).Variable; - } + // Create a new block and final slot (initializer target variable) + var initializerBlock = new Block(blockType); + ILVariable finalSlot = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); + initializerBlock.FinalInstruction = new LdLoc(finalSlot); + initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst.Clone())); + // Move all instructions to the initializer block. for (int i = 1; i <= initializerItemsCount; i++) { switch (body.Instructions[i + pos]) { case CallInstruction call: @@ -171,6 +163,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool IsPartOfInitializer(InstructionCollection instructions, int pos, ILVariable target, IType rootType, ref BlockType blockType) { + // Include any stores to local variables that are single-assigned and do not reference the initializer-variable + // in the list of possible index variables. + // Index variables are used to implement dictionary initializers. if (instructions[pos] is StLoc stloc && stloc.Variable.Kind == VariableKind.Local && stloc.Variable.IsSingleDefinition) { if (stloc.Value.Descendants.OfType().Any(ld => ld.Variable == target && (ld is LdLoc || ld is LdLoca))) return false; @@ -241,7 +236,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms public override string ToString() => $"[{Member}, {Indices}]"; - public static (AccessPathKind Kind, List Path, List Values, ILVariable Target) GetAccessPath(ILInstruction instruction, IType rootType, Dictionary possibleIndexVariables = null) + public static (AccessPathKind Kind, List Path, List Values, ILVariable Target) GetAccessPath( + ILInstruction instruction, IType rootType, Dictionary possibleIndexVariables = null) { List path = new List(); ILVariable target = null; @@ -261,6 +257,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var isGetter = property?.Getter == method; var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray(); if (possibleIndexVariables != null) { + // Mark all index variables as used foreach (var index in indices.OfType()) { if (possibleIndexVariables.TryGetValue(index.Variable, out var info)) possibleIndexVariables[index.Variable] = (-1, info.Value);