Browse Source

Fix #1981: Ensure correctness of TDCU

pull/2005/head
Siegfried Pammer 5 years ago
parent
commit
b6259b7dca
  1. 387
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

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

@ -28,136 +28,255 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Transforms closure fields to local variables. /// Transforms closure fields to local variables.
/// ///
/// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/> and <see cref="TransformExpressionTrees"/>. /// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/>
/// and <see cref="TransformExpressionTrees"/>.
///
/// In general we can perform SROA (scalar replacement of aggregates) on any variable that
/// satisfies the following conditions:
/// 1) It is initialized by an empty/default constructor call.
/// 2) The variable is never passed to another method.
/// 3) The variable is never the target of an invocation.
///
/// Note that 2) and 3) apply because declarations and uses of lambdas and local functions
/// are already transformed by the time this transform is applied.
/// </summary> /// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform class TransformDisplayClassUsage : ILVisitor, IILTransform
{ {
[Flags]
enum Kind
{
DelegateConstruction = 1,
LocalFunction = 2,
ExpressionTree = 4,
MonoStateMachine = 8
}
class VariableToDeclare
{
private readonly DisplayClass container;
private readonly IField field;
private ILVariable declaredVariable;
public string Name => field.Name;
public List<ILInstruction> AssignedValues { get; } = new List<ILInstruction>();
public VariableToDeclare(DisplayClass container, IField field)
{
this.container = container;
this.field = field;
}
public void UseExisting(ILVariable variable)
{
Debug.Assert(this.declaredVariable == null);
this.declaredVariable = variable;
}
public ILVariable GetOrDeclare()
{
if (declaredVariable != null)
return declaredVariable;
declaredVariable = container.Variable.Function.RegisterVariable(VariableKind.Local, field.Type, field.Name);
declaredVariable.HasInitialValue = true;
declaredVariable.CaptureScope = container.CaptureScope;
return declaredVariable;
}
}
class DisplayClass class DisplayClass
{ {
public bool IsMono; public readonly ILVariable Variable;
public ILInstruction Initializer; public readonly ITypeDefinition Type;
public ILVariable Variable; public readonly Dictionary<IField, VariableToDeclare> VariablesToDeclare;
public ITypeDefinition Definition;
public Dictionary<IField, ILVariable> Variables;
public BlockContainer CaptureScope; public BlockContainer CaptureScope;
public ILFunction DeclaringFunction; public Kind Kind;
public DisplayClass(ILVariable variable, ITypeDefinition type)
{
Variable = variable;
Type = type;
VariablesToDeclare = new Dictionary<IField, VariableToDeclare>();
}
} }
ILTransformContext context; ILTransformContext context;
ITypeResolveContext decompilationContext;
readonly Dictionary<ILVariable, DisplayClass> displayClasses = new Dictionary<ILVariable, DisplayClass>(); readonly Dictionary<ILVariable, DisplayClass> displayClasses = new Dictionary<ILVariable, DisplayClass>();
readonly List<ILInstruction> instructionsToRemove = new List<ILInstruction>(); readonly List<ILInstruction> instructionsToRemove = new List<ILInstruction>();
readonly MultiDictionary<IField, StObj> fieldAssignmentsWithVariableValue = new MultiDictionary<IField, StObj>();
public void Run(ILFunction function, ILTransformContext context) void IILTransform.Run(ILFunction function, ILTransformContext context)
{ {
try {
if (this.context != null) if (this.context != null)
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
try {
this.context = context; this.context = context;
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); this.decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
AnalyzeFunction(function);
Transform(function);
} finally {
ClearState();
}
}
void ClearState()
{
instructionsToRemove.Clear();
displayClasses.Clear();
this.decompilationContext = null;
this.context = null;
}
public void Analyze(ILFunction function, ILTransformContext context)
{
ClearState();
this.context = context;
this.decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
AnalyzeFunction(function);
}
private void AnalyzeFunction(ILFunction function)
{
// Traverse nested functions in post-order: // Traverse nested functions in post-order:
// Inner functions are transformed before outer functions // 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()) {
if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f)) var result = AnalyzeVariable(v);
continue; if (result == null)
if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst)) {
if (!CanRemoveAllReferencesTo(context, v))
continue;
if (inst is StObj || inst is StLoc)
instructionsToRemove.Add(inst);
AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
continue; continue;
displayClasses.Add(v, result);
} }
if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
continue;
} }
AnalyzeUseSites(v);
} }
private DisplayClass AnalyzeVariable(ILVariable v)
{
ITypeDefinition definition;
switch (v.Kind) {
case VariableKind.Parameter:
if (context.Settings.YieldReturn && v.Function.StateMachineCompiledWithMono && v.IsThis())
return HandleMonoStateMachine(v.Function, v);
return null;
case VariableKind.StackSlot:
case VariableKind.Local:
case VariableKind.DisplayClassLocal:
if (!AnalyzeInitializer(out definition))
return null;
return Run(definition);
case VariableKind.PinnedLocal:
case VariableKind.UsingLocal:
case VariableKind.ForeachLocal:
case VariableKind.InitializerTarget:
case VariableKind.NamedArgument:
case VariableKind.ExceptionStackSlot:
case VariableKind.ExceptionLocal:
return null;
default:
throw new ArgumentOutOfRangeException();
} }
VisitILFunction(function);
if (instructionsToRemove.Count > 0) { DisplayClass Run(ITypeDefinition type)
context.Step($"Remove instructions", function); {
foreach (var store in instructionsToRemove) { var result = new DisplayClass(v, type) {
if (store.Parent is Block containingBlock) CaptureScope = v.CaptureScope
containingBlock.Instructions.Remove(store); };
foreach (var ldloc in v.LoadInstructions) {
if (!ValidateUse(result, ldloc))
return null;
} }
foreach (var ldloca in v.AddressInstructions) {
if (!ValidateUse(result, ldloca))
return null;
} }
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) if (result.VariablesToDeclare.Count == 0)
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); return null;
} finally { if (IsMonoNestedCaptureScope(type)) {
instructionsToRemove.Clear(); result.CaptureScope = null;
displayClasses.Clear();
fieldAssignmentsWithVariableValue.Clear();
this.context = null;
} }
return result;
} }
private bool CanRemoveAllReferencesTo(ILTransformContext context, ILVariable v) bool ValidateUse(DisplayClass result, ILInstruction use)
{ {
foreach (var use in v.LoadInstructions) { var current = use.Parent;
// we only accept stloc, stobj/ldobj and ld(s)flda instructions, ILInstruction value;
// as these are required by all patterns this transform understands. if (current.MatchLdFlda(out _, out IField field)) {
if (!(use.Parent is StLoc || use.Parent is LdFlda || use.Parent is LdsFlda || use.Parent is StObj || use.Parent is LdObj)) { var keyField = (IField)field.MemberDefinition;
return false; 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);
}
var containingFunction = current.Ancestors.OfType<ILFunction>().FirstOrDefault();
if (containingFunction != null && containingFunction != v.Function) {
switch (containingFunction.Kind) {
case ILFunctionKind.Delegate:
result.Kind |= Kind.DelegateConstruction;
break;
case ILFunctionKind.ExpressionTree:
result.Kind |= Kind.ExpressionTree;
break;
case ILFunctionKind.LocalFunction:
result.Kind |= Kind.LocalFunction;
break;
} }
if (use.Parent.MatchStLoc(out var targetVar) && !IsClosure(context, targetVar, out _, out _)) {
return false;
} }
result.VariablesToDeclare[keyField] = variable;
return true;
} }
if (current.MatchStObj(out _, out value, out _) && value == use)
return true; return true;
return false;
} }
private void AnalyzeUseSites(ILVariable v) bool AnalyzeInitializer(out ITypeDefinition t)
{ {
foreach (var use in v.LoadInstructions) { if (v.Kind != VariableKind.StackSlot) {
if (!(use.Parent?.Parent is Block)) t = v.Type.GetDefinition();
continue; } else if (v.StoreInstructions.Count > 0 && v.StoreInstructions[0] is StLoc stloc && stloc.Value is NewObj newObj) {
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) { t = newObj.Method.DeclaringTypeDefinition;
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent); } else {
} t = null;
if (use.Parent.MatchStsFld(out f, out value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
}
foreach (var use in v.AddressInstructions) {
if (!(use.Parent?.Parent is Block))
continue;
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
} }
if (use.Parent.MatchStsFld(out f, out value) && value == use) { if (t?.DeclaringTypeDefinition == null || t.ParentModule.PEFile != context.PEFile)
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent); return false;
switch (definition.Kind) {
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;
} }
} }
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter) private void Transform(ILFunction function)
{ {
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType); VisitILFunction(function);
// TODO : figure out whether it is a mono compiled closure, without relying on the type name if (instructionsToRemove.Count > 0) {
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); context.Step($"Remove instructions", function);
if (displayClass == null) { foreach (var store in instructionsToRemove) {
displayClasses.Add(v, new DisplayClass { if (store.Parent is Block containingBlock)
IsMono = isMono, containingBlock.Instructions.Remove(store);
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, ILVariable>(),
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope,
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f
});
} else {
if (displayClass.CaptureScope == null && !localFunctionClosureParameter)
displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope;
if (displayClass.CaptureScope != null && !localFunctionClosureParameter) {
displayClass.DeclaringFunction = displayClass.CaptureScope.Ancestors.OfType<ILFunction>().First();
} }
displayClass.Variable = v;
displayClass.Initializer = inst;
displayClasses.Add(v, displayClass);
} }
context.Step($"ResetHasInitialValueFlag", function);
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions))
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context);
} }
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)
@ -190,6 +309,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool IsMonoNestedCaptureScope(ITypeDefinition closureType) bool IsMonoNestedCaptureScope(ITypeDefinition closureType)
{ {
if (!closureType.Name.Contains("AnonStorey"))
return false;
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition())); return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition()));
} }
@ -198,45 +319,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type, /// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type,
/// We construct a <see cref="DisplayClass"/> that spans the whole method body. /// We construct a <see cref="DisplayClass"/> that spans the whole method body.
/// </summary> /// </summary>
bool HandleMonoStateMachine(ILFunction currentFunction, ILVariable thisVariable, SimpleTypeResolveContext decompilationContext, ILFunction nestedFunction) DisplayClass HandleMonoStateMachine(ILFunction function, ILVariable thisVariable)
{ {
if (!(nestedFunction.StateMachineCompiledWithMono && thisVariable.IsThis())) if (!(function.StateMachineCompiledWithMono && thisVariable.IsThis()))
return false; return null;
// Special case for Mono-compiled yield state machines // Special case for Mono-compiled yield state machines
ITypeDefinition closureType = thisVariable.Type.GetDefinition(); ITypeDefinition closureType = thisVariable.Type.GetDefinition();
if (!(closureType != decompilationContext.CurrentTypeDefinition if (!(closureType != decompilationContext.CurrentTypeDefinition
&& IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true))) && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true)))
return false; return null;
var displayClass = new DisplayClass { var displayClass = new DisplayClass(thisVariable, thisVariable.Type.GetDefinition());
IsMono = true, displayClass.CaptureScope = (BlockContainer)function.Body;
Initializer = nestedFunction.Body, foreach (var stateMachineVariable in function.Variables) {
Variable = thisVariable, if (stateMachineVariable.StateMachineField == null || displayClass.VariablesToDeclare.ContainsKey(stateMachineVariable.StateMachineField))
Definition = thisVariable.Type.GetDefinition(),
Variables = new Dictionary<IField, ILVariable>(),
CaptureScope = (BlockContainer)nestedFunction.Body
};
displayClasses.Add(thisVariable, displayClass);
foreach (var stateMachineVariable in nestedFunction.Variables) {
if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField))
continue; continue;
displayClass.Variables.Add(stateMachineVariable.StateMachineField, stateMachineVariable); VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, stateMachineVariable.StateMachineField);
variableToDeclare.UseExisting(stateMachineVariable);
displayClass.VariablesToDeclare.Add(stateMachineVariable.StateMachineField, variableToDeclare);
} }
if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) { if (!function.Method.IsStatic && FindThisField(out var thisField)) {
var thisVar = currentFunction.Variables var thisVar = function.Variables
.FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition); .FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition);
if (thisVar == null) { if (thisVar == null) {
thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" }; thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" };
currentFunction.Variables.Add(thisVar); function.Variables.Add(thisVar);
} }
displayClass.Variables.Add(thisField, thisVar); VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, thisField);
variableToDeclare.UseExisting(thisVar);
displayClass.VariablesToDeclare.Add(thisField, variableToDeclare);
} }
return true; return displayClass;
bool FindThisField(out IField foundField) bool FindThisField(out IField foundField)
{ {
foundField = null; foundField = null;
foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.Variables.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) { foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.VariablesToDeclare.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) {
thisField = field; thisField = field;
return true; return true;
} }
@ -317,11 +435,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
base.VisitStLoc(inst); base.VisitStLoc(inst);
if (inst.Parent is Block && inst.Variable.IsSingleDefinition) { if (inst.Parent is Block && inst.Variable.IsSingleDefinition) {
if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0 && (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction)) {
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
inst.ReplaceWith(inst.Value); inst.ReplaceWith(inst.Value);
return; return;
} }
if (displayClasses.TryGetValue(inst.Variable, out _) && inst.Value is NewObj) {
instructionsToRemove.Add(inst);
return;
}
if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass)) { 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); context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst);
displayClasses.Add(inst.Variable, displayClass); displayClasses.Add(inst.Variable, displayClass);
@ -335,15 +457,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
inst.Value.AcceptVisitor(this); inst.Value.AcceptVisitor(this);
if (inst.Parent is Block) { if (inst.Parent is Block) {
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) { if (IsParameterAssignment(inst, out var declarationSlot, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst); context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, parameter); declarationSlot.UseExisting(parameter);
instructionsToRemove.Add(inst); instructionsToRemove.Add(inst);
return; return;
} }
if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) { if (IsDisplayClassAssignment(inst, out declarationSlot, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst); context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, variable); declarationSlot.UseExisting(variable);
instructionsToRemove.Add(inst); instructionsToRemove.Add(inst);
return; return;
} }
@ -366,32 +488,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
private bool IsDisplayClassAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable variable) private bool IsDisplayClassAssignment(StObj inst, out VariableToDeclare declarationSlot, out ILVariable variable)
{ {
variable = null; variable = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) declarationSlot = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out var displayClass, out var field))
return false; return false;
if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v))) if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v)))
return false; return false;
if (!displayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out declarationSlot))
return false;
if (displayClassVar.Function != currentFunction) if (displayClassVar.Function != currentFunction)
return false; return false;
variable = v; variable = v;
return true; return true;
} }
private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable parameter) private bool IsParameterAssignment(StObj inst, out VariableToDeclare declarationSlot, out ILVariable parameter)
{ {
parameter = null; parameter = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) declarationSlot = null;
return false; if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out var displayClass, out var field))
if (fieldAssignmentsWithVariableValue[field].Count != 1)
return false; return false;
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type))) if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type)))
return false; return false;
if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition)) if (!displayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out declarationSlot))
return false; return false;
if (displayClassVar.Function != currentFunction) if (displayClassVar.Function != currentFunction)
return false; 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; parameter = v;
return true; return true;
} }
@ -414,24 +543,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Get display class info // Get display class info
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field)) if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field))
return; return;
// We want the specialized version, so that display-class type parameters are var keyField = (IField)field.MemberDefinition;
// substituted with the type parameters from the use-site. if (!displayClass.VariablesToDeclare.TryGetValue(keyField, out var v) || v == null) {
var fieldType = field.Type; displayClass.VariablesToDeclare[keyField] = v = new VariableToDeclare(displayClass, field);
// However, use the unspecialized member definition to make reference comparisons in dictionary possible.
field = (IField)field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out var v)) {
context.Step($"Introduce captured variable for {field.FullName}", inst);
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, fieldType, field.Name);
v.HasInitialValue = true;
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
displayClass.Variables.Add(field, v);
} else {
context.Step($"Reuse captured variable {v.Name} for {field.FullName}", inst);
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
} }
context.Step($"Replace {field.FullName} with captured variable {v.Name}", inst);
inst.ReplaceWith(new LdLoca(v.GetOrDeclare()).WithILRange(inst));
} }
} }
} }

Loading…
Cancel
Save