@@ -550,6 +550,166 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
550
550
return method.CreateDelegate(delegateType, closure);
551
551
}
552
552
553
+ #if NET8_0_OR_GREATER
554
+ internal static readonly FieldInfo IlGeneratorField =
555
+ typeof(DynamicMethod).GetField("_ilGenerator", BindingFlags.Instance | BindingFlags.NonPublic);
556
+ internal static readonly Type DynamicILGeneratorType = IlGeneratorField.FieldType;
557
+ internal static readonly ConstructorInfo ScopeTreeCtor =
558
+ DynamicILGeneratorType.BaseType
559
+ .GetField("m_ScopeTree", BindingFlags.Instance | BindingFlags.NonPublic)
560
+ .FieldType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
561
+
562
+ internal static readonly MethodInfo ArrayClearMethod =
563
+ typeof(Array).GetMethod(nameof(Array.Clear), [typeof(Array), typeof(int), typeof(int)]);
564
+ internal static readonly FieldInfo ListOfObjectsSize =
565
+ typeof(List<object>).GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic);
566
+
567
+ internal static readonly FieldInfo DynamicILGeneratorScopeField =
568
+ DynamicILGeneratorType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic);
569
+ internal static readonly FieldInfo DynamicScopeTokensField =
570
+ DynamicILGeneratorScopeField.FieldType.GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic);
571
+ internal static readonly PropertyInfo DynamicScopeTokensItem =
572
+ DynamicScopeTokensField.FieldType.GetProperty("Item");
573
+ internal static MethodInfo GetMethodSigHelperMethod = typeof(SignatureHelper)
574
+ .GetMethod("GetMethodSigHelper", BindingFlags.Static | BindingFlags.Public, null, [typeof(Module), typeof(Type), typeof(Type[])], null);
575
+ internal static MethodInfo GetSignatureMethod = typeof(SignatureHelper)
576
+ .GetMethod("GetSignature", BindingFlags.Instance | BindingFlags.NonPublic, null, [typeof(bool)], null);
577
+ internal static MethodInfo GetTokenForMethod = DynamicILGeneratorScopeField.FieldType
578
+ .GetMethod("GetTokenFor", BindingFlags.Instance | BindingFlags.Public, null, [typeof(byte[])], null);
579
+ internal static FieldInfo MethodSigTokenField =
580
+ DynamicILGeneratorType.GetField("m_methodSigToken", BindingFlags.Instance | BindingFlags.NonPublic);
581
+ internal static Action<DynamicMethod, ILGenerator, Type, Type[]> ReuseDynamicILGeneratorOfAnyMethodSignature()
582
+ {
583
+ const BindingFlags allDeclared = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
584
+
585
+ var dynMethod = new DynamicMethod(string.Empty,
586
+ typeof(void),
587
+ [typeof(ExpressionCompiler.ArrayClosure), typeof(DynamicMethod), typeof(ILGenerator), typeof(Type), typeof(Type[])],
588
+ typeof(ExpressionCompiler.ArrayClosure),
589
+ true);
590
+
591
+ var il = dynMethod.GetILGenerator(256); // precalculating the size to avoid waste
592
+
593
+ var baseFields = DynamicILGeneratorType.BaseType.GetFields(allDeclared);
594
+ foreach (var field in baseFields)
595
+ {
596
+ var fieldName = field.Name;
597
+ if (fieldName == "m_localSignature") // todo: skip, let's see how it works
598
+ continue;
599
+
600
+ // m_ScopeTree = new ScopeTree();
601
+ if (fieldName == "m_ScopeTree")
602
+ {
603
+ il.Demit(OpCodes.Ldarg_2);
604
+ il.Demit(OpCodes.Newobj, ScopeTreeCtor);
605
+ il.Demit(OpCodes.Stfld, field);
606
+ continue;
607
+ }
608
+
609
+ // m_methodBuilder = method; // dynamicMethod
610
+ if (fieldName == "m_methodBuilder")
611
+ {
612
+ il.Demit(OpCodes.Ldarg_2);
613
+ il.Demit(OpCodes.Ldarg_1);
614
+ il.Demit(OpCodes.Stfld, field);
615
+ continue;
616
+ }
617
+
618
+ // instead of m_ILStream = new byte[Math.Max(size, DefaultSize)];
619
+ // let's clear it and reuse the buffer
620
+ if (fieldName == "m_ILStream")
621
+ {
622
+ il.Demit(OpCodes.Ldarg_2);
623
+ il.Demit(OpCodes.Ldfld, field);
624
+ var ilStreamVar = ExpressionCompiler.EmittingVisitor.EmitStoreAndLoadLocalVariable(il, typeof(byte[]));
625
+ il.Demit(OpCodes.Ldc_I4_0);
626
+ ExpressionCompiler.EmittingVisitor.EmitLoadLocalVariable(il, ilStreamVar);
627
+ il.Demit(OpCodes.Ldlen);
628
+ il.Demit(OpCodes.Call, ArrayClearMethod);
629
+ continue;
630
+ }
631
+
632
+ il.Demit(OpCodes.Ldarg_2);
633
+ ExpressionCompiler.EmittingVisitor.EmitDefault(il, field.FieldType);
634
+ il.Demit(OpCodes.Stfld, field);
635
+ }
636
+
637
+ il.Emit(OpCodes.Ldarg_2);
638
+ il.Emit(OpCodes.Ldfld, DynamicILGeneratorScopeField);
639
+ var scopeVar = ExpressionCompiler.EmittingVisitor.EmitStoreAndLoadLocalVariable(il, DynamicILGeneratorScopeField.FieldType);
640
+ il.Emit(OpCodes.Ldfld, DynamicScopeTokensField);
641
+ il.Emit(OpCodes.Dup);
642
+
643
+ // reset its List<T>._size to 1, keep the 0th item
644
+ il.Emit(OpCodes.Ldc_I4_1);
645
+ il.Emit(OpCodes.Stfld, ListOfObjectsSize);
646
+
647
+ // set the 0th item to null
648
+ il.Emit(OpCodes.Ldc_I4_0);
649
+ il.Emit(OpCodes.Ldnull);
650
+ il.Emit(OpCodes.Call, DynamicScopeTokensItem.SetMethod);
651
+
652
+ // byte[] methodSignature =
653
+ // SignatureHelper.GetMethodSigHelper(Module? mod, Type? returnType, Type[]? parameterTypes).GetSignature(true);
654
+ il.Emit(OpCodes.Ldnull); // for the module
655
+ il.Emit(OpCodes.Ldarg_3); // load return type
656
+ il.Emit(OpCodes.Ldarg_S, 4); // load parameter types arrays
657
+ il.Emit(OpCodes.Call, GetMethodSigHelperMethod);
658
+ il.Emit(OpCodes.Ldc_I4_1); // load true
659
+ il.Emit(OpCodes.Call, GetSignatureMethod);
660
+ var signatureBytesVar = ExpressionCompiler.EmittingVisitor.EmitStoreLocalVariable(il, typeof(byte[])); // todo: perf could reuse byte[]?
661
+
662
+ // m_methodSigToken = m_scope.GetTokenFor(methodSignature);
663
+ il.Emit(OpCodes.Ldarg_2);
664
+ ExpressionCompiler.EmittingVisitor.EmitLoadLocalVariable(il, scopeVar);
665
+ ExpressionCompiler.EmittingVisitor.EmitLoadLocalVariable(il, signatureBytesVar);
666
+ il.Emit(OpCodes.Call, GetTokenForMethod);
667
+ il.Emit(OpCodes.Stfld, MethodSigTokenField);
668
+
669
+ // store the reused ILGenerator to
670
+ il.Emit(OpCodes.Ldarg_1);
671
+ il.Emit(OpCodes.Ldarg_2);
672
+ il.Emit(OpCodes.Stfld, IlGeneratorField);
673
+
674
+ il.Emit(OpCodes.Ret);
675
+
676
+ var act = dynMethod.CreateDelegate(typeof(Action<DynamicMethod, ILGenerator, Type, Type[]>), ExpressionCompiler.EmptyArrayClosure);
677
+ return (Action<DynamicMethod, ILGenerator, Type, Type[]>)act;
678
+ }
679
+
680
+ internal static Action<DynamicMethod, ILGenerator, Type, Type[]>
681
+ ReuseDynamicILGeneratorOfAnySignature = ReuseDynamicILGeneratorOfAnyMethodSignature();
682
+
683
+ [ThreadStatic]
684
+ internal static ILGenerator pooledILGenerator;
685
+ #endif
686
+
687
+ /// <summary>Get new or pool and configure existing DynamicILGenerator</summary>
688
+ [MethodImpl((MethodImplOptions)256)]
689
+ public static ILGenerator PoolOrNewILGenerator(DynamicMethod dynMethod, Type returnType, Type[] paramTypes)
690
+ {
691
+ #if NET8_0_OR_GREATER
692
+ var il = Interlocked.Exchange(ref pooledILGenerator, null);
693
+ if (il != null)
694
+ ReuseDynamicILGeneratorOfAnySignature(dynMethod, il, typeof(void), paramTypes);
695
+ else
696
+ il = dynMethod.GetILGenerator();
697
+ return il;
698
+ #else
699
+ return dynMethod.GetILGenerator();
700
+ #endif
701
+ }
702
+
703
+ /// <summary>Should be called only after call to DynamicMethod.CreateDelegate</summary>
704
+ [MethodImpl((MethodImplOptions)256)]
705
+ public static void FreeILGenerator(DynamicMethod dynMethod, ILGenerator il)
706
+ {
707
+ #if NET8_0_OR_GREATER
708
+ IlGeneratorField.SetValue(dynMethod, null); // required to break the link with the current method and avoid memory leak
709
+ Interlocked.Exchange(ref pooledILGenerator, il);
710
+ #endif
711
+ }
712
+
553
713
private static readonly Type[] _closureAsASingleParamType = { typeof(ArrayClosure) };
554
714
private static readonly Type[][] _paramTypesPoolWithElem0OfLength1 = new Type[8][]; // todo: @perf @mem could we use this for other Type arrays?
555
715
0 commit comments