Browse Source

Refactor representation of local functions in ILAst.

pull/1586/head
Siegfried Pammer 6 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. 82
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  11. 20
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

22
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -687,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -687,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
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:
blob.SkipOperand(code);
break;

8
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

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

13
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -198,8 +198,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -198,8 +198,8 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
}
foreach (var (key, (functionName, definition)) in function.LocalFunctions) {
if (functionName == name)
foreach (var f in function.LocalFunctions.OfType<ILFunction>()) {
if (f.Name == name)
return true;
}
@ -212,15 +212,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -212,15 +212,16 @@ namespace ICSharpCode.Decompiler.CSharp
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));
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
if (parent.LocalFunctions.TryGetValue(method, out var info)) {
return info;
var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method);
if (definition != null) {
return definition;
}
}
return (null, null);
return null;
}
bool RequiresQualifier(IMember member, TranslatedExpression target)

60
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -733,11 +733,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -733,11 +733,24 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
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;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false;
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>
@ -850,6 +863,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -850,6 +863,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt)
continueStmt.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(new PrimitiveExpression(true), blockStatement);
case ContainerKind.While:
continueTarget = container.EntryPoint;
@ -871,6 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -871,6 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2)
continueStmt2.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement);
case ContainerKind.DoWhile:
continueTarget = container.Blocks.Last();
@ -889,6 +904,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -889,6 +904,7 @@ namespace ICSharpCode.Decompiler.CSharp
// to continue statements, we have to introduce an extra label.
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
}
DeclareLocalFunctions(currentFunction, container, blockStatement);
if (blockStatement.Statements.Count == 0) {
return new WhileStatement {
Condition = exprBuilder.TranslateCondition(condition),
@ -920,6 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -920,6 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (continueTarget.IncomingEdgeCount > continueCount)
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
DeclareLocalFunctions(currentFunction, container, blockStatement);
return forStmt;
default:
throw new ArgumentOutOfRangeException();
@ -928,7 +945,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -928,7 +945,31 @@ namespace ICSharpCode.Decompiler.CSharp
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)
@ -1016,20 +1057,5 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1016,20 +1057,5 @@ namespace ICSharpCode.Decompiler.CSharp
stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment);
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 @@ -415,7 +415,8 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" init");
}
if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label);
output.Write(" captured in ");
output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope);
}
if (StateMachineField != null) {
output.Write(" from state-machine");

47
ICSharpCode.Decompiler/IL/Instructions.cs

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

14
ICSharpCode.Decompiler/IL/Instructions.tt

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

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

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

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

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

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

@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var inst in function.Descendants) {
cancellationToken.ThrowIfCancellationRequested();
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;
if (!localFunctions.TryGetValue(call.Method, out var info)) {
info = new List<CallInstruction>() { call };
@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
info.Add(call);
}
} 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;
context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn);
if (!localFunctions.TryGetValue(ldftn.Method, out var info)) {
@ -72,40 +72,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -72,40 +72,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
foreach (var (method, useSites) in localFunctions) {
var insertionPoint = FindInsertionPoint(useSites);
context.StepStartGroup($"LocalFunctionDecompiler {insertionPoint.StartILOffset}", insertionPoint);
context.StepStartGroup($"LocalFunctionDecompiler {useSites[0].StartILOffset}", useSites[0]);
try {
TransformLocalFunction(function, method, useSites, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1);
TransformLocalFunction(function, method, useSites);
} finally {
context.StepEndGroup();
}
}
}
static ILInstruction FindInsertionPoint(List<CallInstruction> useSites)
{
ILInstruction insertionPoint = null;
foreach (var call in useSites) {
if (insertionPoint == null) {
insertionPoint = GetStatement(call);
continue;
}
var ancestor = FindCommonAncestorInstruction(insertionPoint, GetStatement(call));
if (ancestor == null)
return null;
insertionPoint = ancestor;
foreach (var f in function.LocalFunctions) {
// handle nested functions
var nestedContext = new ILTransformContext(context, f);
nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", f);
new LocalFunctionDecompiler().Run(f, nestedContext);
nestedContext.StepEndGroup();
}
return insertionPoint;
if (function.Kind == ILFunctionKind.TopLevelFunction) {
var movableFunctions = TreeTraversal.PostOrder(function, f => f.LocalFunctions)
.Where(f => f.Kind == ILFunctionKind.LocalFunction && f.DeclarationScope == null)
.ToArray();
foreach (var f in movableFunctions) {
var parent = (ILFunction)f.Parent;
f.DeclarationScope = (BlockContainer)function.Body;
parent.LocalFunctions.Remove(f);
function.LocalFunctions.Add(f);
}
}
}
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);
return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains);
var ancestorsOfB = new HashSet<T>(b.Ancestors.OfType<T>());
return a.Ancestors.OfType<T>().FirstOrDefault(ancestorsOfB.Contains);
}
internal static bool IsClosureParameter(IParameter parameter)
@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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);
if (!methodDefinition.HasBody())
@ -137,7 +137,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -137,7 +137,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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
// 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);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
@ -147,11 +148,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -147,11 +148,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter);
function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
}
parentFunction.LocalFunctions.Add(function.Method, (function.Method.Name, function));
// handle nested functions
nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", function);
new LocalFunctionDecompiler().Run(function, nestedContext);
nestedContext.StepEndGroup();
function.DeclarationScope = null;
foreach (var useSite in useSites) {
for (int i = useSite.Arguments.Count - 1; i >= 0; i--) {
if (!useSite.Arguments[i].MatchLdLocRef(out var closureVar))
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;
}
@ -190,6 +206,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -190,6 +206,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public static bool IsLocalFunctionMethod(IMethod method)
{
if (method.MetadataToken.IsNil)
return false;
return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken);
}

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

@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (HandleMonoStateMachine(function, v, decompilationContext, f))
continue;
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)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body);
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, localFunctionClosureParameter: true);
}
}
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
@ -91,21 +91,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
if (displayClass == null) {
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
displayClasses.Add(v, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope
});
} else {
if (displayClass.CaptureScope == null && !localFunctionClosureParameter)
displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope;
displayClass.Variable = v;
displayClass.Initializer = inst;
displayClasses.Add(v, displayClass);
@ -124,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -124,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
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());
return true;
}
@ -242,8 +244,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -242,8 +244,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true;
if (target.MatchAddressOf(out var load) && load.MatchLdLoc(out variable))
return true;
return false;
}

Loading…
Cancel
Save