diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3a79c3504..ea43abd63 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -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 // 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 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 method.DeclaringType, new IParameterizedMember[] { method } ) - }, EmptyList.Instance + }, method.TypeArguments ); } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0c9c6f28f..d6f012783 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -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 internal ILFunction ResolveLocalFunction(IMethod method) { Debug.Assert(method.IsLocalFunction); - method = method.ReducedFrom; + method = (IMethod)method.ReducedFrom.MemberDefinition; foreach (var parent in currentFunction.Ancestors.OfType()) { var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method)); if (definition != null) { diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 432f706ad..4a6389adb 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -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; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index a4a77358d..d665f41fe 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -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 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; diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 2bea3eb93..0fdb11d5d 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -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.Empty; diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 5adbdf4e0..f2af0b636 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -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 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() { inst }, @@ -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 { 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;