Browse Source

Fixed all TDCU related tests.

pull/2005/head
Siegfried Pammer 5 years ago
parent
commit
53c593af0b
  1. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
  2. 324
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

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

@ -62,7 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
// Do not try to transform display class usages or delegate construction. // Do not try to transform display class usages or delegate construction.
// DelegateConstruction transform cannot deal with this. // DelegateConstruction transform cannot deal with this.
if (TransformDisplayClassUsage.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) if (TransformDisplayClassUsage.AnalyzeVariable(v, context))
return false; return false;
if (DelegateConstruction.IsDelegateConstruction(newObjInst) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst)) if (DelegateConstruction.IsDelegateConstruction(newObjInst) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst))
return false; return false;

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

@ -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));
} }
} }
} }

Loading…
Cancel
Save