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

4
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -987,6 +987,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -987,6 +987,17 @@ namespace ICSharpCode.Decompiler.CSharp
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType);
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) {
stmt.Modifiers |= Modifiers.Async;
}

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

@ -1793,7 +1793,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1793,7 +1793,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
#endregion
#region Convert Type Parameter
TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp)
internal TypeParameterDeclaration ConvertTypeParameter(ITypeParameter tp)
{
TypeParameterDeclaration decl = new TypeParameterDeclaration();
decl.Variance = tp.Variance;
@ -1803,7 +1803,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1803,7 +1803,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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)) {
return null;

1
ICSharpCode.Decompiler/IL/ILReader.cs

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

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

@ -91,10 +91,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -91,10 +91,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var useSite in info.UseSites) {
context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite);
DetermineCaptureAndDeclarationScope(localFunction, useSite);
if (useSite.OpCode == OpCode.NewObj) {
TransformToLocalFunctionReference(localFunction, useSite);
} else {
DetermineCaptureAndDeclarationScope(localFunction, useSite);
TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
}
@ -129,6 +129,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -129,6 +129,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void HandleUseSite(IMethod targetMethod, CallInstruction inst)
{
if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) {
targetMethod = (IMethod)targetMethod.MemberDefinition;
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { inst },
@ -151,12 +152,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -151,12 +152,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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);
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
// how the local function body is being transformed.
rootFunction.LocalFunctions.Add(function);
@ -219,12 +217,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -219,12 +217,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0]));
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);
}
void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite)
{
if (reducedMethod.TypeParameters.Count > 0)
reducedMethod = new LocalFunctionMethod(useSite.Method, reducedMethod.NumberOfCompilerGeneratedParameters);
bool wasInstanceCall = !useSite.Method.IsStatic;
var replacement = new Call(reducedMethod);
int firstArgumentIndex = wasInstanceCall ? 1 : 0;

Loading…
Cancel
Save