@ -53,18 +53,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
///
/// <para>
/// <para>
/// local functions can either be used in method calls, i.e., call and callvirt instructions,
/// 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(<target-expression>, ldftn <method>)</c>.
/// or can be used as part of the "delegate construction" pattern, i.e.,
/// <c>newobj Delegate(<target-expression>, ldftn <method>)</c>.
/// </para>
/// </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.
/// As local functions can be declared practically anywhere, we have to take a look at
/// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function.
/// all use-sites and infer the declaration location from that. Use-sites can be call,
/// 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>.
/// 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.
/// 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>
/// </summary>
public void Run ( ILFunction function , ILTransformContext context )
public void Run ( ILFunction function , ILTransformContext context )
{
{
if ( ! context . Settings . LocalFunctions )
if ( ! context . Settings . LocalFunctions )
return ;
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 ) )
if ( IsLocalFunctionMethod ( function . Method , context ) | | IsLocalFunctionDisplayClass ( function . Method . ParentModule . PEFile , ( TypeDefinitionHandle ) function . Method . DeclaringTypeDefinition . MetadataToken , context ) )
return ;
return ;
this . context = context ;
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 ] ;
var target = info . UseSites . Where ( us = > us . Arguments [ 0 ] . MatchLdLoc ( out _ ) ) . FirstOrDefault ( ) ? . Arguments [ 0 ] ;
if ( target = = null ) {
if ( target = = null ) {
target = info . UseSites [ 0 ] . Arguments [ 0 ] ;
target = info . UseSites [ 0 ] . Arguments [ 0 ] ;
if ( target . MatchLdObj ( out var target1 , out var type ) & & target1 is LdFlda & & thisVar . Type = = type & & t ype . Kind = = TypeKind . Class & & TransformDisplayClassUsage . IsPotentialClosure ( context , t ype. GetDefinition ( ) ) ) {
if ( target . MatchLdFld ( out var target1 , out var field ) & & thisVar . Type . Equals ( field . Type ) & & field . T ype . Kind = = TypeKind . Class & & TransformDisplayClassUsage . IsPotentialClosure ( context , field . T ype. 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 = = type ) . OnlyOrDefault ( ) ;
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 ) {
if ( variable ! = null ) {
target = new LdLoc ( variable ) ;
target = new LdLoc ( variable ) ;
HandleArgument ( localFunction , 1 , 0 , target ) ;
HandleArgument ( localFunction , 1 , 0 , target ) ;
@ -241,7 +249,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
if ( targetMethod . TypeParameters . Count > 0 ) {
if ( targetMethod . TypeParameters . Count > 0 ) {
var lastParams = targetMethod . Parameters . Where ( p = > IsClosureParameter ( p , this . resolveContext ) ) . SelectMany ( p = > UnwrapByRef ( p . Type ) . TypeArguments )
var lastParams = targetMethod . Parameters . Where ( p = > IsClosureParameter ( p , this . resolveContext ) ) . SelectMany ( p = > UnwrapByRef ( p . Type ) . TypeArguments )
. Select ( pt = > ( int? ) targetMethod . TypeArgument s . IndexOf ( pt ) ) . DefaultIfEmpty ( ) . Max ( ) ;
. Select ( pt = > ( int? ) targetMethod . TypeParameter s . IndexOf ( pt ) ) . DefaultIfEmpty ( ) . Max ( ) ;
if ( lastParams ! = null & & lastParams . GetValueOrDefault ( ) + 1 > skipCount )
if ( lastParams ! = null & & lastParams . GetValueOrDefault ( ) + 1 > skipCount )
skipCount = lastParams . GetValueOrDefault ( ) + 1 ;
skipCount = lastParams . GetValueOrDefault ( ) + 1 ;
}
}
@ -260,9 +268,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var classTypeParameters = new List < ITypeParameter > ( targetMethod . DeclaringType . TypeParameters ) ;
var classTypeParameters = new List < ITypeParameter > ( targetMethod . DeclaringType . TypeParameters ) ;
var methodTypeParameters = new List < ITypeParameter > ( targetMethod . TypeParameters ) ;
var methodTypeParameters = new List < ITypeParameter > ( 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 ;
int idx = 0 ;
foreach ( var curA in a ) {
foreach ( var skippedTA in skippedTypeArguments ) {
int curIdx ;
int curIdx ;
List < ITypeParameter > curParameters ;
List < ITypeParameter > curParameters ;
IReadOnlyList < IType > curArgs ;
IReadOnlyList < IType > curArgs ;
@ -277,7 +285,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
if ( curArgs [ curIdx ] . Kind ! = TypeKind . TypeParameter )
if ( curArgs [ curIdx ] . Kind ! = TypeKind . TypeParameter )
break ;
break ;
curParameters [ curIdx ] = ( ITypeParameter ) cur A;
curParameters [ curIdx ] = ( ITypeParameter ) skippedT A;
idx + + ;
idx + + ;
}
}
if ( idx ! = total ) {
if ( idx ! = total ) {