diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 746925b6e..39ffaa55c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -190,7 +190,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return await Nested(1) + await Nested(2); +#if CS80 + static async Task Nested(int i) +#else async Task Nested(int i) +#endif { await Task.Delay(i); return i; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs index 966b26cb5..01b70b6c5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs @@ -118,7 +118,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return await Nested(1) + await Nested(2); +#if CS80 + static async ValueTask Nested(int i) +#else async ValueTask Nested(int i) +#endif { await Task.Delay(i); return i; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 86ec9d1cc..bb522de53 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -187,7 +187,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Noop("M3", this.M3); Noop("M3", M3); +#if CS80 + static void M3() +#else void M3() +#endif { } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 91c58bacd..013111e78 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -23,12 +23,297 @@ namespace LocalFunctions { internal class LocalFunctions { + public class Generic where T1 : struct, ICloneable, IConvertible + { + public int MixedLocalFunction() where T2 : ICloneable, IConvertible + { + T2 t2 = default(T2); + object z = this; + for (int j = 0; j < 10; j++) { + int i = 0; + i += NonStaticMethod6(); + int NonStaticMethod6() + { + t2 = default(T2); + int l = 0; + return NonStaticMethod6_1() + NonStaticMethod6_1() + z.GetHashCode(); + int NonStaticMethod6_1() + { + return i + l + NonStaticMethod6() + StaticMethod1(); + } + } + } + return MixedLocalFunction() + MixedLocalFunction() + StaticMethod1() + StaticMethod1() + NonStaticMethod3() + StaticMethod4(null) + StaticMethod5(); + int NonStaticMethod3() + { + return GetHashCode(); + } +#if CS80 + static int StaticMethod1() where T3 : struct +#else + int StaticMethod1() where T3 : struct +#endif + { + return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticMethod1() + StaticMethod1_1() + StaticMethod2_RepeatT2(); + } +#if CS80 + static int StaticMethod1_1() where T3 : struct where T4 : Enum +#else + int StaticMethod1_1() where T3 : struct where T4 : Enum +#endif + { + return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + typeof(T4).Name.Length + StaticMethod1() + StaticMethod1_1(); + } +#pragma warning disable CS8387 +#if CS80 + static int StaticMethod2_RepeatT2() where T2 : IConvertible where T3 : struct where T4 : Enum +#else + int StaticMethod2_RepeatT2() where T2 : IConvertible where T3 : struct where T4 : Enum +#endif +#pragma warning restore CS8387 + { + return typeof(T2).Name.Length; + } +#if CS80 + static int StaticMethod4(T dd) +#else + int StaticMethod4(T dd) +#endif + { + return 0; + } +#if CS80 + static int StaticMethod5() +#else + int StaticMethod5() +#endif + { + int k = 0; + return k + NonStaticMethod5_1(); + int NonStaticMethod5_1() + { + return k; + } + } + } + + public int MixedLocalFunction2Delegate() where T2 : ICloneable, IConvertible + { + T2 t2 = default(T2); + object z = this; + for (int j = 0; j < 10; j++) { + int i = 0; + i += StaticInvokeAsFunc(NonStaticMethod6); + int NonStaticMethod6() + { + t2 = default(T2); + int l = 0; + return StaticInvokeAsFunc(NonStaticMethod6_1) + StaticInvokeAsFunc(NonStaticMethod6_1) + z.GetHashCode(); + int NonStaticMethod6_1() + { + return i + l + StaticInvokeAsFunc(NonStaticMethod6) + StaticInvokeAsFunc(StaticMethod1); + } + } + } + return StaticInvokeAsFunc(MixedLocalFunction2Delegate) + StaticInvokeAsFunc(MixedLocalFunction2Delegate) + StaticInvokeAsFunc(StaticMethod1) + StaticInvokeAsFunc(StaticMethod1) + StaticInvokeAsFunc(NonStaticMethod3) + StaticInvokeAsFunc(StaticMethod5) + new Func(StaticMethod4)(null) + StaticInvokeAsFunc2(StaticMethod4) + new Func, int>(StaticInvokeAsFunc2)(StaticMethod4); + int NonStaticMethod3() + { + return GetHashCode(); + } +#if CS80 + static int StaticInvokeAsFunc(Func func) +#else + int StaticInvokeAsFunc(Func func) +#endif + { + return func(); + } +#if CS80 + static int StaticInvokeAsFunc2(Func func) +#else + int StaticInvokeAsFunc2(Func func) +#endif + { + return func(default(T)); + } +#if CS80 + static int StaticMethod1() where T3 : struct +#else + int StaticMethod1() where T3 : struct +#endif + { + return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticInvokeAsFunc(StaticMethod1) + StaticInvokeAsFunc(StaticMethod1_1) + StaticInvokeAsFunc(StaticMethod2_RepeatT2); + } +#if CS80 + static int StaticMethod1_1() where T3 : struct where T4 : Enum +#else + int StaticMethod1_1() where T3 : struct where T4 : Enum +#endif + { + return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + typeof(T4).Name.Length + StaticInvokeAsFunc(StaticMethod1) + StaticInvokeAsFunc(StaticMethod1_1); + } +#pragma warning disable CS8387 +#if CS80 + static int StaticMethod2_RepeatT2() where T2 : IConvertible where T3 : struct where T4 : Enum +#else + int StaticMethod2_RepeatT2() where T2 : IConvertible where T3 : struct where T4 : Enum +#endif +#pragma warning restore CS8387 + { + return typeof(T2).Name.Length; + } +#if CS80 + static int StaticMethod4(T dd) +#else + int StaticMethod4(T dd) +#endif + { + return 0; + } +#if CS80 + static int StaticMethod5() +#else + int StaticMethod5() +#endif + { + int k = 0; + return k + StaticInvokeAsFunc(NonStaticMethod5_1); + int NonStaticMethod5_1() + { + return k; + } + } + } + + public static void Test_CaptureT() + { + T2 t2 = default(T2); + Method1(); + void Method1() + { + t2 = default(T2); + T2 t2x = t2; + T3 t3 = default(T3); + Method1_1(); + void Method1_1() + { + t2 = default(T2); + t2x = t2; + t3 = default(T3); + } + } + } + + public void TestGenericArgs() where T2 : List + { + ZZ(null); + ZZ2(null); +#if CS80 + static void Nop(T data) +#else + void Nop(T data) +#endif + { + } +#if CS80 + static void ZZ(T3 t3) where T3 : T2 +#else + void ZZ(T3 t3) where T3 : T2 +#endif + { + Nop>(t3); + ZZ1(t3); + ZZ3(); + void ZZ3() + { + Nop>(t3); + } + } +#if CS80 + static void ZZ1(T3 t3) +#else + void ZZ1(T3 t3) +#endif + { + Nop>((List)(object)t3); + } +#if CS80 + static void ZZ2(T3 t3) +#else + void ZZ2(T3 t3) +#endif + { + Nop>((List)(object)t3); + } + } + +#if false + public void GenericArgsWithAnonymousType() + { + Method(); +#if CS80 + static void Method() +#else + void Method() +#endif + { + int i = 0; + var obj2 = new { + A = 1 + }; + Method2(obj2); + Method3(obj2); + void Method2(T3 obj1) + { + //keep nested + i = 0; + } +#if CS80 + static void Method3(T3 obj1) +#else + void Method3(T3 obj1) +#endif + { + } + } + } +#if CS80 + public void NameConflict() + { + int i = 0; + Method(); + void Method() + { + Method(); + void Method() + { + Method(); + i = 0; + void Method() + { + i = 0; + Method(); + static void Method() + { + } + } + } + } + } +#endif +#endif + } + private int field; private Lazy nonCapturinglocalFunctionInLambda = new Lazy(delegate { return CreateValue(); +#if CS80 + static object CreateValue() +#else object CreateValue() +#endif { return null; } @@ -69,7 +354,11 @@ namespace LocalFunctions LocalWrite("Hello " + i); } +#if CS80 + static void LocalWrite(string s) +#else void LocalWrite(string s) +#endif { Console.WriteLine(s); } @@ -105,7 +394,11 @@ namespace LocalFunctions LocalWrite("Hello " + i); } +#if CS80 + static void LocalWrite(string s) +#else void LocalWrite(string s) +#endif { Console.WriteLine(s); } @@ -166,7 +459,11 @@ namespace LocalFunctions Test(5); LocalFunctions.Test(2); +#if CS80 + static void Test(int x) +#else void Test(int x) +#endif { Console.WriteLine("x: {0}", x); } @@ -183,7 +480,11 @@ namespace LocalFunctions Name(); action(); +#if CS80 + static void Name() +#else void Name() +#endif { } @@ -194,12 +495,20 @@ namespace LocalFunctions Use(Get(1), Get(2), Get(3)); Use(Get(1), c: Get(2), b: Get(3)); +#if CS80 + static int Get(int i) +#else int Get(int i) +#endif { return i; } +#if CS80 + static void Use(int a, int b, int c) +#else void Use(int a, int b, int c) +#endif { Console.WriteLine(a + b + c); } @@ -232,7 +541,11 @@ namespace LocalFunctions { return FibHelper(i); +#if CS80 + static int FibHelper(int n) +#else int FibHelper(int n) +#endif { if (n <= 0) { return 0; @@ -245,7 +558,11 @@ namespace LocalFunctions { return B(4) + C(3); +#if CS80 + static int A(int i) +#else int A(int i) +#endif { if (i > 0) { return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1); @@ -253,7 +570,11 @@ namespace LocalFunctions return 1; } +#if CS80 + static int B(int i) +#else int B(int i) +#endif { if (i > 0) { return 3 * A(i - 1) + B(i - 1); @@ -261,7 +582,11 @@ namespace LocalFunctions return 1; } +#if CS80 + static int C(int i) +#else int C(int i) +#endif { if (i > 0) { return 2 * A(i - 1) + C(i - 1); @@ -332,5 +657,67 @@ namespace LocalFunctions // } // } //} + + public void NestedCapture1() + { + Method1(null); + +#if CS80 + static Action Method1(Action action) +#else + Action Method1(Action action) +#endif + { + return Method1_1; + + void Method1_1(object containerBuilder) + { + Method1_2(containerBuilder); + } + + void Method1_2(object containerBuilder) + { + action(containerBuilder); + } + } + } + + public int NestedCapture2() + { + return Method(); +#if CS80 + static int Method() +#else + int Method() +#endif + { + int t0 = 0; + return ZZZ_0(); + int ZZZ_0() + { + t0 = 0; + int t2 = t0; + return new Func(ZZZ_0_0)(); + int ZZZ_0_0() + { + t0 = 0; + t2 = 0; + return ZZZ_1(); + } + } + int ZZZ_1() + { + t0 = 0; + int t = t0; + return new Func(ZZZ_1_0)(); + int ZZZ_1_0() + { + t0 = 0; + t = 0; + return 0; + } + } + } + } } } diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3a79c3504..267a757fe 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -194,9 +194,13 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target - } else if (method.IsLocalFunction && localFunction != null) { - target = new IdentifierExpression(localFunction.Name) - .WithoutILInstruction() + } else if (localFunction != null) { + var ide = new IdentifierExpression(localFunction.Name); + if (method.TypeArguments.Count > 0) { + int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; + ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType)); + } + target = ide.WithoutILInstruction() .WithRR(ToMethodGroup(method, localFunction)); } else { target = expressionBuilder.TranslateTarget( @@ -225,7 +229,7 @@ namespace ICSharpCode.Decompiler.CSharp var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, firstParamIndex, callArguments, argumentToParameterMap); - if (method.IsLocalFunction) { + if (localFunction != null) { return new InvocationExpression(target, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm)); @@ -1304,10 +1308,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; @@ -1351,11 +1359,12 @@ namespace ICSharpCode.Decompiler.CSharp memberDeclaringType: method.DeclaringType); requireTarget = expressionBuilder.HidesVariableWithName(method.Name) || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); - + step = requireTarget ? 1 : 0; var savedTarget = target; - for (step = requireTarget ? 1 : 0; step < 7; step++) { + for (; step < 7; step++) { ResolveResult targetResolveResult; - if (!method.IsLocalFunction && (step & 1) != 0) { + //TODO: why there is an check for IsLocalFunction here, it should be unreachable in old code + if (localFunction == null && (step & 1) != 0) { targetResolveResult = savedTarget.ResolveResult; target = savedTarget; } else { @@ -1378,7 +1387,7 @@ namespace ICSharpCode.Decompiler.CSharp break; } } - requireTarget = !method.IsLocalFunction && (step & 1) != 0; + requireTarget = localFunction == null && (step & 1) != 0; ExpressionWithResolveResult targetExpression; Debug.Assert(result != null); if (requireTarget) { @@ -1389,8 +1398,13 @@ 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) { + skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; + } + ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType)); + } targetExpression = ide.WithRR(result); } return targetExpression; @@ -1448,7 +1462,7 @@ namespace ICSharpCode.Decompiler.CSharp method.DeclaringType, new IParameterizedMember[] { method } ) - }, EmptyList.Instance + }, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray() ); } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 75e6841ef..08506d1ee 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,9 +217,9 @@ namespace ICSharpCode.Decompiler.CSharp internal ILFunction ResolveLocalFunction(IMethod method) { Debug.Assert(method.IsLocalFunction); - method = method.ReducedFrom; + method = (IMethod)((IMethod)method.MemberDefinition).ReducedFrom.MemberDefinition; foreach (var parent in currentFunction.Ancestors.OfType()) { - var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method)); + var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.MemberDefinition.Equals(method)); if (definition != null) { return definition; } diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 659e5490a..656ba8394 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -992,9 +992,22 @@ 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 astBuilder = exprBuilder.astBuilder; + if (astBuilder.ShowTypeParameters) { + int skipCount = function.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; + 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; } + if (settings.StaticLocalFunctions && function.ReducedMethod.IsStaticLocalFunction) { + stmt.Modifiers |= Modifiers.Static; + } stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); return stmt; } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index e948ce27c..ddb950d0a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1807,7 +1807,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; @@ -1817,7 +1817,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/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 2b177db3f..95eb3fcb7 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -111,12 +111,13 @@ namespace ICSharpCode.Decompiler readOnlyMethods = false; asyncUsingAndForEachStatement = false; asyncEnumerator = false; + staticLocalFunctions = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement) + if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions) return CSharp.LanguageVersion.CSharp8_0; if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement) return CSharp.LanguageVersion.CSharp7_3; @@ -1082,6 +1083,23 @@ namespace ICSharpCode.Decompiler } } + bool staticLocalFunctions = true; + + /// + /// Gets/Sets whether C# 8.0 static local functions should be transformed. + /// + [Category("C# 8.0 / VS 2019")] + [Description("DecompilerSettings.IntroduceStaticLocalFunctions")] + public bool StaticLocalFunctions { + get { return staticLocalFunctions; } + set { + if (staticLocalFunctions != value) { + staticLocalFunctions = value; + OnPropertyChanged(); + } + } + } + bool nullableReferenceTypes = true; /// 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/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 4d723a0b6..07da7dd7b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - internal static GenericContext? GenericContextFromTypeArguments(TypeParameterSubstitution subst) + static GenericContext? GenericContextFromTypeArguments(TypeParameterSubstitution subst) { var classTypeParameters = new List(); var methodTypeParameters = new List(); diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index e79d30e86..0b2e882e7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -53,18 +53,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// /// local functions can either be used in method calls, i.e., call and callvirt instructions, - /// or can be used as part of the "delegate construction" pattern, i.e., newobj Delegate(<target-expression>, ldftn <method>). + /// or can be used as part of the "delegate construction" pattern, i.e., + /// newobj Delegate(<target-expression>, ldftn <method>). /// - /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions. - /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function. - /// Then all use-sites of the local-function are transformed to a call to the LocalFunctionMethod or a ldftn of the LocalFunctionMethod. + /// As local functions can be declared practically anywhere, we have to take a look at + /// all use-sites and infer the declaration location from that. Use-sites can be call, + /// callvirt and ldftn instructions. + /// After all use-sites are collected we construct the ILAst of the local function + /// and add it to the parent function. + /// Then all use-sites of the local-function are transformed to a call to the + /// LocalFunctionMethod or a ldftn of the LocalFunctionMethod. /// In a next step we handle all nested local functions. - /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope. + /// After all local functions are transformed, we move all local functions that capture + /// any variables to their respective declaration scope. /// public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LocalFunctions) return; + // Disable the transform if we are decompiling a display-class or local function method: + // This happens if a local function or display class is selected in the ILSpy tree view. + if (IsLocalFunctionMethod(function.Method, context) || IsLocalFunctionDisplayClass(function.Method.ParentModule.PEFile, (TypeDefinitionHandle)function.Method.DeclaringTypeDefinition.MetadataToken, context)) + return; this.context = context; this.resolveContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary(); @@ -78,25 +88,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms continue; } - var firstUseSite = info.UseSites[0]; context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition); try { var localFunction = info.Definition; if (!localFunction.Method.IsStatic) { - var target = firstUseSite.Arguments[0]; - context.Step($"Replace 'this' with {target}", localFunction); var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis); + var target = info.UseSites.Where(us => us.Arguments[0].MatchLdLoc(out _)).FirstOrDefault()?.Arguments[0]; + if (target == null) { + target = info.UseSites[0].Arguments[0]; + if (target.MatchLdFld(out var target1, out var field) && thisVar.Type.Equals(field.Type) && field.Type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition())) { + var variable = function.Descendants.OfType().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, null, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault(); + if (variable != null) { + target = new LdLoc(variable); + HandleArgument(localFunction, 1, 0, target); + } + } + } + context.Step($"Replace 'this' with {target}", localFunction); localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); } foreach (var useSite in info.UseSites) { - context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); - if (useSite.OpCode == OpCode.NewObj) { - TransformToLocalFunctionReference(localFunction, useSite); - } else { - DetermineCaptureAndDeclarationScope(localFunction, useSite); - TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); - } + DetermineCaptureAndDeclarationScope(localFunction, useSite); if (function.Method.IsConstructor && localFunction.DeclarationScope == null) { localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite); @@ -109,12 +122,68 @@ namespace ICSharpCode.Decompiler.IL.Transforms function.LocalFunctions.Remove(localFunction); declaringFunction.LocalFunctions.Add(localFunction); } + + if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters) { + Debug.Assert(false); + function.Warnings.Add($"Could not decode local function '{info.Method}'"); + if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) { + declaringFunction.LocalFunctions.Remove(localFunction); + } + continue; + } + + foreach (var useSite in info.UseSites) { + context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); + if (useSite.OpCode == OpCode.NewObj) { + TransformToLocalFunctionReference(localFunction, useSite); + } else { + TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); + } + } } finally { context.StepEndGroup(); } } } + bool TryValidateSkipCount(LocalFunctionInfo info, out int skipCount) + { + skipCount = 0; + var localFunction = info.Definition; + if (localFunction.Method.TypeParameters.Count == 0) + return true; + var parentMethod = ((ILFunction)localFunction.Parent).Method; + var method = localFunction.Method; + skipCount = parentMethod.DeclaringType.TypeParameterCount - method.DeclaringType.TypeParameterCount; + + if (skipCount > 0) + return false; + skipCount += parentMethod.TypeParameters.Count; + if (skipCount < 0 || skipCount > method.TypeArguments.Count) + return false; + + if (skipCount > 0) { +#if DEBUG + foreach (var useSite in info.UseSites) { + var callerMethod = useSite.Ancestors.OfType().First().Method; + callerMethod = callerMethod.ReducedFrom ?? callerMethod; + IMethod method0; + if (useSite.OpCode == OpCode.NewObj) { + method0 = ((LdFtn)useSite.Arguments[1]).Method; + } else { + method0 = useSite.Method; + } + var totalSkipCount = skipCount + method0.DeclaringType.TypeParameterCount; + var methodSkippedArgs = method0.DeclaringType.TypeArguments.Concat(method0.TypeArguments).Take(totalSkipCount); + Debug.Assert(methodSkippedArgs.SequenceEqual(callerMethod.DeclaringType.TypeArguments.Concat(callerMethod.TypeArguments).Take(totalSkipCount))); + Debug.Assert(methodSkippedArgs.All(p => p.Kind == TypeKind.TypeParameter)); + Debug.Assert(methodSkippedArgs.Select(p => p.Name).SequenceEqual(method0.DeclaringType.TypeParameters.Concat(method0.TypeParameters).Take(totalSkipCount).Select(p => p.Name))); + } +#endif + } + return true; + } + void FindUseSites(ILFunction function, ILTransformContext context, Dictionary localFunctions) { foreach (var inst in function.Body.Descendants) { @@ -132,9 +201,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst); info = new LocalFunctionInfo() { UseSites = new List() { inst }, - Method = targetMethod, - Definition = ReadLocalFunctionDefinition(context.Function, targetMethod) + Method = (IMethod)targetMethod.MemberDefinition, }; + var rootFunction = context.Function; + int skipCount = GetSkipCount(rootFunction, targetMethod); + info.Definition = ReadLocalFunctionDefinition(rootFunction, targetMethod, skipCount); localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info); if (info.Definition != null) { FindUseSites(info.Definition, context, localFunctions); @@ -146,17 +217,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod) + ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod, int skipCount) { 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 genericContext = GenericContextFromTypeArguments(targetMethod, skipCount); + if (genericContext == null) + return null; + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.GetValueOrDefault(), 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); @@ -165,10 +236,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); function.DeclarationScope = null; - function.ReducedMethod = ReduceToLocalFunction(targetMethod); + function.ReducedMethod = ReduceToLocalFunction(function.Method, skipCount); return function; } + int GetSkipCount(ILFunction rootFunction, IMethod targetMethod) + { + targetMethod = (IMethod)targetMethod.MemberDefinition; + var skipCount = rootFunction.Method.DeclaringType.TypeParameters.Count + rootFunction.Method.TypeParameters.Count - targetMethod.DeclaringType.TypeParameters.Count; + if (skipCount < 0) { + skipCount = 0; + } + if (targetMethod.TypeParameters.Count > 0) { + var lastParams = targetMethod.Parameters.Where(p => IsClosureParameter(p, this.resolveContext)).SelectMany(p => UnwrapByRef(p.Type).TypeArguments) + .Select(pt => (int?)targetMethod.TypeParameters.IndexOf(pt)).DefaultIfEmpty().Max(); + if (lastParams != null && lastParams.GetValueOrDefault() + 1 > skipCount) + skipCount = lastParams.GetValueOrDefault() + 1; + } + return skipCount; + } + + static TypeSystem.GenericContext? GenericContextFromTypeArguments(IMethod targetMethod, int skipCount) + { + if (skipCount < 0 || skipCount > targetMethod.TypeParameters.Count) { + Debug.Assert(false); + return null; + } + int total = targetMethod.DeclaringType.TypeParameters.Count + skipCount; + if (total == 0) + return default(TypeSystem.GenericContext); + + var classTypeParameters = new List(targetMethod.DeclaringType.TypeParameters); + var methodTypeParameters = new List(targetMethod.TypeParameters); + var skippedTypeArguments = targetMethod.DeclaringType.TypeArguments.Concat(targetMethod.TypeArguments).Take(total); + int idx = 0; + foreach (var skippedTA in skippedTypeArguments) { + int curIdx; + List curParameters; + IReadOnlyList curArgs; + if (idx < classTypeParameters.Count) { + curIdx = idx; + curParameters = classTypeParameters; + curArgs = targetMethod.DeclaringType.TypeArguments; + } else { + curIdx = idx - classTypeParameters.Count; + curParameters = methodTypeParameters; + curArgs = targetMethod.TypeArguments; + } + if (curArgs[curIdx].Kind != TypeKind.TypeParameter) + break; + curParameters[curIdx] = (ITypeParameter)skippedTA; + idx++; + } + if (idx != total) { + Debug.Assert(false); + return null; + } + + return new TypeSystem.GenericContext(classTypeParameters, methodTypeParameters); + } + static T FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) where T : ILInstruction { @@ -204,7 +331,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return inst; } - LocalFunctionMethod ReduceToLocalFunction(IMethod method) + LocalFunctionMethod ReduceToLocalFunction(IMethod method, int skipCount) { int parametersToRemove = 0; for (int i = method.Parameters.Count - 1; i >= 0; i--) { @@ -212,21 +339,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; parametersToRemove++; } - return new LocalFunctionMethod(method, parametersToRemove); + return new LocalFunctionMethod(method, parametersToRemove, skipCount); } static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) { 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); + var specializeMethod = function.ReducedMethod.Specialize(fnptr.Method.Substitution); + var replacement = new LdFtn(specializeMethod).WithILRange((ILInstruction)fnptr); useSite.Arguments[1].ReplaceWith(replacement); } void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite) { + var specializeMethod = reducedMethod.Specialize(useSite.Method.Substitution); bool wasInstanceCall = !useSite.Method.IsStatic; - var replacement = new Call(reducedMethod); + var replacement = new Call(specializeMethod); int firstArgumentIndex = wasInstanceCall ? 1 : 0; int argumentCount = useSite.Arguments.Count; int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); @@ -255,42 +384,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms { int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) { - if (!HandleArgument(i, useSite.Arguments[i])) + if (!HandleArgument(function, firstArgumentIndex, i, useSite.Arguments[i])) break; } if (firstArgumentIndex > 0) { - HandleArgument(0, useSite.Arguments[0]); + HandleArgument(function, firstArgumentIndex, 0, useSite.Arguments[0]); } + } - bool HandleArgument(int i, ILInstruction arg) - { - ILVariable closureVar; - if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) - return false; - if (closureVar.Kind == VariableKind.NamedArgument) - return false; - ITypeDefinition potentialDisplayClass = UnwrapByRef(closureVar.Type).GetDefinition(); - if (!TransformDisplayClassUsage.IsPotentialClosure(context, potentialDisplayClass)) - return false; - if (i - firstArgumentIndex >= 0) { - Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); - } - if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0) - return true; - // determine the capture scope of closureVar and the declaration scope of the function - var instructions = closureVar.StoreInstructions.OfType() - .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset).ToList(); - var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); - if (closureVar.CaptureScope == null) - closureVar.CaptureScope = additionalScope; - else - closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); - if (function.DeclarationScope == null) - function.DeclarationScope = closureVar.CaptureScope; - else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType().First())) - function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); - return true; + bool HandleArgument(ILFunction function, int firstArgumentIndex, int i, ILInstruction arg) + { + ILVariable closureVar; + if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) + return false; + if (closureVar.Kind == VariableKind.NamedArgument) + return false; + if (!TransformDisplayClassUsage.IsClosure(context, closureVar, null, out _, out var initializer)) + return false; + if (i - firstArgumentIndex >= 0) { + Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); } + // determine the capture scope of closureVar and the declaration scope of the function + var additionalScope = BlockContainer.FindClosestContainer(initializer); + if (closureVar.CaptureScope == null) + closureVar.CaptureScope = additionalScope; + else + closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); + if (function.DeclarationScope == null) + function.DeclarationScope = closureVar.CaptureScope; + else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType().First())) + function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + return true; } bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index c8384aac7..ca77a44c4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var v in f.Variables.ToArray()) { if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f)) continue; - if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(v, out ITypeDefinition closureType, out var inst)) { + if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, instructionsToRemove, out ITypeDefinition closureType, out var inst)) { AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); } 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, decompilationContext)) { @@ -110,30 +110,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool IsClosure(ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) + internal static bool IsClosure(ILTransformContext context, ILVariable variable, List instructionsToRemove, out ITypeDefinition closureType, out ILInstruction initializer) { closureType = null; initializer = null; if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) { initializer = inst; - if (IsClosureInit(inst, out closureType)) { - instructionsToRemove.Add(inst); + if (IsClosureInit(context, inst, out closureType)) { + instructionsToRemove?.Add(inst); return true; } } closureType = variable.Type.GetDefinition(); - if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { + if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(context, closureType)) { initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); return true; } return false; } - bool IsClosureInit(StLoc inst, out ITypeDefinition closureType) + static bool IsClosureInit(ILTransformContext context, StLoc inst, out ITypeDefinition closureType) { if (inst.Value is NewObj newObj) { closureType = newObj.Method.DeclaringTypeDefinition; - return closureType != null && IsPotentialClosure(this.context, newObj); + return closureType != null && IsPotentialClosure(context, newObj); } closureType = null; return false; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs index cb53d51a6..242b6b193 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using ICSharpCode.Decompiler.Util; @@ -30,19 +31,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { readonly IMethod baseMethod; - public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters) + public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters) { + if (baseMethod == null) + throw new ArgumentNullException(nameof(baseMethod)); this.baseMethod = baseMethod; this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; + this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters; } - public bool Equals(IMember obj, TypeVisitor typeNormalization) { if (!(obj is LocalFunctionMethod other)) return false; return baseMethod.Equals(other.baseMethod, typeNormalization) - && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters + && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters; } public override bool Equals(object obj) @@ -50,23 +54,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation if (!(obj is LocalFunctionMethod other)) return false; return baseMethod.Equals(other.baseMethod) - && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters + && NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters; } public override int GetHashCode() { - unchecked { - return baseMethod.GetHashCode() + NumberOfCompilerGeneratedParameters + 1; - } + return baseMethod.GetHashCode(); } public override string ToString() { - return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}]", ReducedFrom, NumberOfCompilerGeneratedParameters); + return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}, NumberOfCompilerGeneratedTypeParameters={2}]", ReducedFrom, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); } internal int NumberOfCompilerGeneratedParameters { get; } + internal int NumberOfCompilerGeneratedTypeParameters { get; } + + internal bool IsStaticLocalFunction => NumberOfCompilerGeneratedParameters == 0 && (baseMethod.IsStatic || (baseMethod.DeclaringTypeDefinition.IsCompilerGenerated() && !baseMethod.DeclaringType.GetFields(f => !f.IsStatic).Any())); + public IMember MemberDefinition => this; public IType ReturnType => baseMethod.ReturnType; @@ -79,7 +86,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public IMethod Specialize(TypeParameterSubstitution substitution) { - return SpecializedMethod.Create(this, substitution); + return new LocalFunctionMethod( + baseMethod.Specialize(substitution), + NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters); } IMember IMember.Specialize(TypeParameterSubstitution substitution) diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 21b15a4c0..4bab4df31 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -835,6 +835,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Introduce static local functions. + /// + public static string DecompilerSettings_IntroduceStaticLocalFunctions { + get { + return ResourceManager.GetString("DecompilerSettings.IntroduceStaticLocalFunctions", resourceCulture); + } + } + /// /// Looks up a localized string similar to IsByRefLikeAttribute should be replaced with 'ref' modifiers on structs. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 4a7684684..a653a52a9 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -826,4 +826,7 @@ Are you sure you want to continue? Are you sure that you want to remove all assembly lists and recreate the default assembly lists? + + Introduce static local functions + \ No newline at end of file