From fdf4228a1755efd876bb457e9fb5d47c82bc4912 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 10:34:16 +0200 Subject: [PATCH] Fix #1623: Nest local functions correctly, if captured variables are used. --- .../IL/Transforms/LocalFunctionDecompiler.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 9ae8abff4..d8f4d294e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } foreach (var useSite in info.UseSites) { - context.Step("Transform use site at " + useSite.StartILOffset, useSite); + context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); if (useSite.OpCode == OpCode.NewObj) { TransformToLocalFunctionReference(localFunction, useSite); } else { @@ -245,22 +245,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) { int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; - if (firstArgumentIndex > 0) { - HandleArgument(0, useSite.Arguments[0]); - } for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) { if (!HandleArgument(i, useSite.Arguments[i])) break; } + if (firstArgumentIndex > 0) { + HandleArgument(0, useSite.Arguments[0], skipForDeclarationScope: true); + } - bool HandleArgument(int i, ILInstruction arg) + bool HandleArgument(int i, ILInstruction arg, bool skipForDeclarationScope = false) { ILVariable closureVar; if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) return false; if (closureVar.Kind == VariableKind.NamedArgument) return false; - if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) + 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)); @@ -269,7 +270,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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); + .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset).ToList(); var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); if (closureVar.CaptureScope == null) closureVar.CaptureScope = additionalScope; @@ -277,12 +278,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); if (function.DeclarationScope == null) function.DeclarationScope = closureVar.CaptureScope; - else + 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) + { + return TreeTraversal.PreOrder(function, f => f.LocalFunctions).Any(f => declarationScope.IsDescendantOf(f.Body)); + } + internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context) { if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate)