mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Merge pull request #4100 from icsharpcode/fix/3008-callbuilder-assert
Fix #3008: local function forwarding a display class loses its scope
This commit is contained in:
@@ -902,5 +902,29 @@ namespace LocalFunctions
|
||||
#endif
|
||||
return outer;
|
||||
}
|
||||
|
||||
public void Issue3008_DisplayClassForwardedThroughLocalFunction(int start)
|
||||
{
|
||||
Run();
|
||||
void Run()
|
||||
{
|
||||
int captured = start;
|
||||
A();
|
||||
Console.WriteLine(captured);
|
||||
void A()
|
||||
{
|
||||
captured++;
|
||||
if (captured < 10)
|
||||
{
|
||||
B();
|
||||
}
|
||||
}
|
||||
void B()
|
||||
{
|
||||
captured += 2;
|
||||
A();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,22 +141,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
||||
foreach (var useSite in info.UseSites)
|
||||
{
|
||||
DetermineCaptureAndDeclarationScope(info, useSite);
|
||||
}
|
||||
|
||||
if (context.Function.Method.IsConstructor)
|
||||
if (context.Function.Method.IsConstructor)
|
||||
{
|
||||
// Local functions reached from a field initializer usually capture nothing, so
|
||||
// the closure analysis leaves no scope behind; the innermost block containing
|
||||
// all use-sites is a better place for them than the whole constructor body.
|
||||
// Use-sites spread over separate function bodies have no common block
|
||||
// container at all; there the scope from the closure analysis stands.
|
||||
BlockContainer useSiteScope = null;
|
||||
foreach (var useSite in info.UseSites)
|
||||
{
|
||||
if (localFunction.DeclarationScope == null)
|
||||
{
|
||||
localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite);
|
||||
}
|
||||
else
|
||||
{
|
||||
localFunction.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(useSite, localFunction.DeclarationScope);
|
||||
if (localFunction.DeclarationScope == null)
|
||||
{
|
||||
localFunction.DeclarationScope = (BlockContainer)context.Function.Body;
|
||||
}
|
||||
}
|
||||
useSiteScope = useSiteScope == null
|
||||
? BlockContainer.FindClosestContainer(useSite)
|
||||
: FindCommonAncestorInstruction<BlockContainer>(useSite, useSiteScope);
|
||||
if (useSiteScope == null)
|
||||
break;
|
||||
}
|
||||
localFunction.DeclarationScope = useSiteScope
|
||||
?? localFunction.DeclarationScope
|
||||
?? (BlockContainer)context.Function.Body;
|
||||
}
|
||||
|
||||
if (localFunction.DeclarationScope == null)
|
||||
@@ -341,9 +346,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
||||
return null;
|
||||
if (!(TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition()) || context.Function.Method.DeclaringType.Equals(field.Type)))
|
||||
return null;
|
||||
foreach (var v in context.Function.Descendants.OfType<ILFunction>().SelectMany(f => f.Variables))
|
||||
return FindClosureVariableOfType(field.Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the variable holding the display class instance of the given type, anywhere in the
|
||||
/// function tree currently being decompiled.
|
||||
/// </summary>
|
||||
private ILVariable FindClosureVariableOfType(IType type)
|
||||
{
|
||||
foreach (var v in context.Function.Descendants.OfType<ILFunction>().Prepend(context.Function).SelectMany(f => f.Variables))
|
||||
{
|
||||
if (!(TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)))
|
||||
if (!(TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(type)))
|
||||
continue;
|
||||
return v;
|
||||
}
|
||||
@@ -713,6 +727,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
|
||||
}
|
||||
if (closureVar.Kind == VariableKind.NamedArgument)
|
||||
return false;
|
||||
if (parameterIndex >= 0 && closureVar.Kind == VariableKind.Parameter)
|
||||
{
|
||||
// The use-site sits inside another local function that received the display class as
|
||||
// a parameter and forwards it. A parameter has no initializer, so the scope the
|
||||
// display class was created in has to be recovered from the variable holding it.
|
||||
closureVar = FindClosureVariableOfType(closureVar.Type.UnwrapByRef()) ?? closureVar;
|
||||
}
|
||||
var initializer = GetClosureInitializer(closureVar);
|
||||
if (initializer == null)
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user