@@ -550,6 +550,166 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
550550 return method.CreateDelegate(delegateType, closure);
551551 }
552552
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+
553713 private static readonly Type[] _closureAsASingleParamType = { typeof(ArrayClosure) };
554714 private static readonly Type[][] _paramTypesPoolWithElem0OfLength1 = new Type[8][]; // todo: @perf @mem could we use this for other Type arrays?
555715
0 commit comments