Browse Source

Remove context field from TransformCollectionAndObjectInitializers, inline DoTransform.

pull/2794/head
Siegfried Pammer 3 years ago
parent
commit
1ac1bd6eab
  1. 48
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

48
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -33,29 +33,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
public class TransformCollectionAndObjectInitializers : IStatementTransform public class TransformCollectionAndObjectInitializers : IStatementTransform
{ {
StatementTransformContext context;
void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
{ {
if (!context.Settings.ObjectOrCollectionInitializers) if (!context.Settings.ObjectOrCollectionInitializers)
return; return;
this.context = context; ILInstruction inst = block.Instructions[pos];
try
{
DoTransform(block, pos);
}
finally
{
this.context = null;
}
}
bool DoTransform(Block body, int pos)
{
ILInstruction inst = body.Instructions[pos];
// Match stloc(v, newobj) // Match stloc(v, newobj)
if (!inst.MatchStLoc(out var v, out var initInst) || v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) if (!inst.MatchStLoc(out var v, out var initInst) || v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot)
return false; return;
IType instType; IType instType;
var blockKind = BlockKind.CollectionInitializer; var blockKind = BlockKind.CollectionInitializer;
var insertionPos = initInst.ChildIndex; var insertionPos = initInst.ChildIndex;
@ -71,17 +56,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// prefer to keep local variables (but not stack slots), // prefer to keep local variables (but not stack slots),
// unless we are in a constructor (where inlining object initializers might be critical // unless we are in a constructor (where inlining object initializers might be critical
// for the base ctor call) or a compiler-generated delegate method, which might be used in a query expression. // for the base ctor call) or a compiler-generated delegate method, which might be used in a query expression.
return false; return;
} }
// Do not try to transform delegate construction. // Do not try to transform delegate construction.
// DelegateConstruction transform cannot deal with this. // DelegateConstruction transform cannot deal with this.
if (DelegateConstruction.MatchDelegateConstruction(newObjInst, out _, out _, out _) if (DelegateConstruction.MatchDelegateConstruction(newObjInst, out _, out _, out _)
|| TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst)) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst))
return false; return;
// Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression // Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression
// anon = new { A = 5 } { 3,4,5 } is invalid syntax. // anon = new { A = 5 } { 3,4,5 } is invalid syntax.
if (newObjInst.Method.DeclaringType.ContainsAnonymousType()) if (newObjInst.Method.DeclaringType.ContainsAnonymousType())
return false; return;
instType = newObjInst.Method.DeclaringType; instType = newObjInst.Method.DeclaringType;
break; break;
case DefaultValue defaultVal: case DefaultValue defaultVal:
@ -91,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// prefer to keep local variables (but not stack slots), // prefer to keep local variables (but not stack slots),
// unless we are in a constructor (where inlining object initializers might be // unless we are in a constructor (where inlining object initializers might be
// critical for the base ctor call) // critical for the base ctor call)
return false; return;
} }
instType = defaultVal.Type; instType = defaultVal.Type;
break; break;
@ -112,7 +97,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
blockKind = BlockKind.WithInitializer; blockKind = BlockKind.WithInitializer;
break; break;
} }
return false; return;
} }
int initializerItemsCount = 0; int initializerItemsCount = 0;
possibleIndexVariables = new Dictionary<ILVariable, (int Index, ILInstruction Value)>(); possibleIndexVariables = new Dictionary<ILVariable, (int Index, ILInstruction Value)>();
@ -124,15 +109,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// each must be a callvirt with ldloc v as first argument // each must be a callvirt with ldloc v as first argument
// if the method is a setter we're dealing with an object initializer // if the method is a setter we're dealing with an object initializer
// if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer // if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer
while (pos + initializerItemsCount + 1 < body.Instructions.Count while (pos + initializerItemsCount + 1 < block.Instructions.Count
&& IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind)) && IsPartOfInitializer(block.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind, context))
{ {
initializerItemsCount++; initializerItemsCount++;
} }
// Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable // Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable
// directly after the possible initializer. // directly after the possible initializer.
if (IsMethodCallOnVariable(body.Instructions[pos + initializerItemsCount + 1], v)) if (IsMethodCallOnVariable(block.Instructions[pos + initializerItemsCount + 1], v))
return false; return;
// Calculate the correct number of statements inside the initializer: // Calculate the correct number of statements inside the initializer:
// All index variables that were used in the initializer have Index set to -1. // 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 // We fetch the first unused variable from the list and remove all instructions after its
@ -144,7 +129,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
// The initializer would be empty, there's nothing to do here. // The initializer would be empty, there's nothing to do here.
if (initializerItemsCount <= 0) if (initializerItemsCount <= 0)
return false; return;
context.Step("CollectionOrObjectInitializer", inst); context.Step("CollectionOrObjectInitializer", inst);
// Create a new block and final slot (initializer target variable) // Create a new block and final slot (initializer target variable)
var initializerBlock = new Block(blockKind); var initializerBlock = new Block(blockKind);
@ -154,7 +139,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Move all instructions to the initializer block. // Move all instructions to the initializer block.
for (int i = 1; i <= initializerItemsCount; i++) for (int i = 1; i <= initializerItemsCount; i++)
{ {
switch (body.Instructions[i + pos]) switch (block.Instructions[i + pos])
{ {
case CallInstruction call: case CallInstruction call:
if (!(call is CallVirt || call is Call)) if (!(call is CallVirt || call is Call))
@ -179,10 +164,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
} }
} }
body.Instructions.RemoveRange(pos + 1, initializerItemsCount); block.Instructions.RemoveRange(pos + 1, initializerItemsCount);
siblings[insertionPos] = initializerBlock; siblings[insertionPos] = initializerBlock;
ILInlining.InlineIfPossible(body, pos, context); ILInlining.InlineIfPossible(block, pos, context);
return true;
} }
internal static bool IsRecordCloneMethodCall(CallInstruction ci) internal static bool IsRecordCloneMethodCall(CallInstruction ci)
@ -213,7 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool isCollection; bool isCollection;
Stack<HashSet<AccessPathElement>> pathStack; Stack<HashSet<AccessPathElement>> pathStack;
bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind) bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind, StatementTransformContext context)
{ {
// Include any stores to local variables that are single-assigned and do not reference the initializer-variable // Include any stores to local variables that are single-assigned and do not reference the initializer-variable
// in the list of possible index variables. // in the list of possible index variables.

Loading…
Cancel
Save