From f10ab693281a9ebea2502fd16c7e04bd258dff6e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 23 Jul 2019 18:29:48 +0200 Subject: [PATCH] Improve local-function detection to minimize false positives. --- .../CSharp/ExpressionBuilder.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 29 ++++++++++++------- .../Transforms/TransformDisplayClassUsage.cs | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 87602db3f..e7e560d16 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1877,7 +1877,7 @@ namespace ICSharpCode.Decompiler.CSharp // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) pd.Name = "P_" + i; // if this is a local function, we have to skip the parameters for closure references - if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter)) + if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext)) break; } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index dafda4357..85753bd9e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms class LocalFunctionDecompiler : IILTransform { ILTransformContext context; + ITypeResolveContext resolveContext; struct LocalFunctionInfo { @@ -64,6 +65,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!context.Settings.LocalFunctions) return; this.context = context; + this.resolveContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary(); var cancellationToken = context.CancellationToken; // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. @@ -161,11 +163,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return a.Ancestors.OfType().FirstOrDefault(ancestorsOfB.Contains); } - internal static bool IsClosureParameter(IParameter parameter) + internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context) { - return parameter.IsRef - && ((ByReferenceType)parameter.Type).ElementType - .GetDefinition()?.IsCompilerGenerated() == true; + if (!parameter.IsRef) + return false; + var type = ((ByReferenceType)parameter.Type).ElementType.GetDefinition(); + return type != null + && type.Kind == TypeKind.Struct + && TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type); } static IType UnwrapByRef(IType type) @@ -190,7 +195,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { int parametersToRemove = 0; for (int i = method.Parameters.Count - 1; i >= 0; i--) { - if (!IsClosureParameter(method.Parameters[i])) + if (!IsClosureParameter(method.Parameters[i], resolveContext)) break; parametersToRemove++; } @@ -213,23 +218,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms int argumentCount = useSite.Arguments.Count; int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount)); - // copy flags: + // copy flags replacement.ConstrainedTo = useSite.ConstrainedTo; replacement.ILStackWasEmpty = useSite.ILStackWasEmpty; replacement.IsTail = useSite.IsTail; // copy IL ranges - replacement = replacement.WithILRange(useSite); + replacement.AddILRange(useSite); if (wasInstanceCall) { - replacement = replacement.WithILRange(useSite.Arguments[0]); + replacement.AddILRange(useSite.Arguments[0]); } for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { - replacement = replacement.WithILRange(useSite.Arguments[argumentCount - i - 1]); + replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]); } useSite.ReplaceWith(replacement); } void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) { + int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { var arg = useSite.Arguments[i]; ILVariable closureVar; @@ -239,6 +245,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) break; + 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) continue; // determine the capture scope of closureVar and the declaration scope of the function @@ -262,7 +271,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; var opCode = inst.Arguments[1].OpCode; - return (opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn) + return opCode == OpCode.LdFtn && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 382abe96a..d12191033 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (IsClosure(v, 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)) { + 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)) { AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); } }