From 5be6be97df974a314ad0f90a40974b4223b373c2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 19 Dec 2019 17:35:15 +0100 Subject: [PATCH] Code review: * rename a few identifiers, * add comments, * skip all compiler-generated type-arguments in resolve result, * and change implementation of LocalFunctionMethod.Specialize: wrap specialized method and not the other way round. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 30 ++++++++++++------- .../Implementation/LocalFunctionMethod.cs | 6 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index a2ca47fe1..267a757fe 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1462,7 +1462,7 @@ namespace ICSharpCode.Decompiler.CSharp method.DeclaringType, new IParameterizedMember[] { method } ) - }, method.TypeArguments + }, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray() ); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 2e8d36bbd..31efc2500 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -53,18 +53,26 @@ 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; @@ -88,8 +96,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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.MatchLdObj(out var target1, out var type) && target1 is LdFlda && thisVar.Type == type && type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, 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 == type).OnlyOrDefault(); + 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); @@ -241,7 +249,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } 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.TypeArguments.IndexOf(pt)).DefaultIfEmpty().Max(); + .Select(pt => (int?)targetMethod.TypeParameters.IndexOf(pt)).DefaultIfEmpty().Max(); if (lastParams != null && lastParams.GetValueOrDefault() + 1 > skipCount) skipCount = lastParams.GetValueOrDefault() + 1; } @@ -260,9 +268,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms var classTypeParameters = new List(targetMethod.DeclaringType.TypeParameters); var methodTypeParameters = new List(targetMethod.TypeParameters); - var a = targetMethod.DeclaringType.TypeArguments.Concat(targetMethod.TypeArguments).Take(total); + var skippedTypeArguments = targetMethod.DeclaringType.TypeArguments.Concat(targetMethod.TypeArguments).Take(total); int idx = 0; - foreach (var curA in a) { + foreach (var skippedTA in skippedTypeArguments) { int curIdx; List curParameters; IReadOnlyList curArgs; @@ -277,7 +285,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (curArgs[curIdx].Kind != TypeKind.TypeParameter) break; - curParameters[curIdx] = (ITypeParameter)curA; + curParameters[curIdx] = (ITypeParameter)skippedTA; idx++; } if (idx != total) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs index 9d07a120e..242b6b193 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -35,8 +35,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { if (baseMethod == null) throw new ArgumentNullException(nameof(baseMethod)); - if (baseMethod is SpecializedMethod) - throw new ArgumentException("Must not be a specialized method!", nameof(baseMethod)); this.baseMethod = baseMethod; this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters; @@ -88,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)