diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3f70f2f6..791ade83 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -32,6 +32,9 @@ jobs:
- name: Build
run: dotnet build -c:Release
+ - name: Tests - net10.0 (Latest)
+ run: dotnet run --no-build -c:Release -f:net10.0 --project test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj
+
- name: Tests - net9.0 (Latest)
run: dotnet run --no-build -c:Release -f:net9.0 --project test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj
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/build.bat b/build.bat
index 8118e34e..a46b739f 100644
--- a/build.bat
+++ b/build.bat
@@ -15,6 +15,11 @@ echo:## Finished: RESTORE and BUILD
echo:
echo:## Starting: TESTS...
+echo:
+echo:running on .NET 10.0 (Latest)
+dotnet run --no-build -f:net10.0 -c:Release --project test/FastExpressionCompiler.TestsRunner
+if %ERRORLEVEL% neq 0 goto :error
+
echo:
echo:running on .NET 9.0 (Latest)
dotnet run --no-build -f:net9.0 -c:Release --project test/FastExpressionCompiler.TestsRunner
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