@@ -4620,12 +4620,11 @@ public static T GetFirst<T>(this T[] source)
4620
4620
}
4621
4621
}
4622
4622
4623
- /// Hey
4623
+ /// <summary>Reflecting the internal methods to access the more performant for defining the local variable</summary>
4624
4624
public static class ILGeneratorHacks
4625
4625
{
4626
+ // The original ILGenerator methods we are trying to hack without allocating the `LocalBuilder`
4626
4627
/*
4627
- // Original methods from ILGenerator.cs
4628
-
4629
4628
public virtual LocalBuilder DeclareLocal(Type localType)
4630
4629
{
4631
4630
return this.DeclareLocal(localType, false);
@@ -4649,56 +4648,72 @@ public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
4649
4648
}
4650
4649
*/
4651
4650
4652
- /// Not allocating the LocalBuilder class
4653
- /// emitting this:
4654
- /// il.m_localSignature.AddArgument(type) ;
4655
- /// return PostInc(ref il.LocalCount);
4656
- public static Func < ILGenerator , Type , int > CompileGetNextLocalVarIndex ( )
4651
+ private static readonly Func < ILGenerator , Type , int > _getNextLocalVarIndex ;
4652
+
4653
+ internal static int PostInc ( ref int i ) => i ++ ;
4654
+
4655
+ static ILGeneratorHacks ( )
4657
4656
{
4658
- if ( LocalCountField == null || LocalSignatureField == null || AddArgumentMethod == null )
4659
- return ( i , t ) => i . DeclareLocal ( t ) . LocalIndex ;
4657
+ // the default allocatee method
4658
+ _getNextLocalVarIndex = ( il , type ) => il . DeclareLocal ( type ) . LocalIndex ;
4659
+
4660
+ // now let's try to acquire the more efficient less allocating method
4661
+ var ilGenTypeInfo = typeof ( ILGenerator ) . GetTypeInfo ( ) ;
4662
+ var localSignatureField = ilGenTypeInfo . GetDeclaredField ( "m_localSignature" ) ;
4663
+ if ( localSignatureField == null )
4664
+ return ;
4665
+
4666
+ var localCountField = ilGenTypeInfo . GetDeclaredField ( "m_localCount" ) ;
4667
+ if ( localCountField == null )
4668
+ return ;
4669
+
4670
+ // looking for the `SignatureHelper.AddArgument(Type argument, bool pinned)`
4671
+ MethodInfo addArgumentMethod = null ;
4672
+ foreach ( var m in typeof ( SignatureHelper ) . GetTypeInfo ( ) . GetDeclaredMethods ( "AddArgument" ) )
4673
+ {
4674
+ var ps = m . GetParameters ( ) ;
4675
+ if ( ps . Length == 2 && ps [ 0 ] . ParameterType == typeof ( Type ) && ps [ 1 ] . ParameterType == typeof ( bool ) )
4676
+ {
4677
+ addArgumentMethod = m ;
4678
+ break ;
4679
+ }
4680
+ }
4660
4681
4661
- var method = new DynamicMethod ( string . Empty ,
4682
+ if ( addArgumentMethod == null )
4683
+ return ;
4684
+
4685
+ // our own helper - always available
4686
+ var postIncMethod = typeof ( ILGeneratorHacks ) . GetTypeInfo ( ) . GetDeclaredMethod ( nameof ( PostInc ) ) ;
4687
+
4688
+ // now let's compile the following method without allocating the LocalBuilder class:
4689
+ /*
4690
+ il.m_localSignature.AddArgument(type);
4691
+ return PostInc(ref il.LocalCount);
4692
+ */
4693
+ var efficientMethod = new DynamicMethod ( string . Empty ,
4662
4694
typeof ( int ) , new [ ] { typeof ( ExpressionCompiler . ArrayClosure ) , typeof ( ILGenerator ) , typeof ( Type ) } ,
4663
4695
typeof ( ExpressionCompiler . ArrayClosure ) , skipVisibility : true ) ;
4696
+ var il = efficientMethod . GetILGenerator ( ) ;
4664
4697
4665
- var il = method . GetILGenerator ( ) ;
4666
-
4667
- il . Emit ( OpCodes . Ldarg_1 ) ; // load `il` argument
4668
- il . Emit ( OpCodes . Ldfld , LocalSignatureField ) ;
4669
- il . Emit ( OpCodes . Ldarg_2 ) ; // load `type` argument
4698
+ // emitting `il.m_localSignature.AddArgument(type);`
4699
+ il . Emit ( OpCodes . Ldarg_1 ) ; // load `il` argument (arg_0 is the empty closure object)
4700
+ il . Emit ( OpCodes . Ldfld , localSignatureField ) ;
4701
+ il . Emit ( OpCodes . Ldarg_2 ) ; // load `type` argument
4670
4702
il . Emit ( OpCodes . Ldc_I4_0 ) ; // load `pinned: false` argument
4671
- il . Emit ( OpCodes . Call , AddArgumentMethod ) ;
4703
+ il . Emit ( OpCodes . Call , addArgumentMethod ) ;
4672
4704
4705
+ // emitting `return PostInc(ref il.LocalCount);`
4673
4706
il . Emit ( OpCodes . Ldarg_1 ) ; // load `il` argument
4674
- il . Emit ( OpCodes . Ldflda , LocalCountField ) ;
4675
- il . Emit ( OpCodes . Call , PostIncMethod ) ;
4707
+ il . Emit ( OpCodes . Ldflda , localCountField ) ;
4708
+ il . Emit ( OpCodes . Call , postIncMethod ) ;
4676
4709
4677
4710
il . Emit ( OpCodes . Ret ) ;
4678
4711
4679
- return ( Func < ILGenerator , Type , int > ) method . CreateDelegate ( typeof ( Func < ILGenerator , Type , int > ) , ExpressionCompiler . EmptyArrayClosure ) ;
4712
+ _getNextLocalVarIndex = ( Func < ILGenerator , Type , int > ) efficientMethod . CreateDelegate (
4713
+ typeof ( Func < ILGenerator , Type , int > ) , ExpressionCompiler . EmptyArrayClosure ) ;
4680
4714
}
4681
4715
4682
- internal static int PostInc ( ref int i ) => i ++ ;
4683
-
4684
- public static readonly MethodInfo PostIncMethod = typeof ( ILGeneratorHacks ) . GetTypeInfo ( )
4685
- . GetDeclaredMethod ( nameof ( PostInc ) ) ;
4686
-
4687
- /// Get via reflection
4688
- public static readonly FieldInfo LocalSignatureField = typeof ( ILGenerator ) . GetTypeInfo ( )
4689
- . GetDeclaredField ( "m_localSignature" ) ;
4690
-
4691
- /// Get via reflection
4692
- public static readonly FieldInfo LocalCountField = typeof ( ILGenerator ) . GetTypeInfo ( )
4693
- . GetDeclaredField ( "m_localCount" ) ;
4694
-
4695
- /// Get via reflection
4696
- public static readonly MethodInfo AddArgumentMethod = typeof ( SignatureHelper ) . GetTypeInfo ( )
4697
- . GetDeclaredMethods ( nameof ( SignatureHelper . AddArgument ) ) . First ( m => m . GetParameters ( ) . Length == 2 ) ;
4698
-
4699
- private static readonly Func < ILGenerator , Type , int > _getNextLocalVarIndex = CompileGetNextLocalVarIndex ( ) ;
4700
-
4701
- /// Does the job
4716
+ /// <summary>Efficiently returns the next variable index, hopefully without unnecessary allocations.</summary>
4702
4717
public static int GetNextLocalVarIndex ( this ILGenerator il , Type t ) => _getNextLocalVarIndex ( il , t ) ;
4703
4718
}
4704
4719
0 commit comments