Browse Source

Refactor representation of local functions in ILAst.

pull/1586/head
Siegfried Pammer 7 years ago
parent
commit
305b47245e
  1. 22
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 8
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 13
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 60
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  5. 3
      ICSharpCode.Decompiler/IL/ILVariable.cs
  6. 47
      ICSharpCode.Decompiler/IL/Instructions.cs
  7. 14
      ICSharpCode.Decompiler/IL/Instructions.tt
  8. 34
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  9. 14
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  10. 80
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  11. 18
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

22
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -687,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp
connectedMethods.Enqueue((MethodDefinitionHandle)token); connectedMethods.Enqueue((MethodDefinitionHandle)token);
} }
break; break;
case ILOpCode.Call:
case ILOpCode.Callvirt:
// deal with call/callvirt instructions, i.e., local function invocations
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (token.IsNil)
continue;
switch (token.Kind) {
case HandleKind.MethodDefinition:
break;
case HandleKind.MethodSpecification:
var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token);
if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition)
continue;
token = methodSpec.Method;
break;
default:
continue;
}
if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
break;
default: default:
blob.SkipOperand(code); blob.SkipOperand(code);
break; break;

8
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -183,8 +183,8 @@ namespace ICSharpCode.Decompiler.CSharp
CallOpCode = callOpCode, CallOpCode = callOpCode,
IsLocalFunction = expressionBuilder.IsLocalFunction(method) IsLocalFunction = expressionBuilder.IsLocalFunction(method)
}; };
(string Name, ILFunction Definition) localFunction = default; ILFunction localFunction = null;
if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)).Definition == null) { if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)) == null) {
expectedTargetDetails.IsLocalFunction = false; expectedTargetDetails.IsLocalFunction = false;
} }
TranslatedExpression target; TranslatedExpression target;
@ -193,7 +193,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (expectedTargetDetails.IsLocalFunction) { } else if (expectedTargetDetails.IsLocalFunction) {
target = new IdentifierExpression(localFunction.Name) target = new IdentifierExpression(localFunction.Name)
.WithoutILInstruction() .WithoutILInstruction()
.WithRR(new LocalFunctionReferenceResolveResult(localFunction.Definition)); .WithRR(new LocalFunctionReferenceResolveResult(localFunction));
} else { } else {
target = expressionBuilder.TranslateTarget( target = expressionBuilder.TranslateTarget(
callArguments.FirstOrDefault(), callArguments.FirstOrDefault(),
@ -1248,7 +1248,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (expectedTargetDetails.IsLocalFunction) { if (expectedTargetDetails.IsLocalFunction) {
requireTarget = false; requireTarget = false;
var localFunction = expressionBuilder.ResolveLocalFunction(method); var localFunction = expressionBuilder.ResolveLocalFunction(method);
result = new LocalFunctionReferenceResolveResult(localFunction.Definition); result = new LocalFunctionReferenceResolveResult(localFunction);
target = default; target = default;
methodName = localFunction.Name; methodName = localFunction.Name;
} else { } else {

13
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -198,8 +198,8 @@ namespace ICSharpCode.Decompiler.CSharp
return true; return true;
} }
foreach (var (key, (functionName, definition)) in function.LocalFunctions) { foreach (var f in function.LocalFunctions.OfType<ILFunction>()) {
if (functionName == name) if (f.Name == name)
return true; return true;
} }
@ -212,15 +212,16 @@ namespace ICSharpCode.Decompiler.CSharp
return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method); return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method);
} }
internal (string Name, ILFunction Definition) ResolveLocalFunction(IMethod method) internal ILFunction ResolveLocalFunction(IMethod method)
{ {
Debug.Assert(IsLocalFunction(method)); Debug.Assert(IsLocalFunction(method));
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) { foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
if (parent.LocalFunctions.TryGetValue(method, out var info)) { var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method);
return info; if (definition != null) {
return definition;
} }
} }
return (null, null); return null;
} }
bool RequiresQualifier(IMember member, TranslatedExpression target) bool RequiresQualifier(IMember member, TranslatedExpression target)

60
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -733,11 +733,24 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
return false; return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type))) if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst)))
return false; return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst)) if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false; return false;
return true; return true;
bool AddressUseAllowed(LdLoca la)
{
if (!la.IsDescendantOf(usingContainer))
return false;
if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type))
return true;
var current = la.Parent;
while (current is LdFlda next) {
current = next.Parent;
}
return current is LdObj;
}
} }
/// <summary> /// <summary>
@ -850,6 +863,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt) if (blockStatement.LastOrDefault() is ContinueStatement continueStmt)
continueStmt.Remove(); continueStmt.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(new PrimitiveExpression(true), blockStatement); return new WhileStatement(new PrimitiveExpression(true), blockStatement);
case ContainerKind.While: case ContainerKind.While:
continueTarget = container.EntryPoint; continueTarget = container.EntryPoint;
@ -871,6 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2) if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2)
continueStmt2.Remove(); continueStmt2.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement); return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement);
case ContainerKind.DoWhile: case ContainerKind.DoWhile:
continueTarget = container.Blocks.Last(); continueTarget = container.Blocks.Last();
@ -889,6 +904,7 @@ namespace ICSharpCode.Decompiler.CSharp
// to continue statements, we have to introduce an extra label. // to continue statements, we have to introduce an extra label.
blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
} }
DeclareLocalFunctions(currentFunction, container, blockStatement);
if (blockStatement.Statements.Count == 0) { if (blockStatement.Statements.Count == 0) {
return new WhileStatement { return new WhileStatement {
Condition = exprBuilder.TranslateCondition(condition), Condition = exprBuilder.TranslateCondition(condition),
@ -920,6 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
if (continueTarget.IncomingEdgeCount > continueCount) if (continueTarget.IncomingEdgeCount > continueCount)
blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
DeclareLocalFunctions(currentFunction, container, blockStatement);
return forStmt; return forStmt;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
@ -928,7 +945,31 @@ namespace ICSharpCode.Decompiler.CSharp
BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop) BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop)
{ {
return ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
DeclareLocalFunctions(currentFunction, container, blockStatement);
return blockStatement;
}
void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement)
{
foreach (var localFunction in currentFunction.LocalFunctions) {
if (localFunction.DeclarationScope != container)
continue;
blockStatement.Add(TranslateFunction(localFunction));
}
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{
var stmt = new LocalFunctionDeclarationStatement();
var tsab = CSharpDecompiler.CreateAstBuilder(null);
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function));
return stmt;
}
} }
BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable<Block> blocks, bool isLoop) BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable<Block> blocks, bool isLoop)
@ -1016,20 +1057,5 @@ namespace ICSharpCode.Decompiler.CSharp
stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment); stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment);
return stmt; return stmt;
} }
protected internal override Statement VisitILFunction(ILFunction function)
{
var stmt = new LocalFunctionDeclarationStatement();
var tsab = CSharpDecompiler.CreateAstBuilder(null);
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
var localFunction = exprBuilder.ResolveLocalFunction(function.Method);
Debug.Assert(localFunction.Definition == function);
stmt.Name = localFunction.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function));
return stmt;
}
} }
} }

3
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -415,7 +415,8 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" init"); output.Write(" init");
} }
if (CaptureScope != null) { if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label); output.Write(" captured in ");
output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope);
} }
if (StateMachineField != null) { if (StateMachineField != null) {
output.Write(" from state-machine"); output.Write(" from state-machine");

47
ICSharpCode.Decompiler/IL/Instructions.cs

@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (CallInstruction)ShallowClone(); var clone = (CallInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.body, value, 0); SetChildInstruction(ref this.body, value, 0);
} }
} }
public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions");
public InstructionCollection<ILFunction> LocalFunctions { get; private set; }
protected sealed override int GetChildCount() protected sealed override int GetChildCount()
{ {
return 1; return 1 + LocalFunctions.Count;
} }
protected sealed override ILInstruction GetChild(int index) protected sealed override ILInstruction GetChild(int index)
{ {
@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL
case 0: case 0:
return this.body; return this.body;
default: default:
throw new IndexOutOfRangeException(); return this.LocalFunctions[index - 1];
} }
} }
protected sealed override void SetChild(int index, ILInstruction value) protected sealed override void SetChild(int index, ILInstruction value)
@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = value; this.Body = value;
break; break;
default: default:
throw new IndexOutOfRangeException(); this.LocalFunctions[index - 1] = (ILFunction)value;
break;
} }
} }
protected sealed override SlotInfo GetChildSlot(int index) protected sealed override SlotInfo GetChildSlot(int index)
@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL
case 0: case 0:
return BodySlot; return BodySlot;
default: default:
throw new IndexOutOfRangeException(); return LocalFunctionsSlot;
} }
} }
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (ILFunction)ShallowClone(); var clone = (ILFunction)ShallowClone();
clone.Body = this.body.Clone(); clone.Body = this.body.Clone();
clone.LocalFunctions = new InstructionCollection<ILFunction>(clone, 1);
clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone()));
clone.CloneVariables(); clone.CloneVariables();
return clone; return clone;
} }
@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as ILFunction; var o = other as ILFunction;
return o != null && this.body.PerformMatch(o.body, ref match); return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match);
} }
} }
} }
@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Indices[index - 0] = value; this.Indices[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (NewArr)ShallowClone(); var clone = (NewArr)ShallowClone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 0); clone.Indices = new InstructionCollection<ILInstruction>(clone, 0);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
public override StackType ResultType { get { return StackType.O; } } public override StackType ResultType { get { return StackType.O; } }
@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL
this.Array = value; this.Array = value;
break; break;
default: default:
this.Indices[index - 1] = value; this.Indices[index - 1] = (ILInstruction)value;
break; break;
} }
} }
@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL
var clone = (LdElema)ShallowClone(); var clone = (LdElema)ShallowClone();
clone.Array = this.array.Clone(); clone.Array = this.array.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 1); clone.Indices = new InstructionCollection<ILInstruction>(clone, 1);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced
@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (DynamicGetIndexInstruction)ShallowClone(); var clone = (DynamicGetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (DynamicSetIndexInstruction)ShallowClone(); var clone = (DynamicSetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (DynamicInvokeMemberInstruction)ShallowClone(); var clone = (DynamicInvokeMemberInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (DynamicInvokeConstructorInstruction)ShallowClone(); var clone = (DynamicInvokeConstructorInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
default: default:
this.Arguments[index - 0] = value; this.Arguments[index - 0] = (ILInstruction)value;
break; break;
} }
} }
@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var clone = (DynamicInvokeInstruction)ShallowClone(); var clone = (DynamicInvokeInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0); clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()

14
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,7 +49,8 @@
VoidResult, NoArguments, CustomWriteTo), VoidResult, NoArguments, CustomWriteTo),
new OpCode("ILFunction", "A container of IL blocks.", new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] { CustomChildren(new [] {
new ChildInfo("body") new ChildInfo("body"),
new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" }
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O") }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")
), ),
new OpCode("BlockContainer", "A container of IL blocks.", new OpCode("BlockContainer", "A container of IL blocks.",
@ -769,6 +770,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly string SlotName; public readonly string SlotName;
public bool IsCollection; public bool IsCollection;
public string Type = "ILInstruction";
public bool CanInlineInto; public bool CanInlineInto;
public string[] ExpectedTypes; public string[] ExpectedTypes;
@ -814,7 +816,7 @@ namespace ICSharpCode.Decompiler.IL
childCount = children.Length - 1; childCount = children.Length - 1;
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)"); opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)");
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg); opCode.ConstructorParameters.Add("params ILInstruction[] " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");"); opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");"); opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");");
opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)"); opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)");
if (i == 0) if (i == 0)
@ -827,7 +829,7 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);"); opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);");
opCode.WriteArguments.Add("}"); opCode.WriteArguments.Add("}");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }"); opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }");
} else { } else {
opCode.Flags.Add(arg + ".Flags"); opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg); opCode.ConstructorParameters.Add("ILInstruction " + arg);
@ -906,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL
if (collection == null) if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else { else {
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;"); b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;");
b.AppendLine("\t\t\tbreak;"); b.AppendLine("\t\t\tbreak;");
} }
b.AppendLine("\t}"); b.AppendLine("\t}");
@ -936,8 +938,8 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
for (int i = 0; i < children.Length; i++) { for (int i = 0; i < children.Length; i++) {
if (children[i].IsCollection) { if (children[i].IsCollection) {
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(clone, " + i + ");"); b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));"); b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));");
} else { } else {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
} }

34
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -18,11 +18,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Decompiler.IL.Transforms; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
@ -30,6 +31,8 @@ namespace ICSharpCode.Decompiler.IL
{ {
public readonly IMethod Method; public readonly IMethod Method;
public readonly GenericContext GenericContext; public readonly GenericContext GenericContext;
public string Name;
/// <summary> /// <summary>
/// Size of the IL code in this function. /// Size of the IL code in this function.
/// Note: after async/await transform, this is the code size of the MoveNext function. /// Note: after async/await transform, this is the code size of the MoveNext function.
@ -37,6 +40,12 @@ namespace ICSharpCode.Decompiler.IL
public int CodeSize; public int CodeSize;
public readonly ILVariableCollection Variables; public readonly ILVariableCollection Variables;
/// <summary>
/// Gets the scope in which the local function is declared.
/// Returns null, if this is not a local function.
/// </summary>
public BlockContainer DeclarationScope { get; internal set; }
/// <summary> /// <summary>
/// List of warnings of ILReader. /// List of warnings of ILReader.
/// </summary> /// </summary>
@ -110,8 +119,6 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
public Dictionary<IMethod, (string Name, ILFunction Declaration)> LocalFunctions { get; } = new Dictionary<IMethod, (string Name, ILFunction Declaration)>();
public readonly IType ReturnType; public readonly IType ReturnType;
public readonly IReadOnlyList<IParameter> Parameters; public readonly IReadOnlyList<IParameter> Parameters;
@ -119,12 +126,14 @@ namespace ICSharpCode.Decompiler.IL
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{ {
this.Method = method; this.Method = method;
this.Name = method.Name;
this.CodeSize = codeSize; this.CodeSize = codeSize;
this.GenericContext = genericContext; this.GenericContext = genericContext;
this.Body = body; this.Body = body;
this.ReturnType = Method?.ReturnType; this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters; this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this); this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = kind; this.kind = kind;
} }
@ -135,6 +144,7 @@ namespace ICSharpCode.Decompiler.IL
this.ReturnType = returnType; this.ReturnType = returnType;
this.Parameters = parameters; this.Parameters = parameters;
this.Variables = new ILVariableCollection(this); this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = ILFunctionKind.ExpressionTree; this.kind = ILFunctionKind.ExpressionTree;
} }
@ -144,20 +154,24 @@ namespace ICSharpCode.Decompiler.IL
case ILFunctionKind.TopLevelFunction: case ILFunctionKind.TopLevelFunction:
Debug.Assert(Parent == null); Debug.Assert(Parent == null);
Debug.Assert(DelegateType == null); Debug.Assert(DelegateType == null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(Method != null); Debug.Assert(Method != null);
break; break;
case ILFunctionKind.Delegate: case ILFunctionKind.Delegate:
Debug.Assert(Parent != null && !(Parent is Block)); Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null); Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1)); Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1));
break; break;
case ILFunctionKind.ExpressionTree: case ILFunctionKind.ExpressionTree:
Debug.Assert(Parent != null && !(Parent is Block)); Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null); Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1);
break; break;
case ILFunctionKind.LocalFunction: case ILFunctionKind.LocalFunction:
Debug.Assert(Parent is Block); Debug.Assert(Parent is ILFunction);
Debug.Assert(DeclarationScope != null);
Debug.Assert(DelegateType == null); Debug.Assert(DelegateType == null);
Debug.Assert(Method != null); Debug.Assert(Method != null);
break; break;
@ -205,6 +219,11 @@ namespace ICSharpCode.Decompiler.IL
if (IsIterator) { if (IsIterator) {
output.WriteLine(".iterator"); output.WriteLine(".iterator");
} }
if (DeclarationScope != null) {
output.Write("declared as " + Name + " in ");
output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope);
output.WriteLine();
}
output.MarkFoldStart(Variables.Count + " variable(s)", true); output.MarkFoldStart(Variables.Count + " variable(s)", true);
foreach (var variable in Variables) { foreach (var variable in Variables) {
@ -221,6 +240,11 @@ namespace ICSharpCode.Decompiler.IL
body.WriteTo(output, options); body.WriteTo(output, options);
output.WriteLine(); output.WriteLine();
foreach (var localFunction in LocalFunctions) {
output.WriteLine();
localFunction.WriteTo(output, options);
}
if (options.ShowILRanges) { if (options.ShowILRanges) {
var unusedILRanges = FindUnusedILRanges(); var unusedILRanges = FindUnusedILRanges();
if (!unusedILRanges.IsEmpty) { if (!unusedILRanges.IsEmpty) {

14
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -162,11 +162,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
} }
} }
foreach (var method in function.LocalFunctions.Keys.ToArray()) { foreach (var localFunction in function.LocalFunctions) {
var info = function.LocalFunctions[method]; if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
if (!LocalFunctionDecompiler.ParseLocalFunctionName(method.Name, out _, out var newName) || !IsValidName(newName))
newName = null; newName = null;
function.LocalFunctions[method] = (newName, info.Declaration); localFunction.Name = newName;
} }
// Now generate names: // Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance); var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
@ -180,13 +179,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.Name = name; v.Name = name;
} }
} }
foreach (var method in function.LocalFunctions.Keys.ToArray()) { foreach (var localFunction in function.LocalFunctions) {
var info = function.LocalFunctions[method]; var newName = localFunction.Name;
var newName = info.Name;
if (newName == null) { if (newName == null) {
newName = GetAlternativeName("f"); newName = GetAlternativeName("f");
} }
function.LocalFunctions[method] = (newName, info.Declaration); localFunction.Name = newName;
} }
} }

80
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var inst in function.Descendants) { foreach (var inst in function.Descendants) {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method)) { if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method)) {
if (function.Ancestors.OfType<ILFunction>().Any(f => f.LocalFunctions.ContainsKey(call.Method))) if (function.Ancestors.OfType<ILFunction>().SelectMany(f => f.LocalFunctions).Any(f => f.Method == call.Method))
continue; continue;
if (!localFunctions.TryGetValue(call.Method, out var info)) { if (!localFunctions.TryGetValue(call.Method, out var info)) {
info = new List<CallInstruction>() { call }; info = new List<CallInstruction>() { call };
@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
info.Add(call); info.Add(call);
} }
} else if (inst is LdFtn ldftn && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { } else if (inst is LdFtn ldftn && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) {
if (function.Ancestors.OfType<ILFunction>().Any(f => f.LocalFunctions.ContainsKey(ldftn.Method))) if (function.Ancestors.OfType<ILFunction>().SelectMany(f => f.LocalFunctions).Any(f => f.Method == ldftn.Method))
continue; continue;
context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn); context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn);
if (!localFunctions.TryGetValue(ldftn.Method, out var info)) { if (!localFunctions.TryGetValue(ldftn.Method, out var info)) {
@ -72,40 +72,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
foreach (var (method, useSites) in localFunctions) { foreach (var (method, useSites) in localFunctions) {
var insertionPoint = FindInsertionPoint(useSites); context.StepStartGroup($"LocalFunctionDecompiler {useSites[0].StartILOffset}", useSites[0]);
context.StepStartGroup($"LocalFunctionDecompiler {insertionPoint.StartILOffset}", insertionPoint);
try { try {
TransformLocalFunction(function, method, useSites, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1); TransformLocalFunction(function, method, useSites);
} finally { } finally {
context.StepEndGroup(); context.StepEndGroup();
} }
} }
}
static ILInstruction FindInsertionPoint(List<CallInstruction> useSites) foreach (var f in function.LocalFunctions) {
{ // handle nested functions
ILInstruction insertionPoint = null; var nestedContext = new ILTransformContext(context, f);
foreach (var call in useSites) { nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", f);
if (insertionPoint == null) { new LocalFunctionDecompiler().Run(f, nestedContext);
insertionPoint = GetStatement(call); nestedContext.StepEndGroup();
continue;
} }
var ancestor = FindCommonAncestorInstruction(insertionPoint, GetStatement(call)); if (function.Kind == ILFunctionKind.TopLevelFunction) {
var movableFunctions = TreeTraversal.PostOrder(function, f => f.LocalFunctions)
if (ancestor == null) .Where(f => f.Kind == ILFunctionKind.LocalFunction && f.DeclarationScope == null)
return null; .ToArray();
foreach (var f in movableFunctions) {
insertionPoint = ancestor; var parent = (ILFunction)f.Parent;
f.DeclarationScope = (BlockContainer)function.Body;
parent.LocalFunctions.Remove(f);
function.LocalFunctions.Add(f);
}
} }
return insertionPoint;
} }
static ILInstruction FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) static T FindCommonAncestorInstruction<T>(ILInstruction a, ILInstruction b)
where T : ILInstruction
{ {
var ancestorsOfB = new HashSet<ILInstruction>(b.Ancestors); var ancestorsOfB = new HashSet<T>(b.Ancestors.OfType<T>());
return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains); return a.Ancestors.OfType<T>().FirstOrDefault(ancestorsOfB.Contains);
} }
internal static bool IsClosureParameter(IParameter parameter) internal static bool IsClosureParameter(IParameter parameter)
@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return inst; return inst;
} }
private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List<CallInstruction> useSites, Block parent, int insertionPoint) private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List<CallInstruction> useSites)
{ {
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody()) if (!methodDefinition.HasBody())
@ -137,7 +137,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken);
// Embed the local function into the parent function's ILAst, so that "Show steps" can show // Embed the local function into the parent function's ILAst, so that "Show steps" can show
// how the local function body is being transformed. // how the local function body is being transformed.
parent.Instructions.Insert(insertionPoint, function); parentFunction.LocalFunctions.Add(function);
function.DeclarationScope = (BlockContainer)parentFunction.Body;
function.CheckInvariant(ILPhase.Normal); function.CheckInvariant(ILPhase.Normal);
var nestedContext = new ILTransformContext(context, function); var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
@ -147,11 +148,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter);
function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
} }
parentFunction.LocalFunctions.Add(function.Method, (function.Method.Name, function)); function.DeclarationScope = null;
// handle nested functions foreach (var useSite in useSites) {
nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", function); for (int i = useSite.Arguments.Count - 1; i >= 0; i--) {
new LocalFunctionDecompiler().Run(function, nestedContext); if (!useSite.Arguments[i].MatchLdLocRef(out var closureVar))
nestedContext.StepEndGroup(); break;
if (!TransformDisplayClassUsage.IsPotentialClosure(context, closureVar.Type.GetDefinition()))
break;
var instructions = closureVar.StoreInstructions.OfType<ILInstruction>()
.Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset);
var additionalScope = BlockContainer.FindClosestContainer(instructions.First());
if (closureVar.CaptureScope == null)
closureVar.CaptureScope = additionalScope;
else
closureVar.CaptureScope = FindCommonAncestorInstruction<BlockContainer>(closureVar.CaptureScope, additionalScope);
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(function.DeclarationScope, closureVar.CaptureScope);
}
}
return function; return function;
} }
@ -190,6 +206,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public static bool IsLocalFunctionMethod(IMethod method) public static bool IsLocalFunctionMethod(IMethod method)
{ {
if (method.MetadataToken.IsNil)
return false;
return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken); return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken);
} }

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

@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (HandleMonoStateMachine(function, v, decompilationContext, f)) if (HandleMonoStateMachine(function, v, decompilationContext, f))
continue; continue;
if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { if (IsClosure(v, out ITypeDefinition closureType, out var inst)) {
AddOrUpdateDisplayClass(f, v, closureType, inst); AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
} }
if (f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { 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)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body); AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
} }
} }
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
@ -91,21 +91,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst) private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter)
{ {
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType); var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType);
if (displayClass == null) {
// TODO : figure out whether it is a mono compiled closure, without relying on the type name // TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
if (displayClass == null) {
displayClasses.Add(v, new DisplayClass { displayClasses.Add(v, new DisplayClass {
IsMono = isMono, IsMono = isMono,
Initializer = inst, Initializer = inst,
Variable = v, Variable = v,
Definition = closureType, Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(), Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope
}); });
} else { } else {
if (displayClass.CaptureScope == null && !localFunctionClosureParameter)
displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope;
displayClass.Variable = v; displayClass.Variable = v;
displayClass.Initializer = inst; displayClass.Initializer = inst;
displayClasses.Add(v, displayClass); displayClasses.Add(v, displayClass);
@ -124,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
closureType = variable.Type.GetDefinition(); closureType = variable.Type.GetDefinition();
if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.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;
} }
@ -242,8 +244,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true; return true;
if (target.MatchAddressOf(out var load) && load.MatchLdLoc(out variable))
return true;
return false; return false;
} }

Loading…
Cancel
Save