Browse Source

Merge pull request #1797 from yyjdelete/localFunc

Add support for generic and static local function

Fixes #1588
pull/1880/head
Siegfried Pammer 6 years ago
parent
commit
f08a721e6a
  1. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  2. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs
  3. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  4. 387
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  5. 40
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  6. 6
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 13
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  8. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  9. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  10. 1
      ICSharpCode.Decompiler/IL/ILReader.cs
  11. 2
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  12. 238
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  13. 14
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  14. 27
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  15. 9
      ILSpy/Properties/Resources.Designer.cs
  16. 3
      ILSpy/Properties/Resources.resx

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -190,7 +190,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -190,7 +190,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return await Nested(1) + await Nested(2);
#if CS80
static async Task<int> Nested(int i)
#else
async Task<int> Nested(int i)
#endif
{
await Task.Delay(i);
return i;

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs

@ -118,7 +118,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -118,7 +118,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return await Nested(1) + await Nested(2);
#if CS80
static async ValueTask<int> Nested(int i)
#else
async ValueTask<int> Nested(int i)
#endif
{
await Task.Delay(i);
return i;

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -187,7 +187,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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
{
}

387
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -23,12 +23,297 @@ namespace LocalFunctions @@ -23,12 +23,297 @@ namespace LocalFunctions
{
internal class LocalFunctions
{
public class Generic<T1> where T1 : struct, ICloneable, IConvertible
{
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
{
T2 t2 = default(T2);
object z = this;
for (int j = 0; j < 10; j++) {
int i = 0;
i += NonStaticMethod6<object>();
int NonStaticMethod6<T3>()
{
t2 = default(T2);
int l = 0;
return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode();
int NonStaticMethod6_1<T4>()
{
return i + l + NonStaticMethod6<T4>() + StaticMethod1<decimal>();
}
}
}
return MixedLocalFunction<T1>() + MixedLocalFunction<T2>() + StaticMethod1<decimal>() + StaticMethod1<int>() + NonStaticMethod3() + StaticMethod4<object>(null) + StaticMethod5<T1>();
int NonStaticMethod3()
{
return GetHashCode();
}
#if CS80
static int StaticMethod1<T3>() where T3 : struct
#else
int StaticMethod1<T3>() where T3 : struct
#endif
{
return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticMethod1<float>() + StaticMethod1_1<T3, DayOfWeek>() + StaticMethod2_RepeatT2<T2, T3, DayOfWeek>();
}
#if CS80
static int StaticMethod1_1<T3, T4>() where T3 : struct where T4 : Enum
#else
int StaticMethod1_1<T3, T4>() 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<float>() + StaticMethod1_1<T3, DayOfWeek>();
}
#pragma warning disable CS8387
#if CS80
static int StaticMethod2_RepeatT2<T2, T3, T4>() where T2 : IConvertible where T3 : struct where T4 : Enum
#else
int StaticMethod2_RepeatT2<T2, T3, T4>() 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>(T dd)
#else
int StaticMethod4<T>(T dd)
#endif
{
return 0;
}
#if CS80
static int StaticMethod5<T3>()
#else
int StaticMethod5<T3>()
#endif
{
int k = 0;
return k + NonStaticMethod5_1<T1>();
int NonStaticMethod5_1<T4>()
{
return k;
}
}
}
public int MixedLocalFunction2Delegate<T2>() where T2 : ICloneable, IConvertible
{
T2 t2 = default(T2);
object z = this;
for (int j = 0; j < 10; j++) {
int i = 0;
i += StaticInvokeAsFunc(NonStaticMethod6<object>);
int NonStaticMethod6<T3>()
{
t2 = default(T2);
int l = 0;
return StaticInvokeAsFunc(NonStaticMethod6_1<T1>) + StaticInvokeAsFunc(NonStaticMethod6_1<T2>) + z.GetHashCode();
int NonStaticMethod6_1<T4>()
{
return i + l + StaticInvokeAsFunc(NonStaticMethod6<T4>) + StaticInvokeAsFunc(StaticMethod1<decimal>);
}
}
}
return StaticInvokeAsFunc(MixedLocalFunction2Delegate<T1>) + StaticInvokeAsFunc(MixedLocalFunction2Delegate<T2>) + StaticInvokeAsFunc(StaticMethod1<decimal>) + StaticInvokeAsFunc(StaticMethod1<int>) + StaticInvokeAsFunc(NonStaticMethod3) + StaticInvokeAsFunc(StaticMethod5<T1>) + new Func<object, int>(StaticMethod4<object>)(null) + StaticInvokeAsFunc2<object>(StaticMethod4<object>) + new Func<Func<object, int>, int>(StaticInvokeAsFunc2<object>)(StaticMethod4<object>);
int NonStaticMethod3()
{
return GetHashCode();
}
#if CS80
static int StaticInvokeAsFunc(Func<int> func)
#else
int StaticInvokeAsFunc(Func<int> func)
#endif
{
return func();
}
#if CS80
static int StaticInvokeAsFunc2<T>(Func<T, int> func)
#else
int StaticInvokeAsFunc2<T>(Func<T, int> func)
#endif
{
return func(default(T));
}
#if CS80
static int StaticMethod1<T3>() where T3 : struct
#else
int StaticMethod1<T3>() where T3 : struct
#endif
{
return typeof(T1).Name.Length + typeof(T2).Name.Length + typeof(T3).Name.Length + StaticInvokeAsFunc(StaticMethod1<float>) + StaticInvokeAsFunc(StaticMethod1_1<T3, DayOfWeek>) + StaticInvokeAsFunc(StaticMethod2_RepeatT2<T2, T3, DayOfWeek>);
}
#if CS80
static int StaticMethod1_1<T3, T4>() where T3 : struct where T4 : Enum
#else
int StaticMethod1_1<T3, T4>() 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<float>) + StaticInvokeAsFunc(StaticMethod1_1<T3, DayOfWeek>);
}
#pragma warning disable CS8387
#if CS80
static int StaticMethod2_RepeatT2<T2, T3, T4>() where T2 : IConvertible where T3 : struct where T4 : Enum
#else
int StaticMethod2_RepeatT2<T2, T3, T4>() 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>(T dd)
#else
int StaticMethod4<T>(T dd)
#endif
{
return 0;
}
#if CS80
static int StaticMethod5<T3>()
#else
int StaticMethod5<T3>()
#endif
{
int k = 0;
return k + StaticInvokeAsFunc(NonStaticMethod5_1<T1>);
int NonStaticMethod5_1<T4>()
{
return k;
}
}
}
public static void Test_CaptureT<T2>()
{
T2 t2 = default(T2);
Method1<int>();
void Method1<T3>()
{
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<T2>() where T2 : List<T2>
{
ZZ<T2>(null);
ZZ2<object>(null);
#if CS80
static void Nop<T>(T data)
#else
void Nop<T>(T data)
#endif
{
}
#if CS80
static void ZZ<T3>(T3 t3) where T3 : T2
#else
void ZZ<T3>(T3 t3) where T3 : T2
#endif
{
Nop<List<T2>>(t3);
ZZ1<T3>(t3);
ZZ3();
void ZZ3()
{
Nop<List<T2>>(t3);
}
}
#if CS80
static void ZZ1<T3>(T3 t3)
#else
void ZZ1<T3>(T3 t3)
#endif
{
Nop<List<T2>>((List<T2>)(object)t3);
}
#if CS80
static void ZZ2<T3>(T3 t3)
#else
void ZZ2<T3>(T3 t3)
#endif
{
Nop<List<T2>>((List<T2>)(object)t3);
}
}
#if false
public void GenericArgsWithAnonymousType()
{
Method<int>();
#if CS80
static void Method<T2>()
#else
void Method<T2>()
#endif
{
int i = 0;
var obj2 = new {
A = 1
};
Method2(obj2);
Method3(obj2);
void Method2<T3>(T3 obj1)
{
//keep nested
i = 0;
}
#if CS80
static void Method3<T3>(T3 obj1)
#else
void Method3<T3>(T3 obj1)
#endif
{
}
}
}
#if CS80
public void NameConflict()
{
int i = 0;
Method<int>();
void Method<T2>()
{
Method();
void Method()
{
Method<T2>();
i = 0;
void Method<T2>()
{
i = 0;
Method();
static void Method()
{
}
}
}
}
}
#endif
#endif
}
private int field;
private Lazy<object> nonCapturinglocalFunctionInLambda = new Lazy<object>(delegate {
return CreateValue();
#if CS80
static object CreateValue()
#else
object CreateValue()
#endif
{
return null;
}
@ -69,7 +354,11 @@ namespace LocalFunctions @@ -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 @@ -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 @@ -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 @@ -183,7 +480,11 @@ namespace LocalFunctions
Name();
action();
#if CS80
static void Name()
#else
void Name()
#endif
{
}
@ -194,12 +495,20 @@ namespace LocalFunctions @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -332,5 +657,67 @@ namespace LocalFunctions
// }
// }
//}
public void NestedCapture1()
{
Method1(null);
#if CS80
static Action<object> Method1(Action<object> action)
#else
Action<object> Method1(Action<object> 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<int>(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<int>(ZZZ_1_0)();
int ZZZ_1_0()
{
t0 = 0;
t = 0;
return 0;
}
}
}
}
}
}

40
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -194,9 +194,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -1448,7 +1462,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, EmptyList<IType>.Instance
}, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray()
);
}

6
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,9 +217,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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<ILFunction>()) {
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;
}

13
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -992,9 +992,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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;
}

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

@ -1807,7 +1807,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -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;

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -111,12 +111,13 @@ namespace ICSharpCode.Decompiler @@ -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 @@ -1082,6 +1083,23 @@ namespace ICSharpCode.Decompiler
}
}
bool staticLocalFunctions = true;
/// <summary>
/// Gets/Sets whether C# 8.0 static local functions should be transformed.
/// </summary>
[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;
/// <summary>

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;

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

@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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<ITypeParameter>();
var methodTypeParameters = new List<ITypeParameter>();

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

@ -53,18 +53,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -53,18 +53,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// <para>
/// 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., <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
/// or can be used as part of the "delegate construction" pattern, i.e.,
/// <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
/// </para>
/// 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 <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
/// 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
/// <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
/// 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.
/// </summary>
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<MethodDefinitionHandle, LocalFunctionInfo>();
@ -78,25 +88,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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<ILFunction>().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 @@ -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<ILFunction>().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<MethodDefinitionHandle, LocalFunctionInfo> localFunctions)
{
foreach (var inst in function.Body.Descendants) {
@ -132,9 +201,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -132,9 +201,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { 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 @@ -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 @@ -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<ITypeParameter>(targetMethod.DeclaringType.TypeParameters);
var methodTypeParameters = new List<ITypeParameter>(targetMethod.TypeParameters);
var skippedTypeArguments = targetMethod.DeclaringType.TypeArguments.Concat(targetMethod.TypeArguments).Take(total);
int idx = 0;
foreach (var skippedTA in skippedTypeArguments) {
int curIdx;
List<ITypeParameter> curParameters;
IReadOnlyList<IType> 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<T>(ILInstruction a, ILInstruction b)
where T : ILInstruction
{
@ -204,7 +331,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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<ILInstruction>()
.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<BlockContainer>(closureVar.CaptureScope, additionalScope);
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType<ILFunction>().First()))
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(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<BlockContainer>(closureVar.CaptureScope, additionalScope);
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType<ILFunction>().First()))
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(function.DeclarationScope, closureVar.CaptureScope);
return true;
}
bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function)

14
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -59,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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<ILInstruction> 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;

27
ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs

@ -18,6 +18,7 @@ @@ -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 @@ -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 @@ -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 @@ -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)

9
ILSpy/Properties/Resources.Designer.cs generated

@ -835,6 +835,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -835,6 +835,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Introduce static local functions.
/// </summary>
public static string DecompilerSettings_IntroduceStaticLocalFunctions {
get {
return ResourceManager.GetString("DecompilerSettings.IntroduceStaticLocalFunctions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to IsByRefLikeAttribute should be replaced with &apos;ref&apos; modifiers on structs.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -826,4 +826,7 @@ Are you sure you want to continue?</value> @@ -826,4 +826,7 @@ Are you sure you want to continue?</value>
<data name="ListsResetConfirmation" xml:space="preserve">
<value>Are you sure that you want to remove all assembly lists and recreate the default assembly lists?</value>
</data>
<data name="DecompilerSettings.IntroduceStaticLocalFunctions" xml:space="preserve">
<value>Introduce static local functions</value>
</data>
</root>
Loading…
Cancel
Save