|
|
@ -42,15 +42,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
class TransformDisplayClassUsage : ILVisitor, IILTransform |
|
|
|
class TransformDisplayClassUsage : ILVisitor, IILTransform |
|
|
|
{ |
|
|
|
{ |
|
|
|
[Flags] |
|
|
|
|
|
|
|
enum Kind |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
DelegateConstruction = 1, |
|
|
|
|
|
|
|
LocalFunction = 2, |
|
|
|
|
|
|
|
ExpressionTree = 4, |
|
|
|
|
|
|
|
MonoStateMachine = 8 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VariableToDeclare |
|
|
|
class VariableToDeclare |
|
|
|
{ |
|
|
|
{ |
|
|
|
private readonly DisplayClass container; |
|
|
|
private readonly DisplayClass container; |
|
|
@ -59,18 +50,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
|
|
|
|
|
|
|
|
public string Name => field.Name; |
|
|
|
public string Name => field.Name; |
|
|
|
|
|
|
|
|
|
|
|
public List<ILInstruction> AssignedValues { get; } = new List<ILInstruction>(); |
|
|
|
public bool CanPropagate { get; private set; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ILInstruction Initializer { get; set; } |
|
|
|
|
|
|
|
|
|
|
|
public VariableToDeclare(DisplayClass container, IField field) |
|
|
|
public VariableToDeclare(DisplayClass container, IField field, ILVariable declaredVariable = null) |
|
|
|
{ |
|
|
|
{ |
|
|
|
this.container = container; |
|
|
|
this.container = container; |
|
|
|
this.field = field; |
|
|
|
this.field = field; |
|
|
|
|
|
|
|
this.declaredVariable = declaredVariable; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Assert(declaredVariable == null || declaredVariable.StateMachineField == field); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void UseExisting(ILVariable variable) |
|
|
|
public void Propagate(ILVariable variable) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Debug.Assert(this.declaredVariable == null); |
|
|
|
Debug.Assert(declaredVariable == null || (variable == null && declaredVariable.StateMachineField == null)); |
|
|
|
this.declaredVariable = variable; |
|
|
|
this.declaredVariable = variable; |
|
|
|
|
|
|
|
this.CanPropagate = variable != null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public ILVariable GetOrDeclare() |
|
|
|
public ILVariable GetOrDeclare() |
|
|
@ -84,13 +81,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[DebuggerDisplay("[DisplayClass {Variable} : {Type}]")]
|
|
|
|
class DisplayClass |
|
|
|
class DisplayClass |
|
|
|
{ |
|
|
|
{ |
|
|
|
public readonly ILVariable Variable; |
|
|
|
public readonly ILVariable Variable; |
|
|
|
public readonly ITypeDefinition Type; |
|
|
|
public readonly ITypeDefinition Type; |
|
|
|
public readonly Dictionary<IField, VariableToDeclare> VariablesToDeclare; |
|
|
|
public readonly Dictionary<IField, VariableToDeclare> VariablesToDeclare; |
|
|
|
public BlockContainer CaptureScope; |
|
|
|
public BlockContainer CaptureScope; |
|
|
|
public Kind Kind; |
|
|
|
public ILInstruction Initializer; |
|
|
|
|
|
|
|
|
|
|
|
public DisplayClass(ILVariable variable, ITypeDefinition type) |
|
|
|
public DisplayClass(ILVariable variable, ITypeDefinition type) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -135,10 +133,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
AnalyzeFunction(function); |
|
|
|
AnalyzeFunction(function); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static bool AnalyzeVariable(ILVariable v, ILTransformContext context) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var tdcu = new TransformDisplayClassUsage(); |
|
|
|
|
|
|
|
tdcu.context = context; |
|
|
|
|
|
|
|
tdcu.decompilationContext = new SimpleTypeResolveContext(context.Function.Method); |
|
|
|
|
|
|
|
return tdcu.AnalyzeVariable(v) != null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void AnalyzeFunction(ILFunction function) |
|
|
|
private void AnalyzeFunction(ILFunction function) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Traverse nested functions in post-order:
|
|
|
|
|
|
|
|
// Inner functions are transformed before outer functions
|
|
|
|
|
|
|
|
foreach (var f in function.Descendants.OfType<ILFunction>()) { |
|
|
|
foreach (var f in function.Descendants.OfType<ILFunction>()) { |
|
|
|
foreach (var v in f.Variables.ToArray()) { |
|
|
|
foreach (var v in f.Variables.ToArray()) { |
|
|
|
var result = AnalyzeVariable(v); |
|
|
|
var result = AnalyzeVariable(v); |
|
|
@ -147,11 +151,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
displayClasses.Add(v, result); |
|
|
|
displayClasses.Add(v, result); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var displayClass in displayClasses.Values) { |
|
|
|
|
|
|
|
foreach (var v in displayClass.VariablesToDeclare.Values) { |
|
|
|
|
|
|
|
if (v.CanPropagate) { |
|
|
|
|
|
|
|
var variableToPropagate = v.GetOrDeclare(); |
|
|
|
|
|
|
|
if (variableToPropagate.Kind != VariableKind.Parameter && !displayClasses.ContainsKey(variableToPropagate)) |
|
|
|
|
|
|
|
v.Propagate(null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private DisplayClass AnalyzeVariable(ILVariable v) |
|
|
|
private DisplayClass AnalyzeVariable(ILVariable v) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ITypeDefinition definition; |
|
|
|
|
|
|
|
switch (v.Kind) { |
|
|
|
switch (v.Kind) { |
|
|
|
case VariableKind.Parameter: |
|
|
|
case VariableKind.Parameter: |
|
|
|
if (context.Settings.YieldReturn && v.Function.StateMachineCompiledWithMono && v.IsThis()) |
|
|
|
if (context.Settings.YieldReturn && v.Function.StateMachineCompiledWithMono && v.IsThis()) |
|
|
@ -160,9 +173,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
case VariableKind.StackSlot: |
|
|
|
case VariableKind.StackSlot: |
|
|
|
case VariableKind.Local: |
|
|
|
case VariableKind.Local: |
|
|
|
case VariableKind.DisplayClassLocal: |
|
|
|
case VariableKind.DisplayClassLocal: |
|
|
|
if (!AnalyzeInitializer(out definition)) |
|
|
|
return HandleDisplayClass(v); |
|
|
|
return null; |
|
|
|
|
|
|
|
return Run(definition); |
|
|
|
|
|
|
|
case VariableKind.PinnedLocal: |
|
|
|
case VariableKind.PinnedLocal: |
|
|
|
case VariableKind.UsingLocal: |
|
|
|
case VariableKind.UsingLocal: |
|
|
|
case VariableKind.ForeachLocal: |
|
|
|
case VariableKind.ForeachLocal: |
|
|
@ -174,99 +185,125 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
default: |
|
|
|
default: |
|
|
|
throw new ArgumentOutOfRangeException(); |
|
|
|
throw new ArgumentOutOfRangeException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DisplayClass Run(ITypeDefinition type) |
|
|
|
DisplayClass HandleDisplayClass(ILVariable v) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var result = new DisplayClass(v, type) { |
|
|
|
ITypeDefinition definition; |
|
|
|
CaptureScope = v.CaptureScope |
|
|
|
if (v.Kind != VariableKind.StackSlot) { |
|
|
|
}; |
|
|
|
definition = v.Type.GetDefinition(); |
|
|
|
foreach (var ldloc in v.LoadInstructions) { |
|
|
|
} else if (v.StoreInstructions.Count > 0 && v.StoreInstructions[0] is StLoc stloc && stloc.Value is NewObj newObj) { |
|
|
|
if (!ValidateUse(result, ldloc)) |
|
|
|
definition = newObj.Method.DeclaringTypeDefinition; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
definition = null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (definition?.DeclaringTypeDefinition == null || definition.ParentModule.PEFile != context.PEFile) |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
DisplayClass result; |
|
|
|
|
|
|
|
switch (definition.Kind) { |
|
|
|
|
|
|
|
case TypeKind.Class: |
|
|
|
|
|
|
|
if (!v.IsSingleDefinition) |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
if (!(v.StoreInstructions[0] is StLoc stloc)) |
|
|
|
foreach (var ldloca in v.AddressInstructions) { |
|
|
|
|
|
|
|
if (!ValidateUse(result, ldloca)) |
|
|
|
|
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
// TODO thoroughly validate ctor
|
|
|
|
if (result.VariablesToDeclare.Count == 0) |
|
|
|
if (!(stloc.Value is NewObj newObj && newObj.Method.Parameters.Count == 0)) |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
if (IsMonoNestedCaptureScope(type)) { |
|
|
|
result = new DisplayClass(v, definition) { |
|
|
|
result.CaptureScope = null; |
|
|
|
CaptureScope = v.CaptureScope, |
|
|
|
} |
|
|
|
Initializer = stloc |
|
|
|
return result; |
|
|
|
}; |
|
|
|
} |
|
|
|
if (stloc.Parent is Block initBlock) { |
|
|
|
|
|
|
|
for (int i = stloc.ChildIndex + 1; i < initBlock.Instructions.Count; i++) { |
|
|
|
bool ValidateUse(DisplayClass result, ILInstruction use) |
|
|
|
var variable = AddVariable(result, initBlock.Instructions[i], out var field); |
|
|
|
{ |
|
|
|
if (variable == null) |
|
|
|
var current = use.Parent; |
|
|
|
break; |
|
|
|
ILInstruction value; |
|
|
|
result.VariablesToDeclare[(IField)field.MemberDefinition] = variable; |
|
|
|
if (current.MatchLdFlda(out _, out IField field)) { |
|
|
|
|
|
|
|
var keyField = (IField)field.MemberDefinition; |
|
|
|
|
|
|
|
result.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable); |
|
|
|
|
|
|
|
if (current.Parent.MatchStObj(out _, out value, out var type)) { |
|
|
|
|
|
|
|
if (variable == null) { |
|
|
|
|
|
|
|
variable = new VariableToDeclare(result, field); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
variable.AssignedValues.Add(value); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
var containingFunction = current.Ancestors.OfType<ILFunction>().FirstOrDefault(); |
|
|
|
case TypeKind.Struct: |
|
|
|
if (containingFunction != null && containingFunction != v.Function) { |
|
|
|
if (!v.HasInitialValue) |
|
|
|
switch (containingFunction.Kind) { |
|
|
|
return null; |
|
|
|
case ILFunctionKind.Delegate: |
|
|
|
if (v.StoreCount != 1) |
|
|
|
result.Kind |= Kind.DelegateConstruction; |
|
|
|
return null; |
|
|
|
break; |
|
|
|
result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope }; |
|
|
|
case ILFunctionKind.ExpressionTree: |
|
|
|
if (v.Function.Body is BlockContainer b) { |
|
|
|
result.Kind |= Kind.ExpressionTree; |
|
|
|
var block = b.EntryPoint; |
|
|
|
break; |
|
|
|
for (int i = 0; i < block.Instructions.Count; i++) { |
|
|
|
case ILFunctionKind.LocalFunction: |
|
|
|
var variable = AddVariable(result, block.Instructions[i], out var field); |
|
|
|
result.Kind |= Kind.LocalFunction; |
|
|
|
if (variable == null) |
|
|
|
break; |
|
|
|
break; |
|
|
|
|
|
|
|
result.VariablesToDeclare[(IField)field.MemberDefinition] = variable; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
result.VariablesToDeclare[keyField] = variable; |
|
|
|
break; |
|
|
|
return true; |
|
|
|
default: |
|
|
|
} |
|
|
|
return null; |
|
|
|
if (current.MatchStObj(out _, out value, out _) && value == use) |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool AnalyzeInitializer(out ITypeDefinition t) |
|
|
|
foreach (var ldloc in v.LoadInstructions) { |
|
|
|
{ |
|
|
|
if (!ValidateUse(result, ldloc)) |
|
|
|
if (v.Kind != VariableKind.StackSlot) { |
|
|
|
return null; |
|
|
|
t = v.Type.GetDefinition(); |
|
|
|
} |
|
|
|
} else if (v.StoreInstructions.Count > 0 && v.StoreInstructions[0] is StLoc stloc && stloc.Value is NewObj newObj) { |
|
|
|
foreach (var ldloca in v.AddressInstructions) { |
|
|
|
t = newObj.Method.DeclaringTypeDefinition; |
|
|
|
if (!ValidateUse(result, ldloca)) |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
t = null; |
|
|
|
} |
|
|
|
} |
|
|
|
if (IsMonoNestedCaptureScope(definition)) { |
|
|
|
if (t?.DeclaringTypeDefinition == null || t.ParentModule.PEFile != context.PEFile) |
|
|
|
result.CaptureScope = null; |
|
|
|
return false; |
|
|
|
} |
|
|
|
switch (definition.Kind) { |
|
|
|
return result; |
|
|
|
case TypeKind.Class: |
|
|
|
} |
|
|
|
if (!v.IsSingleDefinition) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(v.StoreInstructions[0] is StLoc stloc)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(stloc.Value is NewObj newObj && newObj.Method.Parameters.Count == 0)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case TypeKind.Struct: |
|
|
|
|
|
|
|
if (!v.HasInitialValue) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (v.StoreCount != 1) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
VariableToDeclare AddVariable(DisplayClass result, ILInstruction init, out IField field) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!init.MatchStFld(out var target, out field, out var value)) |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
if (!target.MatchLdLocRef(result.Variable)) |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
if (result.VariablesToDeclare.ContainsKey((IField)field.MemberDefinition)) |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
VariableToDeclare variable = new VariableToDeclare(result, field); |
|
|
|
|
|
|
|
if (value.MatchLdLoc(out var variableToPropagate) && variableToPropagate.StateMachineField == null && (variableToPropagate.Kind == VariableKind.StackSlot || variableToPropagate.Type.Equals(field.Type))) { |
|
|
|
|
|
|
|
variable.Propagate(variableToPropagate); |
|
|
|
|
|
|
|
variable.Initializer = init; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return variable; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool ValidateUse(DisplayClass container, ILInstruction use) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
IField field; |
|
|
|
|
|
|
|
switch (use.Parent) { |
|
|
|
|
|
|
|
case LdFlda ldflda when ldflda.MatchLdFlda(out _, out field): |
|
|
|
|
|
|
|
var keyField = (IField)field.MemberDefinition; |
|
|
|
|
|
|
|
if (!container.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable) || variable == null) { |
|
|
|
|
|
|
|
variable = new VariableToDeclare(container, field); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
container.VariablesToDeclare[keyField] = variable; |
|
|
|
|
|
|
|
return variable != null; |
|
|
|
|
|
|
|
case StObj stobj when stobj.MatchStObj(out var target, out ILInstruction value, out _) && value == use: |
|
|
|
|
|
|
|
if (target.MatchLdFlda(out var load, out field) && load.MatchLdLocRef(out var otherVariable) && displayClasses.TryGetValue(otherVariable, out var displayClass)) { |
|
|
|
|
|
|
|
if (displayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out var declaredVar)) |
|
|
|
|
|
|
|
return declaredVar.CanPropagate; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void Transform(ILFunction function) |
|
|
|
private void Transform(ILFunction function) |
|
|
|
{ |
|
|
|
{ |
|
|
|
VisitILFunction(function); |
|
|
|
VisitILFunction(function); |
|
|
|
|
|
|
|
foreach (var displayClass in displayClasses.Values) { |
|
|
|
|
|
|
|
foreach (var v in displayClass.VariablesToDeclare.Values) { |
|
|
|
|
|
|
|
if (v.CanPropagate && v.Initializer != null) { |
|
|
|
|
|
|
|
instructionsToRemove.Add(v.Initializer); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
if (instructionsToRemove.Count > 0) { |
|
|
|
if (instructionsToRemove.Count > 0) { |
|
|
|
context.Step($"Remove instructions", function); |
|
|
|
context.Step($"Remove instructions", function); |
|
|
|
foreach (var store in instructionsToRemove) { |
|
|
|
foreach (var store in instructionsToRemove) { |
|
|
@ -275,8 +312,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
context.Step($"ResetHasInitialValueFlag", function); |
|
|
|
context.Step($"ResetHasInitialValueFlag", function); |
|
|
|
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) |
|
|
|
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) { |
|
|
|
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); |
|
|
|
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); |
|
|
|
|
|
|
|
f.CapturedVariables.RemoveWhere(v => v.IsDead); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) |
|
|
|
internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) |
|
|
@ -334,8 +373,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
foreach (var stateMachineVariable in function.Variables) { |
|
|
|
foreach (var stateMachineVariable in function.Variables) { |
|
|
|
if (stateMachineVariable.StateMachineField == null || displayClass.VariablesToDeclare.ContainsKey(stateMachineVariable.StateMachineField)) |
|
|
|
if (stateMachineVariable.StateMachineField == null || displayClass.VariablesToDeclare.ContainsKey(stateMachineVariable.StateMachineField)) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, stateMachineVariable.StateMachineField); |
|
|
|
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, stateMachineVariable.StateMachineField, stateMachineVariable); |
|
|
|
variableToDeclare.UseExisting(stateMachineVariable); |
|
|
|
|
|
|
|
displayClass.VariablesToDeclare.Add(stateMachineVariable.StateMachineField, variableToDeclare); |
|
|
|
displayClass.VariablesToDeclare.Add(stateMachineVariable.StateMachineField, variableToDeclare); |
|
|
|
} |
|
|
|
} |
|
|
|
if (!function.Method.IsStatic && FindThisField(out var thisField)) { |
|
|
|
if (!function.Method.IsStatic && FindThisField(out var thisField)) { |
|
|
@ -345,8 +383,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" }; |
|
|
|
thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" }; |
|
|
|
function.Variables.Add(thisVar); |
|
|
|
function.Variables.Add(thisVar); |
|
|
|
} |
|
|
|
} |
|
|
|
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, thisField); |
|
|
|
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, thisField, thisVar); |
|
|
|
variableToDeclare.UseExisting(thisVar); |
|
|
|
|
|
|
|
displayClass.VariablesToDeclare.Add(thisField, variableToDeclare); |
|
|
|
displayClass.VariablesToDeclare.Add(thisField, variableToDeclare); |
|
|
|
} |
|
|
|
} |
|
|
|
return displayClass; |
|
|
|
return displayClass; |
|
|
@ -415,11 +452,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
protected internal override void VisitILFunction(ILFunction function) |
|
|
|
protected internal override void VisitILFunction(ILFunction function) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var oldFunction = this.currentFunction; |
|
|
|
var oldFunction = this.currentFunction; |
|
|
|
|
|
|
|
context.StepStartGroup("Visit " + function.Name); |
|
|
|
try { |
|
|
|
try { |
|
|
|
this.currentFunction = function; |
|
|
|
this.currentFunction = function; |
|
|
|
base.VisitILFunction(function); |
|
|
|
base.VisitILFunction(function); |
|
|
|
} finally { |
|
|
|
} finally { |
|
|
|
this.currentFunction = oldFunction; |
|
|
|
this.currentFunction = oldFunction; |
|
|
|
|
|
|
|
context.StepEndGroup(keepIfEmpty: true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -444,34 +483,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass)) { |
|
|
|
|
|
|
|
context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst); |
|
|
|
|
|
|
|
displayClasses.Add(inst.Variable, displayClass); |
|
|
|
|
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitStObj(StObj inst) |
|
|
|
protected internal override void VisitStObj(StObj inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
base.VisitStObj(inst); |
|
|
|
if (inst.Parent is Block) { |
|
|
|
var instructions = inst.Parent.Children; |
|
|
|
if (IsParameterAssignment(inst, out var declarationSlot, out var parameter)) { |
|
|
|
int childIndex = inst.ChildIndex; |
|
|
|
context.Step($"Detected parameter assignment {parameter.Name}", inst); |
|
|
|
EarlyExpressionTransforms.StObjToStLoc(inst, context); |
|
|
|
declarationSlot.UseExisting(parameter); |
|
|
|
if (inst != instructions[childIndex]) { |
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
foreach (var displayClass in displayClasses.Values) { |
|
|
|
return; |
|
|
|
foreach (var v in displayClass.VariablesToDeclare.Values) { |
|
|
|
|
|
|
|
if (v.CanPropagate && v.Initializer == inst) { |
|
|
|
|
|
|
|
v.Initializer = instructions[childIndex]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (IsDisplayClassAssignment(inst, out declarationSlot, out var variable)) { |
|
|
|
if (instructions[childIndex].MatchStLoc(out var innerDisplayClassVar, out var value) && value.MatchLdLoc(out var outerDisplayClassVar)) { |
|
|
|
context.Step($"Detected display-class assignment {variable.Name}", inst); |
|
|
|
if (!displayClasses.ContainsKey(innerDisplayClassVar) && displayClasses.TryGetValue(outerDisplayClassVar, out var displayClass)) { |
|
|
|
declarationSlot.UseExisting(variable); |
|
|
|
displayClasses.Add(innerDisplayClassVar, displayClass); |
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
instructionsToRemove.Add(instructions[childIndex]); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
inst.Target.AcceptVisitor(this); |
|
|
|
|
|
|
|
EarlyExpressionTransforms.StObjToStLoc(inst, context); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitLdObj(LdObj inst) |
|
|
|
protected internal override void VisitLdObj(LdObj inst) |
|
|
@ -488,43 +523,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private bool IsDisplayClassAssignment(StObj inst, out VariableToDeclare declarationSlot, out ILVariable variable) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
variable = null; |
|
|
|
|
|
|
|
declarationSlot = null; |
|
|
|
|
|
|
|
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out var displayClass, out var field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v))) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!displayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out declarationSlot)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (displayClassVar.Function != currentFunction) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
variable = v; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private bool IsParameterAssignment(StObj inst, out VariableToDeclare declarationSlot, out ILVariable parameter) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
parameter = null; |
|
|
|
|
|
|
|
declarationSlot = null; |
|
|
|
|
|
|
|
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out var displayClass, out var field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type))) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!displayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out declarationSlot)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (displayClassVar.Function != currentFunction) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (declarationSlot.AssignedValues.Count > 1) { |
|
|
|
|
|
|
|
var first = declarationSlot.AssignedValues[0].Ancestors.OfType<ILFunction>().First(); |
|
|
|
|
|
|
|
if (declarationSlot.AssignedValues.All(i => i.Ancestors.OfType<ILFunction>().First() == first)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
parameter = v; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field) |
|
|
|
private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field) |
|
|
|
{ |
|
|
|
{ |
|
|
|
displayClass = null; |
|
|
|
displayClass = null; |
|
|
@ -544,11 +542,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field)) |
|
|
|
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field)) |
|
|
|
return; |
|
|
|
return; |
|
|
|
var keyField = (IField)field.MemberDefinition; |
|
|
|
var keyField = (IField)field.MemberDefinition; |
|
|
|
if (!displayClass.VariablesToDeclare.TryGetValue(keyField, out var v) || v == null) { |
|
|
|
var v = displayClass.VariablesToDeclare[keyField]; |
|
|
|
displayClass.VariablesToDeclare[keyField] = v = new VariableToDeclare(displayClass, field); |
|
|
|
context.Step($"Replace {field.Name} with captured variable {v.Name}", inst); |
|
|
|
|
|
|
|
ILVariable variable = v.GetOrDeclare(); |
|
|
|
|
|
|
|
inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); |
|
|
|
|
|
|
|
if (variable.Function != currentFunction && variable.Kind != VariableKind.DisplayClassLocal) { |
|
|
|
|
|
|
|
currentFunction.CapturedVariables.Add(variable); |
|
|
|
} |
|
|
|
} |
|
|
|
context.Step($"Replace {field.FullName} with captured variable {v.Name}", inst); |
|
|
|
|
|
|
|
inst.ReplaceWith(new LdLoca(v.GetOrDeclare()).WithILRange(inst)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|