Browse Source

Fix #2527: Support skip locals init

pull/2529/head
Siegfried Pammer 4 years ago
parent
commit
e1ca4db851
  1. 4
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 97
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  3. 11
      ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs
  4. 3
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  5. 8
      ICSharpCode.Decompiler/IL/ILReader.cs
  6. 114
      ICSharpCode.Decompiler/IL/ILVariable.cs
  7. 6
      ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs
  8. 7
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
  9. 18
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2444,9 +2444,9 @@ namespace ICSharpCode.Decompiler.CSharp
var body = statementBuilder.ConvertAsBlock(container); var body = statementBuilder.ConvertAsBlock(container);
var comment = new Comment(" Could not convert BlockContainer to single expression"); var comment = new Comment(" Could not convert BlockContainer to single expression");
body.InsertChildAfter(null, comment, Roles.Comment); body.InsertChildAfter(null, comment, Roles.Comment);
// set ILVariable.HasInitialValue for any variables being used inside the container // set ILVariable.UsesInitialValue for any variables being used inside the container
foreach (var stloc in container.Descendants.OfType<StLoc>()) foreach (var stloc in container.Descendants.OfType<StLoc>())
stloc.Variable.HasInitialValue = true; stloc.Variable.UsesInitialValue = true;
var ame = new AnonymousMethodExpression { Body = body }; var ame = new AnonymousMethodExpression { Body = body };
var systemFuncType = compilation.FindType(typeof(Func<>)); var systemFuncType = compilation.FindType(typeof(Func<>));
var blockReturnType = InferReturnType(body); var blockReturnType = InferReturnType(body);

97
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -70,6 +70,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
enum VariableInitKind
{
None,
NeedsDefaultValue,
NeedsSkipInit
}
[DebuggerDisplay("VariableToDeclare(Name={Name})")] [DebuggerDisplay("VariableToDeclare(Name={Name})")]
class VariableToDeclare class VariableToDeclare
{ {
@ -80,7 +87,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary> /// <summary>
/// Whether the variable needs to be default-initialized. /// Whether the variable needs to be default-initialized.
/// </summary> /// </summary>
public bool DefaultInitialization; public VariableInitKind DefaultInitialization;
/// <summary> /// <summary>
/// Integer value that can be used to compare to VariableToDeclare instances /// Integer value that can be used to compare to VariableToDeclare instances
@ -106,10 +113,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public bool RemovedDueToCollision => ReplacementDueToCollision != null; public bool RemovedDueToCollision => ReplacementDueToCollision != null;
public bool DeclaredInDeconstruction; public bool DeclaredInDeconstruction;
public VariableToDeclare(ILVariable variable, bool defaultInitialization, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder) public VariableToDeclare(ILVariable variable, InsertionPoint insertionPoint, IdentifierExpression firstUse, int sourceOrder)
{ {
this.ILVariable = variable; this.ILVariable = variable;
this.DefaultInitialization = defaultInitialization; if (variable.UsesInitialValue)
{
if (variable.InitialValueIsInitialized)
{
this.DefaultInitialization = VariableInitKind.NeedsDefaultValue;
}
else
{
this.DefaultInitialization = VariableInitKind.NeedsSkipInit;
}
}
else
{
this.DefaultInitialization = VariableInitKind.None;
}
this.InsertionPoint = insertionPoint; this.InsertionPoint = insertionPoint;
this.FirstUse = firstUse; this.FirstUse = firstUse;
this.SourceOrder = sourceOrder; this.SourceOrder = sourceOrder;
@ -304,7 +325,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
else else
{ {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr }; newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
if (variable.HasInitialValue) if (variable.UsesInitialValue)
{ {
// Uninitialized variables are logically initialized at the beginning of the function // Uninitialized variables are logically initialized at the beginning of the function
// Because it's possible that the variable has a loop-carried dependency, // Because it's possible that the variable has a loop-carried dependency,
@ -331,8 +352,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
else else
{ {
v = new VariableToDeclare(variable, variable.HasInitialValue, v = new VariableToDeclare(variable, newPoint,
newPoint, identExpr, sourceOrder: variableDict.Count); identExpr, sourceOrder: variableDict.Count);
variableDict.Add(variable, v); variableDict.Add(variable, v);
} }
} }
@ -623,17 +644,69 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
// Insert a separate declaration statement. // Insert a separate declaration statement.
Expression initializer = null; Expression initializer = null;
AstType type = context.TypeSystemAstBuilder.ConvertType(v.Type); AstType type = context.TypeSystemAstBuilder.ConvertType(v.Type);
if (v.DefaultInitialization) if (v.DefaultInitialization == VariableInitKind.NeedsDefaultValue)
{ {
initializer = new DefaultValueExpression(type.Clone()); initializer = new DefaultValueExpression(type.Clone());
} }
var vds = new VariableDeclarationStatement(type, v.Name, initializer); var vds = new VariableDeclarationStatement(type, v.Name, initializer);
vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(ilVariable)); vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(ilVariable));
Debug.Assert(v.InsertionPoint.nextNode.Role == BlockStatement.StatementRole); Debug.Assert(v.InsertionPoint.nextNode.Role == BlockStatement.StatementRole);
v.InsertionPoint.nextNode.Parent.InsertChildBefore( if (v.DefaultInitialization == VariableInitKind.NeedsSkipInit)
v.InsertionPoint.nextNode, {
vds, AstType unsafeType = context.TypeSystemAstBuilder.ConvertType(
BlockStatement.StatementRole); context.TypeSystem.FindType(KnownTypeCode.Unsafe));
if (context.Settings.OutVariables)
{
var outVarDecl = new OutVarDeclarationExpression(type.Clone(), v.Name);
outVarDecl.Variable.AddAnnotation(new ILVariableResolveResult(ilVariable));
v.InsertionPoint.nextNode.Parent.InsertChildBefore(
v.InsertionPoint.nextNode,
new ExpressionStatement {
Expression = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new TypeReferenceExpression(unsafeType),
MemberName = "SkipInit"
},
Arguments = {
outVarDecl
}
}
},
BlockStatement.StatementRole);
}
else
{
v.InsertionPoint.nextNode.Parent.InsertChildBefore(
v.InsertionPoint.nextNode,
vds,
BlockStatement.StatementRole);
v.InsertionPoint.nextNode.Parent.InsertChildBefore(
v.InsertionPoint.nextNode,
new ExpressionStatement {
Expression = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new TypeReferenceExpression(unsafeType),
MemberName = "SkipInit"
},
Arguments = {
new DirectionExpression(
FieldDirection.Out,
new IdentifierExpression(v.Name)
.WithRR(new ILVariableResolveResult(ilVariable))
)
}
}
},
BlockStatement.StatementRole);
}
}
else
{
v.InsertionPoint.nextNode.Parent.InsertChildBefore(
v.InsertionPoint.nextNode,
vds,
BlockStatement.StatementRole);
}
} }
} }
// perform replacements at end, so that we don't replace a node while it is still referenced by a VariableToDeclare // perform replacements at end, so that we don't replace a node while it is still referenced by a VariableToDeclare
@ -650,7 +723,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return false; return false;
if (!context.Settings.OutVariables) if (!context.Settings.OutVariables)
return false; return false;
if (v.DefaultInitialization) if (v.DefaultInitialization != VariableInitKind.None)
return false; return false;
for (AstNode node = v.FirstUse; node != null; node = node.Parent) for (AstNode node = v.FirstUse; node != null; node = node.Parent)
{ {

11
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs

@ -296,14 +296,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
var stores = storesByVar[vi]; var stores = storesByVar[vi];
if (stores != null) if (stores != null)
{ {
int expectedStoreCount = scope.Variables[vi].StoreCount; int expectedStoreCount = scope.Variables[vi].StoreInstructions.Count;
if (!scope.Variables[vi].HasInitialValue) // Extra store for the uninitialized state.
{ expectedStoreCount += 1;
// Extra store for the uninitialized state.
expectedStoreCount += 1;
// Note that for variables with HasInitialValue=true,
// this extra store is already accounted for in ILVariable.StoreCount.
}
Debug.Assert(stores.Count == expectedStoreCount); Debug.Assert(stores.Count == expectedStoreCount);
stores.CopyTo(allStores, si); stores.CopyTo(allStores, si);
// Add all stores except for the first (representing the uninitialized state) // Add all stores except for the first (representing the uninitialized state)

3
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -995,7 +995,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
name = fieldDef.Name.Substring(1, pos - 1); name = fieldDef.Name.Substring(1, pos - 1);
} }
v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name); v = function.RegisterVariable(VariableKind.Local, ldflda.Field.ReturnType, name);
v.HasInitialValue = true; // the field was default-initialized, so keep those semantics for the variable v.InitialValueIsInitialized = true; // the field was default-initialized, so keep those semantics for the variable
v.UsesInitialValue = true;
v.StateMachineField = ldflda.Field; v.StateMachineField = ldflda.Field;
fieldToVariableMap.Add(fieldDef, v); fieldToVariableMap.Add(fieldDef, v);
} }

8
ICSharpCode.Decompiler/IL/ILReader.cs

@ -118,12 +118,10 @@ namespace ICSharpCode.Decompiler.IL
this.methodReturnStackType = method.ReturnType.GetStackType(); this.methodReturnStackType = method.ReturnType.GetStackType();
InitParameterVariables(); InitParameterVariables();
localVariables = InitLocalVariables(); localVariables = InitLocalVariables();
if (body.LocalVariablesInitialized) foreach (var v in localVariables)
{ {
foreach (var v in localVariables) v.InitialValueIsInitialized = body.LocalVariablesInitialized;
{ v.UsesInitialValue = true;
v.HasInitialValue = true;
}
} }
this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType);
this.instructionBuilder = new List<ILInstruction>(); this.instructionBuilder = new List<ILInstruction>();

114
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -252,13 +252,13 @@ namespace ICSharpCode.Decompiler.IL
/// <item>stloc</item> /// <item>stloc</item>
/// <item>TryCatchHandler (assigning the exception variable)</item> /// <item>TryCatchHandler (assigning the exception variable)</item>
/// <item>PinnedRegion (assigning the pointer variable)</item> /// <item>PinnedRegion (assigning the pointer variable)</item>
/// <item>initial values (<see cref="HasInitialValue"/>)</item> /// <item>initial values (<see cref="UsesInitialValue"/>)</item>
/// </list> /// </list>
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This variable is automatically updated when adding/removing stores instructions from the ILAst. /// This variable is automatically updated when adding/removing stores instructions from the ILAst.
/// </remarks> /// </remarks>
public int StoreCount => (hasInitialValue ? 1 : 0) + StoreInstructions.Count; public int StoreCount => (usesInitialValue ? 1 : 0) + StoreInstructions.Count;
readonly List<IStoreInstruction> storeInstructions = new List<IStoreInstruction>(); readonly List<IStoreInstruction> storeInstructions = new List<IStoreInstruction>();
@ -270,7 +270,7 @@ namespace ICSharpCode.Decompiler.IL
/// <item>stloc</item> /// <item>stloc</item>
/// <item>TryCatchHandler (assigning the exception variable)</item> /// <item>TryCatchHandler (assigning the exception variable)</item>
/// <item>PinnedRegion (assigning the pointer variable)</item> /// <item>PinnedRegion (assigning the pointer variable)</item>
/// <item>initial values (<see cref="HasInitialValue"/>) -- however, there is no instruction for /// <item>initial values (<see cref="UsesInitialValue"/>) -- however, there is no instruction for
/// the initial value, so it is not contained in the store list.</item> /// the initial value, so it is not contained in the store list.</item>
/// </list> /// </list>
/// </summary> /// </summary>
@ -320,27 +320,86 @@ namespace ICSharpCode.Decompiler.IL
list.RemoveAt(indexToMove); list.RemoveAt(indexToMove);
} }
bool hasInitialValue; bool initialValueIsInitialized;
/// <summary> /// <summary>
/// Gets/Sets whether the variable has an initial value. /// Gets/Sets whether the variable's initial value is initialized.
/// This is always <c>true</c> for parameters (incl. <c>this</c>). /// This is always <c>true</c> for parameters (incl. <c>this</c>).
/// ///
/// Normal variables have an initial value if the function uses ".locals init" /// Normal variables have an initial value if the function uses ".locals init".
/// and that initialization is not a dead store. /// </summary>
public bool InitialValueIsInitialized {
get { return initialValueIsInitialized; }
set {
if (Kind == VariableKind.Parameter && !value)
throw new InvalidOperationException("Cannot remove InitialValueIsInitialized from parameters");
initialValueIsInitialized = value;
}
}
bool usesInitialValue;
/// <summary>
/// Gets/Sets whether the initial value of the variable is used.
/// This is always <c>true</c> for parameters (incl. <c>this</c>).
///
/// Normal variables use the initial value, if no explicit initialization is done.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// An initial value is counted as a store (adds 1 to StoreCount) /// The following table shows the relationship between <see cref="InitialValueIsInitialized"/>
/// and <see cref="UsesInitialValue"/>.
/// <list type="table">
/// <listheader>
/// <term><see cref="InitialValueIsInitialized"/></term>
/// <term><see cref="UsesInitialValue"/></term>
/// <term>Meaning</term>
/// </listheader>
/// <item>
/// <term><see langword="true" /></term>
/// <term><see langword="true" /></term>
/// <term>This variable's initial value is zero-initialized (<c>.locals init</c>) and the initial value is used.
/// From C#'s point of view a the value <c>default(T)</c> is assigned at the site of declaration.</term>
/// </item>
/// <item>
/// <term><see langword="true" /></term>
/// <term><see langword="false" /></term>
/// <term>This variable's initial value is zero-initialized (<c>.locals init</c>) and the initial value is not used.
/// From C#'s point of view no implicit initialization occurs, because the code assigns a value
/// explicitly, before the variable is first read.</term>
/// </item>
/// <item>
/// <term><see langword="false" /></term>
/// <term><see langword="true" /></term>
/// <term>This variable's initial value is uninitialized (<c>.locals</c> without <c>init</c>) and the
/// initial value is used.
/// From C#'s point of view a call to <see cref="System.Runtime.CompilerServices.Unsafe.SkipInit{T}(out T)"/>
/// is generated after the declaration.</term>
/// </item>
/// <item>
/// <term><see langword="false" /></term>
/// <term><see langword="false" /></term>
/// <term>This variable's initial value is uninitialized (<c>.locals</c> without <c>init</c>) and the
/// initial value is not used.
/// From C#'s point of view no implicit initialization occurs, because the code assigns a value
/// explicitly, before the variable is first read.</term>
/// </item>
/// </list>
/// </remarks> /// </remarks>
public bool HasInitialValue { public bool UsesInitialValue {
get { return hasInitialValue; } get { return usesInitialValue; }
set { set {
if (Kind == VariableKind.Parameter && !value) if (Kind == VariableKind.Parameter && !value)
throw new InvalidOperationException("Cannot remove HasInitialValue from parameters"); throw new InvalidOperationException("Cannot remove UsesInitialValue from parameters");
hasInitialValue = value; usesInitialValue = value;
} }
} }
[Obsolete("Use 'UsesInitialValue' instead.")]
public bool HasInitialValue {
get => UsesInitialValue;
set => UsesInitialValue = value;
}
/// <summary> /// <summary>
/// Gets whether the variable is in SSA form: /// Gets whether the variable is in SSA form:
/// There is exactly 1 store, and every load sees the value from that store. /// There is exactly 1 store, and every load sees the value from that store.
@ -362,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public bool IsDead { public bool IsDead {
get { get {
return StoreCount == (HasInitialValue ? 1 : 0) return StoreInstructions.Count == 0
&& LoadCount == 0 && LoadCount == 0
&& AddressCount == 0; && AddressCount == 0;
} }
@ -388,7 +447,10 @@ namespace ICSharpCode.Decompiler.IL
this.StackType = type.GetStackType(); this.StackType = type.GetStackType();
this.Index = index; this.Index = index;
if (kind == VariableKind.Parameter) if (kind == VariableKind.Parameter)
this.HasInitialValue = true; {
this.InitialValueIsInitialized = true;
this.UsesInitialValue = true;
}
CheckInvariant(); CheckInvariant();
} }
@ -401,7 +463,10 @@ namespace ICSharpCode.Decompiler.IL
this.StackType = stackType; this.StackType = stackType;
this.Index = index; this.Index = index;
if (kind == VariableKind.Parameter) if (kind == VariableKind.Parameter)
this.HasInitialValue = true; {
this.InitialValueIsInitialized = true;
this.UsesInitialValue = true;
}
CheckInvariant(); CheckInvariant();
} }
@ -472,9 +537,24 @@ namespace ICSharpCode.Decompiler.IL
output.Write("Index={0}, ", Index); output.Write("Index={0}, ", Index);
} }
output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount); output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount);
if (hasInitialValue && Kind != VariableKind.Parameter) if (Kind != VariableKind.Parameter)
{ {
output.Write(" init"); if (initialValueIsInitialized)
{
output.Write(" init");
}
else
{
output.Write(" uninit");
}
if (usesInitialValue)
{
output.Write(" used");
}
else
{
output.Write(" unused");
}
} }
if (CaptureScope != null) if (CaptureScope != null)
{ {

6
ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
ResetHasInitialValueFlag(function, context); ResetUsesInitialValueFlag(function, context);
// Remove dead stores to variables that are never read from. // Remove dead stores to variables that are never read from.
// If the stored value has some side-effect, the value is unwrapped. // If the stored value has some side-effect, the value is unwrapped.
// This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler.
@ -105,7 +105,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
internal static void ResetHasInitialValueFlag(ILFunction function, ILTransformContext context) internal static void ResetUsesInitialValueFlag(ILFunction function, ILTransformContext context)
{ {
var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken);
function.AcceptVisitor(visitor); function.AcceptVisitor(visitor);
@ -113,7 +113,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v))
{ {
v.HasInitialValue = false; v.UsesInitialValue = false;
} }
} }
} }

7
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -270,14 +270,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.Name = inst.Variable.Name; v.Name = inst.Variable.Name;
v.HasGeneratedName = inst.Variable.HasGeneratedName; v.HasGeneratedName = inst.Variable.HasGeneratedName;
v.StateMachineField = inst.Variable.StateMachineField; v.StateMachineField = inst.Variable.StateMachineField;
v.HasInitialValue = false; // we'll set HasInitialValue when we encounter an uninit load v.InitialValueIsInitialized = inst.Variable.InitialValueIsInitialized;
v.UsesInitialValue = false; // we'll set UsesInitialValue when we encounter an uninit load
v.RemoveIfRedundant = inst.Variable.RemoveIfRedundant; v.RemoveIfRedundant = inst.Variable.RemoveIfRedundant;
newVariables.Add(representative, v); newVariables.Add(representative, v);
inst.Variable.Function.Variables.Add(v); inst.Variable.Function.Variables.Add(v);
} }
if (inst.Variable.HasInitialValue && uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad) && uninitLoad == inst) if (inst.Variable.UsesInitialValue && uninitVariableUsage.TryGetValue(inst.Variable, out var uninitLoad) && uninitLoad == inst)
{ {
v.HasInitialValue = true; v.UsesInitialValue = true;
} }
return v; return v;
} }

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

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public string Name => field.Name; public string Name => field.Name;
public bool CanPropagate { get; private set; } public bool CanPropagate { get; private set; }
public bool HasInitialValue { get; set; } public bool UsesInitialValue { get; set; }
public HashSet<ILInstruction> Initializers { get; } = new HashSet<ILInstruction>(); public HashSet<ILInstruction> Initializers { get; } = new HashSet<ILInstruction>();
@ -80,7 +80,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (declaredVariable != null) if (declaredVariable != null)
return declaredVariable; return declaredVariable;
declaredVariable = container.Variable.Function.RegisterVariable(VariableKind.Local, field.Type, field.Name); declaredVariable = container.Variable.Function.RegisterVariable(VariableKind.Local, field.Type, field.Name);
declaredVariable.HasInitialValue = HasInitialValue; declaredVariable.InitialValueIsInitialized = true;
declaredVariable.UsesInitialValue = UsesInitialValue;
declaredVariable.CaptureScope = container.CaptureScope; declaredVariable.CaptureScope = container.CaptureScope;
return declaredVariable; return declaredVariable;
} }
@ -185,7 +186,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (displayClass.VariablesToDeclare.ContainsKey(f)) if (displayClass.VariablesToDeclare.ContainsKey(f))
continue; continue;
var variable = AddVariable(displayClass, null, f); var variable = AddVariable(displayClass, null, f);
variable.HasInitialValue = true; variable.UsesInitialValue = true;
displayClass.VariablesToDeclare[(IField)f.MemberDefinition] = variable; displayClass.VariablesToDeclare[(IField)f.MemberDefinition] = variable;
} }
@ -304,7 +305,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
HandleInitBlock(stloc.Parent as Block, stloc.ChildIndex + 1, result, result.Variable); HandleInitBlock(stloc.Parent as Block, stloc.ChildIndex + 1, result, result.Variable);
break; break;
case TypeKind.Struct: case TypeKind.Struct:
if (v.StoreCount != (v.HasInitialValue ? 1 : 0)) if (v.StoreInstructions.Count != 0)
return null; return null;
Debug.Assert(v.StoreInstructions.Count == 0); Debug.Assert(v.StoreInstructions.Count == 0);
result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope }; result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope };
@ -509,8 +510,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
variable.Propagate(ResolveVariableToPropagate(statement.Value, field.Type)); variable.Propagate(ResolveVariableToPropagate(statement.Value, field.Type));
variable.Initializers.Add(statement); variable.Initializers.Add(statement);
} }
variable.HasInitialValue = variable.UsesInitialValue =
result.Type.IsReferenceType != false || result.Variable.HasInitialValue; result.Type.IsReferenceType != false || result.Variable.UsesInitialValue;
return variable; return variable;
} }
@ -583,7 +584,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.Step($"ResetHasInitialValueFlag", function); context.Step($"ResetHasInitialValueFlag", function);
foreach (var f in function.Descendants.OfType<ILFunction>()) foreach (var f in function.Descendants.OfType<ILFunction>())
{ {
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); RemoveDeadVariableInit.ResetUsesInitialValueFlag(f, context);
f.CapturedVariables.RemoveWhere(v => v.IsDead); f.CapturedVariables.RemoveWhere(v => v.IsDead);
} }
} }
@ -601,7 +602,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
closureType = variable.Type.GetDefinition(); closureType = variable.Type.GetDefinition();
if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(context, closureType)) if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct
&& variable.UsesInitialValue && IsPotentialClosure(context, closureType))
{ {
initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First());
return true; return true;

Loading…
Cancel
Save