Browse Source

Basic implementation of local functions.

pull/1586/head
Siegfried Pammer 6 years ago
parent
commit
bd77b8301f
  1. 3
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 19
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  3. 11
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 21
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  5. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  6. 94
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  7. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  8. 2
      ICSharpCode.Decompiler/DecompilerSettings.cs
  9. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  11. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  12. 15
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  13. 64
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  14. 8
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  15. 103
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  16. 2
      ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs
  17. 7
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  18. 2
      ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs
  19. 2
      ILSpy/Languages/ILAstLanguage.cs

3
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp
},
new ProxyCallReplacer(),
new DelegateConstruction(),
new LocalFunctionDecompiler(),
new HighLevelLoopTransform(),
new ReduceNestingTransform(),
new IntroduceDynamicTypeOnLocals(),
@ -395,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -395,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new DecompilerTypeSystem(file, resolver);
}
TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
internal static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
{
var typeSystemAstBuilder = new TypeSystemAstBuilder();
typeSystemAstBuilder.ShowAttributes = true;

19
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Semicolon();
EndNode(variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
StartNode(localFunctionDeclarationStatement);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
EndNode(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement(WhileStatement whileStatement)
{

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -1015,5 +1015,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1015,5 +1015,16 @@ 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);
stmt.Name = function.Method.Name;
stmt.Parameters.AddRange(function.Method.Parameters.Select(tsab.ConvertParameter));
stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType);
stmt.Body = ConvertAsBlock(function.Body);
return stmt;
}
}
}

21
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
VisitChildren (variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
VisitChildren(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement (WhileStatement whileStatement)
{
VisitChildren (whileStatement);
@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement);
}
public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
return VisitChildren(localFunctionDeclarationStatement);
}
public virtual T VisitWhileStatement (WhileStatement whileStatement)
{
return VisitChildren (whileStatement);
@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement, data);
}
public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data)
{
return VisitChildren(localFunctionDeclarationStatement, data);
}
public virtual S VisitWhileStatement (WhileStatement whileStatement, T data)
{
return VisitChildren (whileStatement, data);

3
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitUnsafeStatement(UnsafeStatement unsafeStatement);
void VisitUsingStatement(UsingStatement usingStatement);
void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
void VisitWhileStatement(WhileStatement whileStatement);
void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement);
S VisitUsingStatement(UsingStatement usingStatement);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
S VisitWhileStatement(WhileStatement whileStatement);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data);
S VisitUsingStatement(UsingStatement usingStatement, T data);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data);
S VisitWhileStatement(WhileStatement whileStatement, T data);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data);

94
ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class LocalFunctionDeclarationStatement : Statement
{
public AstNodeCollection<TypeParameterDeclaration> TypeParameters {
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
}
public AstNodeCollection<Constraint> Constraints {
get { return GetChildrenByRole(Roles.Constraint); }
}
public BlockStatement Body {
get { return GetChildByRole(Roles.Body); }
set { SetChildByRole(Roles.Body, value); }
}
public Modifiers Modifiers {
get { return EntityDeclaration.GetModifiers(this); }
set { EntityDeclaration.SetModifiers(this, value); }
}
public bool HasModifier(Modifiers mod)
{
return (Modifiers & mod) == mod;
}
public IEnumerable<CSharpModifierToken> ModifierTokens {
get { return GetChildrenByRole(EntityDeclaration.ModifierRole); }
}
public virtual string Name {
get {
return GetChildByRole(Roles.Identifier).Name;
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));
}
}
public virtual Identifier NameToken {
get { return GetChildByRole(Roles.Identifier); }
set { SetChildByRole(Roles.Identifier, value); }
}
public virtual AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitLocalFunctionDeclarationStatement(this, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement;
return o != null && MatchString(this.Name, o.Name)
&& (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers)
&& this.ReturnType.DoMatch(o.ReturnType, match)
&& this.TypeParameters.DoMatch(o.TypeParameters, match)
&& this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match)
&& this.Body.DoMatch(o.Body, match);
}
}
}

2
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
if (variable.HasInitialValue) {
// Uninitialized variables are logically initialized at the beginning of the functin
// Uninitialized variables are logically initialized at the beginning of the function
// Because it's possible that the variable has a loop-carried dependency,
// declare it outside of any loops.
while (startIndex >= 0) {

2
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -942,7 +942,7 @@ namespace ICSharpCode.Decompiler @@ -942,7 +942,7 @@ namespace ICSharpCode.Decompiler
}
}
bool localFunctions = false;
bool localFunctions = true;
/// <summary>
/// Gets/Sets whether C# 7.0 local functions should be used.

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -161,6 +161,7 @@ @@ -161,6 +161,7 @@
<Compile Include="CSharp\Syntax\Statements\GotoStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\IfElseStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LabelStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LocalFunctionDeclarationStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LockStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\ReturnStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\Statement.cs" />

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
methodTypeParameters: null);
var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var il = context.CreateILReader()
.ReadIL(method, body, genericContext, context.CancellationToken);
.ReadIL(method, body, genericContext, context.Function.Kind, context.CancellationToken);
il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true),
new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) {
CancellationToken = context.CancellationToken,

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL @@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Decodes the specified method body and returns an ILFunction.
/// </summary>
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default)
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Init(method, body, genericContext);
ReadInstructions(cancellationToken);
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);

15
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -280,6 +280,21 @@ namespace ICSharpCode.Decompiler.IL @@ -280,6 +280,21 @@ namespace ICSharpCode.Decompiler.IL
return inst;
}
/// <summary>
/// Gets the closest parent Block.
/// Returns null, if the instruction is not a descendant of a Block.
/// </summary>
public static Block FindClosestBlock(ILInstruction inst)
{
var curr = inst;
while (curr != null) {
if (curr is Block)
return (Block)curr;
curr = curr.Parent;
}
return null;
}
public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value)
{
call = null;

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

@ -99,13 +99,22 @@ namespace ICSharpCode.Decompiler.IL @@ -99,13 +99,22 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IType DelegateType;
public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1;
ILFunctionKind kind;
public ILFunctionKind Kind {
get => kind;
set {
if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction)
throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed.");
kind = value;
}
}
public readonly IType ReturnType;
public readonly IReadOnlyList<IParameter> Parameters;
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{
this.Method = method;
this.CodeSize = codeSize;
@ -114,6 +123,7 @@ namespace ICSharpCode.Decompiler.IL @@ -114,6 +123,7 @@ namespace ICSharpCode.Decompiler.IL
this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this);
this.kind = kind;
}
public ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
@ -123,10 +133,31 @@ namespace ICSharpCode.Decompiler.IL @@ -123,10 +133,31 @@ namespace ICSharpCode.Decompiler.IL
this.ReturnType = returnType;
this.Parameters = parameters;
this.Variables = new ILVariableCollection(this);
this.kind = ILFunctionKind.ExpressionTree;
}
internal override void CheckInvariant(ILPhase phase)
{
switch (kind) {
case ILFunctionKind.TopLevelFunction:
Debug.Assert(Parent == null);
Debug.Assert(DelegateType == null);
break;
case ILFunctionKind.Delegate:
Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != 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(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1);
break;
case ILFunctionKind.LocalFunction:
Debug.Assert(Parent is Block);
Debug.Assert(DelegateType == null);
break;
}
for (int i = 0; i < Variables.Count; i++) {
Debug.Assert(Variables[i].Function == this);
Debug.Assert(Variables[i].IndexInFunction == i);
@ -148,8 +179,13 @@ namespace ICSharpCode.Decompiler.IL @@ -148,8 +179,13 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
Method.WriteTo(output);
}
if (IsExpressionTree) {
output.Write(".ET");
switch (kind) {
case ILFunctionKind.ExpressionTree:
output.Write(".ET");
break;
case ILFunctionKind.LocalFunction:
output.Write(".local");
break;
}
if (DelegateType != null) {
output.Write("[");
@ -290,4 +326,24 @@ namespace ICSharpCode.Decompiler.IL @@ -290,4 +326,24 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(ok);
}
}
public enum ILFunctionKind
{
/// <summary>
/// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator.
/// </summary>
TopLevelFunction,
/// <summary>
/// ILFunction is a delegate or lambda expression.
/// </summary>
Delegate,
/// <summary>
/// ILFunction is an expression tree lambda.
/// </summary>
ExpressionTree,
/// <summary>
/// ILFunction is a C# 7.0 local function.
/// </summary>
LocalFunction
}
}

8
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -175,11 +175,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -175,11 +175,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;
if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken))
return null;
target = value.Arguments[0];
if (targetMethod.MetadataToken.IsNil)
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod))
return null;
target = value.Arguments[0];
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken);
function.DelegateType = value.Method.DeclaringType;
function.CheckInvariant(ILPhase.Normal);
// Embed the lambda into the parent function's ILAst, so that "Show steps" can show

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

@ -1,20 +1,121 @@ @@ -1,20 +1,121 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class LocalFunctionDecompiler : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;
public void Run(ILFunction function, ILTransformContext context)
{
throw new NotImplementedException();
if (!context.Settings.LocalFunctions)
return;
this.context = context;
this.decompilationContext = new SimpleTypeResolveContext(function.Method);
var localFunctions = new Dictionary<IMethod, List<Call>>();
var cancellationToken = context.CancellationToken;
// Find use-sites
foreach (var inst in function.Descendants) {
cancellationToken.ThrowIfCancellationRequested();
if (inst is Call call && IsLocalFunctionMethod(call.Method)) {
context.StepStartGroup($"LocalFunctionDecompiler {call.StartILOffset}", call);
if (!localFunctions.TryGetValue(call.Method, out var info)) {
info = new List<Call>() { call };
localFunctions.Add(call.Method, info);
} else {
info.Add(call);
}
context.StepEndGroup();
}
}
foreach (var (method, useSites) in localFunctions) {
var insertionPoint = FindInsertionPoint(useSites);
if (TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1) == null)
continue;
}
}
static ILInstruction FindInsertionPoint(List<Call> 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;
}
switch (insertionPoint) {
case BlockContainer bc:
return insertionPoint;
case Block b:
return insertionPoint;
default:
return insertionPoint;
}
}
static ILInstruction FindCommonAncestorInstruction(ILInstruction a, ILInstruction b)
{
var ancestorsOfB = new HashSet<ILInstruction>(b.Ancestors);
return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains);
}
static ILInstruction GetStatement(ILInstruction inst)
{
while (inst.Parent != null) {
if (inst.Parent is Block)
return inst;
inst = inst.Parent;
}
return inst;
}
private ILFunction TransformLocalFunction(IMethod targetMethod, Block parent, int insertionPoint)
{
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution);
if (genericContext == null)
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
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);
function.CheckInvariant(ILPhase.Normal);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
return function;
}
public static bool IsLocalFunctionMethod(IMethod method)
{
return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken);
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle)

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// partially copied from CSharpDecompiler
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, ILFunctionKind.TopLevelFunction, context.CancellationToken);
var transformContext = new ILTransformContext(context, proxyFunction);
proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext);
if (!(proxyFunction.Body is BlockContainer blockContainer))

7
ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call)
{
lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
lambda.DelegateType = call.Method.ReturnType;
}
@ -341,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -341,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return (null, SpecialType.UnknownType);
case ILFunction function:
if (function.IsExpressionTree) {
if (function.Kind == ILFunctionKind.ExpressionTree) {
function.DelegateType = UnwrapExpressionTree(function.DelegateType);
}
return (function, function.DelegateType);
@ -372,6 +373,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -372,6 +373,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt
&& pt.FullName == "System.Linq.Expressions.Expression"
&& pt.TypeArguments.Count == 1;
IType UnwrapExpressionTree(IType delegateType)
{
if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) {

2
ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs

@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite @@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite
// decompile method and optimize the switch
var ilReader = new ILReader(ctx.TypeSystem.MainModule);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ctx.CancellationToken);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken);
var context = new ILTransformContext(function, ctx.TypeSystem, null) {
CancellationToken = ctx.CancellationToken

2
ILSpy/Languages/ILAstLanguage.cs

@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy @@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy
var reader = new ILReader(typeSystem.MainModule);
reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: options.CancellationToken);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken);
var namespaces = new HashSet<string>();
var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken };
ILTransformContext context = decompiler.CreateILTransformContext(il);

Loading…
Cancel
Save