From 44ced13ae927352e4d8452397277a3f3a2f23cf4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Apr 2026 18:40:36 +0000
Subject: [PATCH 1/4] Initial plan
From 4c0f314cc42a253eca19cdee7bd746749fbe3d22 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Apr 2026 19:08:43 +0000
Subject: [PATCH 2/4] Add UnsafeAccessorType (NET10+) for non-public IL
generator types and enable EmitHacksTest
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/e521f323-57a4-40f5-aea8-5ae1e3bbbff0
Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
---
Directory.Build.props | 2 +-
...tExpressionCompiler.LightExpression.csproj | 4 +-
.../FastExpressionCompiler.cs | 47 ++++++
.../FastExpressionCompiler.csproj | 4 +-
.../EmitHacksTest.cs | 152 +++++++++++++++---
.../FastExpressionCompiler.IssueTests.csproj | 4 +-
.../FastExpressionCompiler.TestsRunner.csproj | 4 +-
.../Program.cs | 2 +-
8 files changed, 190 insertions(+), 29 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index 5a50f3fb..591b8919 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,7 +9,7 @@
true
true
- IDE0251;IDE0079;IDE0047;NETSDK1212
+ IDE0251;IDE0079;IDE0047;NETSDK1212;NU1510
false
diff --git a/src/FastExpressionCompiler.LightExpression/FastExpressionCompiler.LightExpression.csproj b/src/FastExpressionCompiler.LightExpression/FastExpressionCompiler.LightExpression.csproj
index f44de903..07cacce9 100644
--- a/src/FastExpressionCompiler.LightExpression/FastExpressionCompiler.LightExpression.csproj
+++ b/src/FastExpressionCompiler.LightExpression/FastExpressionCompiler.LightExpression.csproj
@@ -1,7 +1,7 @@
- net472;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0
- net472;net9.0
+ net472;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0;net10.0
+ net472;net9.0;net10.0
5.4.0
diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs
index 24caf5dd..21af4134 100644
--- a/src/FastExpressionCompiler/FastExpressionCompiler.cs
+++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs
@@ -9295,6 +9295,15 @@ public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
return localBuilder;
}
*/
+#if NET10_0_OR_GREATER
+ // In .NET 10+, use UnsafeAccessorType to directly access RuntimeILGenerator's private fields
+ // without the need for reflection-based DynamicMethod generation at startup
+ GetNextLocalVarLocation = static (il, t) =>
+ {
+ GetMLocalSignature(il).AddArgument(t, false);
+ return PostInc(ref GetMLocalCount(il));
+ };
+#else
// Let's try to acquire the more efficient less allocating method
var m_localSignatureField = DynamicILGeneratorType.GetField("m_localSignature", instanceNonPublic);
if (m_localSignatureField == null)
@@ -9340,6 +9349,7 @@ public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
ExpressionCompiler.FreePooledParamTypes(paramTypes);
endOfGetNextVar:;
+#endif
}
// Restore the demit
@@ -9465,6 +9475,43 @@ public override void Emit(OpCode opcode, MethodInfo meth, int paramCount)
m_length += 4;
}
*/
+
+#if NET10_0_OR_GREATER
+ // UnsafeAccessorType methods for accessing private fields of the non-public RuntimeILGenerator class.
+ // RuntimeILGenerator is the internal base class of DynamicILGenerator that holds the core IL generation state.
+ // Using UnsafeAccessorType avoids reflection at call time and is compatible with AOT compilation.
+
+ /// Gets a ref to the m_localSignature field of the ILGenerator (declared in the internal RuntimeILGenerator).
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_localSignature")]
+ internal static extern ref SignatureHelper GetMLocalSignature(
+ [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il);
+
+ /// Gets a ref to the m_localCount field of the ILGenerator (declared in the internal RuntimeILGenerator).
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_localCount")]
+ internal static extern ref int GetMLocalCount(
+ [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il);
+
+ /// Gets a ref to the m_length field of the ILGenerator (declared in the internal RuntimeILGenerator).
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_length")]
+ internal static extern ref int GetMLength(
+ [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il);
+
+ /// Gets a ref to the m_ILStream field of the ILGenerator (declared in the internal RuntimeILGenerator).
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_ILStream")]
+ internal static extern ref byte[] GetMILStream(
+ [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il);
+
+ /// Calls the internal UpdateStackSize method on the ILGenerator (declared in the internal RuntimeILGenerator).
+ [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "UpdateStackSize")]
+ internal static extern void UpdateStackSize(
+ [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il,
+ OpCode opcode, int stackchange);
+
+ /// Gets a ref to the m_tokens field on a DynamicScope instance (internal type).
+ [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_tokens")]
+ internal static extern ref System.Collections.Generic.List