|
|
@ -37,26 +37,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
public ILInstruction Initializer; |
|
|
|
public ILInstruction Initializer; |
|
|
|
public ILVariable Variable; |
|
|
|
public ILVariable Variable; |
|
|
|
public ITypeDefinition Definition; |
|
|
|
public ITypeDefinition Definition; |
|
|
|
public Dictionary<IField, DisplayClassVariable> Variables; |
|
|
|
public Dictionary<IField, ILVariable> Variables; |
|
|
|
public BlockContainer CaptureScope; |
|
|
|
public BlockContainer CaptureScope; |
|
|
|
public ILFunction DeclaringFunction; |
|
|
|
public ILFunction DeclaringFunction; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct DisplayClassVariable |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
public ILVariable Variable; |
|
|
|
|
|
|
|
public ILInstruction Value; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ILTransformContext context; |
|
|
|
ILTransformContext context; |
|
|
|
ILFunction currentFunction; |
|
|
|
|
|
|
|
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>(); |
|
|
|
|
|
|
|
|
|
|
|
public void Run(ILFunction function, ILTransformContext context) |
|
|
|
public void Run(ILFunction function, ILTransformContext context) |
|
|
|
{ |
|
|
|
{ |
|
|
|
try { |
|
|
|
try { |
|
|
|
if (this.context != null || this.currentFunction != null) |
|
|
|
if (this.context != null) |
|
|
|
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); |
|
|
|
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); |
|
|
|
this.context = context; |
|
|
|
this.context = context; |
|
|
|
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); |
|
|
|
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); |
|
|
@ -73,12 +66,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); |
|
|
|
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { |
|
|
|
|
|
|
|
context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer); |
|
|
|
|
|
|
|
this.currentFunction = f; |
|
|
|
|
|
|
|
VisitILFunction(f); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
VisitILFunction(function); |
|
|
|
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) { |
|
|
@ -91,7 +80,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
instructionsToRemove.Clear(); |
|
|
|
instructionsToRemove.Clear(); |
|
|
|
displayClasses.Clear(); |
|
|
|
displayClasses.Clear(); |
|
|
|
this.context = null; |
|
|
|
this.context = null; |
|
|
|
this.currentFunction = null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -106,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
Initializer = inst, |
|
|
|
Initializer = inst, |
|
|
|
Variable = v, |
|
|
|
Variable = v, |
|
|
|
Definition = closureType, |
|
|
|
Definition = closureType, |
|
|
|
Variables = new Dictionary<IField, DisplayClassVariable>(), |
|
|
|
Variables = new Dictionary<IField, ILVariable>(), |
|
|
|
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope, |
|
|
|
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope, |
|
|
|
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f |
|
|
|
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f |
|
|
|
}); |
|
|
|
}); |
|
|
@ -151,11 +139,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool IsOuterClosureReference(IField field) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool IsMonoNestedCaptureScope(ITypeDefinition closureType) |
|
|
|
bool IsMonoNestedCaptureScope(ITypeDefinition closureType) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); |
|
|
|
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); |
|
|
@ -181,17 +164,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
Initializer = nestedFunction.Body, |
|
|
|
Initializer = nestedFunction.Body, |
|
|
|
Variable = thisVariable, |
|
|
|
Variable = thisVariable, |
|
|
|
Definition = thisVariable.Type.GetDefinition(), |
|
|
|
Definition = thisVariable.Type.GetDefinition(), |
|
|
|
Variables = new Dictionary<IField, DisplayClassVariable>(), |
|
|
|
Variables = new Dictionary<IField, ILVariable>(), |
|
|
|
CaptureScope = (BlockContainer)nestedFunction.Body |
|
|
|
CaptureScope = (BlockContainer)nestedFunction.Body |
|
|
|
}; |
|
|
|
}; |
|
|
|
displayClasses.Add(thisVariable, displayClass); |
|
|
|
displayClasses.Add(thisVariable, displayClass); |
|
|
|
foreach (var stateMachineVariable in nestedFunction.Variables) { |
|
|
|
foreach (var stateMachineVariable in nestedFunction.Variables) { |
|
|
|
if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField)) |
|
|
|
if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField)) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
displayClass.Variables.Add(stateMachineVariable.StateMachineField, new DisplayClassVariable { |
|
|
|
displayClass.Variables.Add(stateMachineVariable.StateMachineField, stateMachineVariable); |
|
|
|
Variable = stateMachineVariable, |
|
|
|
|
|
|
|
Value = new LdLoc(stateMachineVariable) |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) { |
|
|
|
if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) { |
|
|
|
var thisVar = currentFunction.Variables |
|
|
|
var thisVar = currentFunction.Variables |
|
|
@ -200,7 +180,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" }; |
|
|
|
currentFunction.Variables.Add(thisVar); |
|
|
|
currentFunction.Variables.Add(thisVar); |
|
|
|
} |
|
|
|
} |
|
|
|
displayClass.Variables.Add(thisField, new DisplayClassVariable { Variable = thisVar, Value = new LdLoc(thisVar) }); |
|
|
|
displayClass.Variables.Add(thisField, thisVar); |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
@ -263,11 +243,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable) |
|
|
|
ILFunction currentFunction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitILFunction(ILFunction function) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) |
|
|
|
var oldFunction = this.currentFunction; |
|
|
|
return true; |
|
|
|
try { |
|
|
|
return false; |
|
|
|
this.currentFunction = function; |
|
|
|
|
|
|
|
base.VisitILFunction(function); |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
this.currentFunction = oldFunction; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected override void Default(ILInstruction inst) |
|
|
|
protected override void Default(ILInstruction inst) |
|
|
@ -280,105 +266,102 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
protected internal override void VisitStLoc(StLoc inst) |
|
|
|
protected internal override void VisitStLoc(StLoc inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.VisitStLoc(inst); |
|
|
|
base.VisitStLoc(inst); |
|
|
|
// Sometimes display class references are copied into other local variables.
|
|
|
|
|
|
|
|
// We remove the assignment and store the relationship between the display class and the variable in the
|
|
|
|
if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { |
|
|
|
// displayClasses dictionary.
|
|
|
|
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); |
|
|
|
if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) { |
|
|
|
|
|
|
|
displayClasses[inst.Variable] = displayClass; |
|
|
|
|
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
|
|
|
|
} else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { |
|
|
|
|
|
|
|
inst.ReplaceWith(inst.Value); |
|
|
|
inst.ReplaceWith(inst.Value); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitStObj(StObj inst) |
|
|
|
protected internal override void VisitStObj(StObj inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.VisitStObj(inst); |
|
|
|
inst.Value.AcceptVisitor(this); |
|
|
|
// This instruction has been marked deletable, do not transform it further
|
|
|
|
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) { |
|
|
|
if (instructionsToRemove.Contains(inst)) |
|
|
|
context.Step($"Detected parameter assignment {parameter.Name}", inst); |
|
|
|
return; |
|
|
|
displayClass.Variables.Add(field, parameter); |
|
|
|
// The target of the store instruction must be a field reference
|
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) |
|
|
|
} else if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) { |
|
|
|
return; |
|
|
|
context.Step($"Detected display-class assignment {variable.Name}", inst); |
|
|
|
// Get display class info
|
|
|
|
displayClass.Variables.Add(field, variable); |
|
|
|
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) |
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
return; |
|
|
|
|
|
|
|
field = (IField)field.MemberDefinition; |
|
|
|
|
|
|
|
if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { |
|
|
|
|
|
|
|
// If the display class field was previously initialized, we use a simple assignment.
|
|
|
|
|
|
|
|
inst.ReplaceWith(new StLoc(info.Variable, inst.Value).WithILRange(inst)); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// This is an uninitialized variable:
|
|
|
|
inst.Target.AcceptVisitor(this); |
|
|
|
ILInstruction value; |
|
|
|
EarlyExpressionTransforms.StObjToStLoc(inst, context); |
|
|
|
if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { |
|
|
|
|
|
|
|
// Special case for parameters: remove copies of parameter values.
|
|
|
|
|
|
|
|
instructionsToRemove.Add(inst); |
|
|
|
|
|
|
|
value = inst.Value; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
context.Step($"Introduce captured variable for {field.FullName}", inst); |
|
|
|
|
|
|
|
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); |
|
|
|
|
|
|
|
// Introduce a fresh variable for the display class field.
|
|
|
|
|
|
|
|
if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) { |
|
|
|
|
|
|
|
displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); |
|
|
|
|
|
|
|
v.HasInitialValue = true; |
|
|
|
|
|
|
|
v.CaptureScope = displayClass.CaptureScope; |
|
|
|
|
|
|
|
inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); |
|
|
|
|
|
|
|
value = new LdLoc(v); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
displayClass.Variables.Add(field, new DisplayClassVariable { Value = value, Variable = v }); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitLdObj(LdObj inst) |
|
|
|
protected internal override void VisitLdObj(LdObj inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.VisitLdObj(inst); |
|
|
|
base.VisitLdObj(inst); |
|
|
|
// The target of the store instruction must be a field reference
|
|
|
|
EarlyExpressionTransforms.LdObjToLdLoc(inst, context); |
|
|
|
if (!inst.Target.MatchLdFlda(out var target, out IField field)) |
|
|
|
} |
|
|
|
return; |
|
|
|
|
|
|
|
// Get display class info
|
|
|
|
private bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable) |
|
|
|
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) |
|
|
|
{ |
|
|
|
return; |
|
|
|
// We cannot use MatchLdLocRef here because local functions use ref parameters
|
|
|
|
// Get display class variable info
|
|
|
|
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) |
|
|
|
if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) |
|
|
|
return true; |
|
|
|
return; |
|
|
|
return false; |
|
|
|
// Replace usage of display class field with the variable.
|
|
|
|
} |
|
|
|
var replacement = info.Value.Clone(); |
|
|
|
|
|
|
|
replacement.SetILRange(inst); |
|
|
|
private bool IsDisplayClassAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable variable) |
|
|
|
inst.ReplaceWith(replacement); |
|
|
|
{ |
|
|
|
|
|
|
|
variable = null; |
|
|
|
|
|
|
|
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v))) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (displayClassVar.Function != currentFunction) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
variable = v; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable parameter) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
parameter = null; |
|
|
|
|
|
|
|
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (displayClass.Variables.ContainsKey(field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (displayClassVar.Function != currentFunction) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
parameter = v; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
displayClass = null; |
|
|
|
|
|
|
|
displayClassVar = null; |
|
|
|
|
|
|
|
field = null; |
|
|
|
|
|
|
|
if (!(inst is LdFlda ldflda)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
field = (IField)ldflda.Field.MemberDefinition; |
|
|
|
|
|
|
|
return IsDisplayClassLoad(ldflda.Target, out displayClassVar) |
|
|
|
|
|
|
|
&& displayClasses.TryGetValue(displayClassVar, out displayClass); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected internal override void VisitLdFlda(LdFlda inst) |
|
|
|
protected internal override void VisitLdFlda(LdFlda inst) |
|
|
|
{ |
|
|
|
{ |
|
|
|
base.VisitLdFlda(inst); |
|
|
|
base.VisitLdFlda(inst); |
|
|
|
// TODO : Figure out why this was added in https://github.com/icsharpcode/ILSpy/pull/1303
|
|
|
|
|
|
|
|
if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" |
|
|
|
|
|
|
|
&& inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { |
|
|
|
|
|
|
|
//Debug.Assert(false, "This should not be executed!");
|
|
|
|
|
|
|
|
var variable = currentFunction.Variables.First((f) => f.Index == -1); |
|
|
|
|
|
|
|
inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Skip stfld/ldfld
|
|
|
|
|
|
|
|
if (inst.Parent is LdObj || inst.Parent is StObj) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
// Get display class info
|
|
|
|
// Get display class info
|
|
|
|
if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) |
|
|
|
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field)) |
|
|
|
return; |
|
|
|
return; |
|
|
|
var field = (IField)inst.Field.MemberDefinition; |
|
|
|
if (!displayClass.Variables.TryGetValue(field, out var v)) { |
|
|
|
if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { |
|
|
|
|
|
|
|
context.Step($"Introduce captured variable for {field.FullName}", inst); |
|
|
|
context.Step($"Introduce captured variable for {field.FullName}", inst); |
|
|
|
// Introduce a fresh variable for the display class field.
|
|
|
|
// Introduce a fresh variable for the display class field.
|
|
|
|
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); |
|
|
|
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); |
|
|
|
var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); |
|
|
|
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); |
|
|
|
v.HasInitialValue = true; |
|
|
|
v.HasInitialValue = true; |
|
|
|
v.CaptureScope = displayClass.CaptureScope; |
|
|
|
v.CaptureScope = displayClass.CaptureScope; |
|
|
|
inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); |
|
|
|
inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); |
|
|
|
displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v }); |
|
|
|
displayClass.Variables.Add(field, v); |
|
|
|
} else if (info.Value is LdLoc l) { |
|
|
|
|
|
|
|
inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst)); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Debug.Fail("LdFlda pattern not supported!"); |
|
|
|
context.Step($"Reuse captured variable {v.Name} for {field.FullName}", inst); |
|
|
|
|
|
|
|
inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|