Browse Source

Add support for generic local function

pull/1797/head
SilverFox 6 years ago
parent
commit
f039705704
  1. 45
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 11
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  5. 1
      ICSharpCode.Decompiler/IL/ILReader.cs
  6. 15
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

45
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -195,8 +195,21 @@ namespace ICSharpCode.Decompiler.CSharp
if (callOpCode == OpCode.NewObj) { if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target target = default(TranslatedExpression); // no target
} else if (method.IsLocalFunction && localFunction != null) { } else if (method.IsLocalFunction && localFunction != null) {
target = new IdentifierExpression(localFunction.Name) var ide = new IdentifierExpression(localFunction.Name);
.WithoutILInstruction() if (method.TypeArguments.Count > 0) {
var parentMethod = ((ILFunction)localFunction.Parent).Method;
int skipCount = parentMethod.DeclaringType.TypeParameterCount + parentMethod.TypeParameters.Count - localFunction.Method.DeclaringType.TypeParameterCount;
#if DEBUG
Debug.Assert(skipCount >= 0);
if (skipCount > 0) {
var currentMethod = expressionBuilder.currentFunction.Method;
currentMethod = currentMethod.ReducedFrom ?? currentMethod;
Debug.Assert(currentMethod.DeclaringType.TypeParameters.Concat(currentMethod.TypeParameters).Take(skipCount).SequenceEqual(method.DeclaringType.TypeArguments.Concat(method.TypeArguments).Take(skipCount)));
}
#endif
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
}
target = ide.WithoutILInstruction()
.WithRR(ToMethodGroup(method, localFunction)); .WithRR(ToMethodGroup(method, localFunction));
} else { } else {
target = expressionBuilder.TranslateTarget( target = expressionBuilder.TranslateTarget(
@ -1304,10 +1317,14 @@ namespace ICSharpCode.Decompiler.CSharp
// 2. add type arguments (represented as bit 1) // 2. add type arguments (represented as bit 1)
// 3. cast target (represented as bit 2) // 3. cast target (represented as bit 2)
int step; int step;
ILFunction localFunction = null;
if (method.IsLocalFunction) { if (method.IsLocalFunction) {
step = 0; localFunction = expressionBuilder.ResolveLocalFunction(method);
Debug.Assert(localFunction != null);
}
if (localFunction != null) {
step = 2;
requireTarget = false; requireTarget = false;
var localFunction = expressionBuilder.ResolveLocalFunction(method);
result = ToMethodGroup(method, localFunction); result = ToMethodGroup(method, localFunction);
target = default; target = default;
targetType = default; targetType = default;
@ -1389,8 +1406,22 @@ namespace ICSharpCode.Decompiler.CSharp
targetExpression = mre.WithRR(result); targetExpression = mre.WithRR(result);
} else { } else {
var ide = new IdentifierExpression(methodName); var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) if ((step & 2) != 0) {
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); int skipCount = 0;
if (localFunction != null && method.TypeArguments.Count > 0) {
var parentMethod = ((ILFunction)localFunction.Parent).Method;
skipCount = parentMethod.DeclaringType.TypeParameterCount + parentMethod.TypeParameters.Count - localFunction.Method.DeclaringType.TypeParameterCount;
#if DEBUG
Debug.Assert(skipCount >= 0);
if (skipCount > 0) {
var currentMethod = expressionBuilder.currentFunction.Method;
currentMethod = currentMethod.ReducedFrom ?? currentMethod;
Debug.Assert(currentMethod.DeclaringType.TypeParameters.Concat(currentMethod.TypeParameters).Take(skipCount).SequenceEqual(method.DeclaringType.TypeArguments.Concat(method.TypeArguments).Take(skipCount)));
}
#endif
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
}
targetExpression = ide.WithRR(result); targetExpression = ide.WithRR(result);
} }
return targetExpression; return targetExpression;
@ -1448,7 +1479,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType, method.DeclaringType,
new IParameterizedMember[] { method } new IParameterizedMember[] { method }
) )
}, EmptyList<IType>.Instance }, method.TypeArguments
); );
} }

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly ILFunction currentFunction; internal readonly ILFunction currentFunction;
internal readonly ICompilation compilation; internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver; internal readonly CSharpResolver resolver;
readonly TypeSystemAstBuilder astBuilder; internal readonly TypeSystemAstBuilder astBuilder;
internal readonly TypeInference typeInference; internal readonly TypeInference typeInference;
internal readonly DecompilerSettings settings; internal readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
@ -217,7 +217,7 @@ namespace ICSharpCode.Decompiler.CSharp
internal ILFunction ResolveLocalFunction(IMethod method) internal ILFunction ResolveLocalFunction(IMethod method)
{ {
Debug.Assert(method.IsLocalFunction); Debug.Assert(method.IsLocalFunction);
method = method.ReducedFrom; method = (IMethod)method.ReducedFrom.MemberDefinition;
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) { foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method)); var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method));
if (definition != null) { if (definition != null) {

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -987,6 +987,17 @@ namespace ICSharpCode.Decompiler.CSharp
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
if (function.Method.TypeParameters.Count > 0) {
var parentMethod = ((ILFunction)function.Parent).Method;
int skipCount = parentMethod.DeclaringType.TypeParameterCount + parentMethod.TypeParameters.Count - function.Method.DeclaringType.TypeParameterCount;
var astBuilder = exprBuilder.astBuilder;
if (astBuilder.ShowTypeParameters) {
stmt.TypeParameters.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameter(t)));
if (astBuilder.ShowTypeParameterConstraints) {
stmt.Constraints.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameterConstraint(t)).Where(c => c != null));
}
}
}
if (function.IsAsync) { if (function.IsAsync) {
stmt.Modifiers |= Modifiers.Async; stmt.Modifiers |= Modifiers.Async;
} }

4
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1793,7 +1793,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
#endregion #endregion
#region Convert Type Parameter #region Convert Type Parameter
TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp) internal TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp)
{ {
TypeParameterDeclaration decl = new TypeParameterDeclaration(); TypeParameterDeclaration decl = new TypeParameterDeclaration();
decl.Variance = tp.Variance; decl.Variance = tp.Variance;
@ -1803,7 +1803,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return decl; return decl;
} }
Constraint ConvertTypeParameterConstraint(ITypeParameter tp) internal Constraint ConvertTypeParameterConstraint(ITypeParameter tp)
{ {
if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) { if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) {
return null; return null;

1
ICSharpCode.Decompiler/IL/ILReader.cs

@ -100,7 +100,6 @@ namespace ICSharpCode.Decompiler.IL
this.method = this.method.Specialize(genericContext.ToSubstitution()); this.method = this.method.Specialize(genericContext.ToSubstitution());
} }
this.genericContext = genericContext; this.genericContext = genericContext;
var methodDefinition = metadata.GetMethodDefinition(methodDefinitionHandle);
this.body = body; this.body = body;
this.reader = body.GetILReader(); this.reader = body.GetILReader();
this.currentStack = ImmutableStack<ILVariable>.Empty; this.currentStack = ImmutableStack<ILVariable>.Empty;

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

@ -91,10 +91,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var useSite in info.UseSites) { foreach (var useSite in info.UseSites) {
context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite);
DetermineCaptureAndDeclarationScope(localFunction, useSite);
if (useSite.OpCode == OpCode.NewObj) { if (useSite.OpCode == OpCode.NewObj) {
TransformToLocalFunctionReference(localFunction, useSite); TransformToLocalFunctionReference(localFunction, useSite);
} else { } else {
DetermineCaptureAndDeclarationScope(localFunction, useSite);
TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
} }
@ -129,6 +129,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void HandleUseSite(IMethod targetMethod, CallInstruction inst) void HandleUseSite(IMethod targetMethod, CallInstruction inst)
{ {
if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) { if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) {
targetMethod = (IMethod)targetMethod.MemberDefinition;
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst); context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() { info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { inst }, UseSites = new List<CallInstruction>() { inst },
@ -151,12 +152,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody()) if (!methodDefinition.HasBody())
return null; return null;
var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution);
if (genericContext == null)
return null;
var ilReader = context.CreateILReader(); var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, default, 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.
rootFunction.LocalFunctions.Add(function); rootFunction.LocalFunctions.Add(function);
@ -219,12 +217,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0])); useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0]));
var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1]; var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1];
var replacement = new LdFtn(function.ReducedMethod).WithILRange((ILInstruction)fnptr); LocalFunctionMethod reducedMethod = function.ReducedMethod;
if (reducedMethod.TypeParameters.Count > 0)
reducedMethod = new LocalFunctionMethod(fnptr.Method, reducedMethod.NumberOfCompilerGeneratedParameters);
var replacement = new LdFtn(reducedMethod).WithILRange((ILInstruction)fnptr);
useSite.Arguments[1].ReplaceWith(replacement); useSite.Arguments[1].ReplaceWith(replacement);
} }
void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite) void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite)
{ {
if (reducedMethod.TypeParameters.Count > 0)
reducedMethod = new LocalFunctionMethod(useSite.Method, reducedMethod.NumberOfCompilerGeneratedParameters);
bool wasInstanceCall = !useSite.Method.IsStatic; bool wasInstanceCall = !useSite.Method.IsStatic;
var replacement = new Call(reducedMethod); var replacement = new Call(reducedMethod);
int firstArgumentIndex = wasInstanceCall ? 1 : 0; int firstArgumentIndex = wasInstanceCall ? 1 : 0;

Loading…
Cancel
Save