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 GetMTokens( + [UnsafeAccessorType("System.Reflection.Emit.DynamicScope")] object scope); +#endif } [RequiresUnreferencedCode(Trimming.Message)] diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.csproj b/src/FastExpressionCompiler/FastExpressionCompiler.csproj index d0792ad4..4ec426d3 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.csproj +++ b/src/FastExpressionCompiler/FastExpressionCompiler.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/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs b/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs index 9366fbe9..e35bedba 100644 --- a/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs +++ b/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs @@ -15,9 +15,12 @@ public class EmitHacksTest : ITest public int Run() { DynamicMethod_Emit_Hack(); - // DynamicMethod_Emit_Newobj(); - // DynamicMethod_Hack_Emit_Newobj(); +#if NET10_0_OR_GREATER + DynamicMethod_Emit_Hack_Net10(); + return 4; +#else return 3; +#endif } public void DynamicMethod_Emit_Hack() @@ -27,18 +30,27 @@ public void DynamicMethod_Emit_Hack() Asserts.AreEqual(42, a); } - static Type ilType = typeof(ILGenerator).Assembly.GetType("System.Reflection.Emit.DynamicILGenerator"); - static FieldInfo mScopeField = ilType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); + static readonly Type ilType = typeof(ILGenerator).Assembly.GetType("System.Reflection.Emit.DynamicILGenerator"); - static Type scopeType = ilType.Assembly.GetType("System.Reflection.Emit.DynamicScope"); - static FieldInfo mTokensField = scopeType.GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); + // m_scope field is on DynamicILGenerator (internal class) - accessed via reflection since + // the field type DynamicScope is also internal (UnsafeAccessorType can't return non-public types). + static readonly FieldInfo mScopeField = ilType?.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); - static FieldInfo mLengthField = typeof(ILGenerator).GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic); - static FieldInfo mILStreamField = typeof(ILGenerator).GetField("m_ILStream", BindingFlags.Instance | BindingFlags.NonPublic); - static MethodInfo updateStackSize = typeof(ILGenerator).GetMethod("UpdateStackSize", BindingFlags.Instance | BindingFlags.NonPublic); + static readonly Type scopeType = ilType?.Assembly.GetType("System.Reflection.Emit.DynamicScope"); + static readonly FieldInfo mTokensField = scopeType?.GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); + + // m_length, m_ILStream, and UpdateStackSize are on RuntimeILGenerator (the internal base class of DynamicILGenerator), + // NOT on the public ILGenerator class. Look up the fields on the correct type. + static readonly Type runtimeILGenType = ilType?.BaseType; // System.Reflection.Emit.RuntimeILGenerator + static readonly FieldInfo mLengthField = runtimeILGenType?.GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + static readonly FieldInfo mILStreamField = runtimeILGenType?.GetField("m_ILStream", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); + static readonly MethodInfo updateStackSizeMethod = runtimeILGenType?.GetMethod("UpdateStackSize", BindingFlags.Instance | BindingFlags.NonPublic); private static Func> GetScopeTokens() { + if (mScopeField == null || mTokensField == null) + return null; + var dynMethod = new DynamicMethod(string.Empty, typeof(IList), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator) }, typeof(ExpressionCompiler), skipVisibility: true); @@ -57,6 +69,8 @@ private static Func> GetScopeTokens() private static GetFieldRefDelegate CreateFieldAccessor(FieldInfo field) { + if (field == null) return null; + var dynMethod = new DynamicMethod(string.Empty, typeof(TField).MakeByRefType(), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(TFieldHolder) }, typeof(TFieldHolder), skipVisibility: true); @@ -69,14 +83,34 @@ private static GetFieldRefDelegate CreateFieldAccessor)dynMethod.CreateDelegate(typeof(GetFieldRefDelegate)); } - static GetFieldRefDelegate mLengthFieldAccessor = CreateFieldAccessor(mLengthField); - static GetFieldRefDelegate mILStreamAccessor = CreateFieldAccessor(mILStreamField); + static readonly GetFieldRefDelegate mLengthFieldAccessor = CreateFieldAccessor(mLengthField); + static readonly GetFieldRefDelegate mILStreamAccessor = CreateFieldAccessor(mILStreamField); + + static readonly Action updateStackSizeDelegate = GetUpdateStackSizeDelegate(); - static Action updateStackSizeDelegate = - (Action)Delegate.CreateDelegate(typeof(Action), null, updateStackSize); + private static Action GetUpdateStackSizeDelegate() + { + if (updateStackSizeMethod == null) return null; + // Cannot use Delegate.CreateDelegate with a method from a non-public declaring type (RuntimeILGenerator). + // Instead, wrap the call in a DynamicMethod with skipVisibility: true. + var dynMethod = new DynamicMethod(string.Empty, + typeof(void), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(ILGenerator), typeof(OpCode), typeof(int) }, + typeof(ExpressionCompiler), skipVisibility: true); + var il = dynMethod.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); // ILGenerator (runtime type: DynamicILGenerator : RuntimeILGenerator) + il.Emit(OpCodes.Ldarg_2); // OpCode + il.Emit(OpCodes.Ldarg_3); // int stackchange + il.Emit(OpCodes.Call, updateStackSizeMethod); + il.Emit(OpCodes.Ret); + return (Action)dynMethod.CreateDelegate( + typeof(Action), ExpressionCompiler.EmptyArrayClosure); + } public static Func Get_DynamicMethod_Emit_Hack() { + if (mLengthFieldAccessor == null || mILStreamAccessor == null || updateStackSizeDelegate == null || getScopeTokens == null) + return null; + var meth = MethodStatic1Arg; var paramCount = 1; @@ -128,6 +162,86 @@ public static Func Get_DynamicMethod_Emit_Hack() return (Func)dynMethod.CreateDelegate(typeof(Func), ExpressionCompiler.EmptyArrayClosure); } +#if NET10_0_OR_GREATER + // In .NET 10+, use UnsafeAccessorType to access the private fields of non-public types directly, + // without the DynamicMethod-based delegation used in earlier .NET versions. + // RuntimeILGenerator is the internal base class of DynamicILGenerator that holds the IL stream state. + + /// Directly accesses m_length on RuntimeILGenerator via UnsafeAccessorType (NET10+). + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_length")] + private static extern ref int GetMLength_Net10( + [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il); + + /// Directly accesses m_ILStream on RuntimeILGenerator via UnsafeAccessorType (NET10+). + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_ILStream")] + private static extern ref byte[] GetMILStream_Net10( + [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il); + + /// Directly calls UpdateStackSize on RuntimeILGenerator via UnsafeAccessorType (NET10+). + [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "UpdateStackSize")] + private static extern void UpdateStackSize_Net10( + [UnsafeAccessorType("System.Reflection.Emit.RuntimeILGenerator")] object il, + OpCode opcode, int stackchange); + + /// Directly accesses m_tokens on DynamicScope via UnsafeAccessorType (NET10+). + [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_tokens")] + private static extern ref List GetMTokens_Net10( + [UnsafeAccessorType("System.Reflection.Emit.DynamicScope")] object scope); + + public void DynamicMethod_Emit_Hack_Net10() + { + var f = Get_DynamicMethod_Emit_Hack_Net10(); + var a = f(41); + Asserts.AreEqual(42, a); + } + + /// + /// Demonstrates using UnsafeAccessorType (NET10+) to directly access private fields + /// of non-public types (RuntimeILGenerator, DynamicScope) for fast IL emission. + /// Replaces the DynamicMethod-based delegation approach used in earlier .NET versions. + /// + public static Func Get_DynamicMethod_Emit_Hack_Net10() + { + var meth = MethodStatic1Arg; + var paramCount = 1; + + var dynMethod = new DynamicMethod(string.Empty, + typeof(int), new[] { typeof(ExpressionCompiler.ArrayClosure), typeof(int) }, + typeof(ExpressionCompiler), + skipVisibility: true); + + var il = dynMethod.GetILGenerator(16); + + // Use UnsafeAccessorType to get refs to the internal IL stream fields directly + ref var mLength = ref GetMLength_Net10(il); + ref var mILStream = ref GetMILStream_Net10(il); + + // il.Emit(OpCodes.Ldarg_1); + mILStream[mLength++] = (byte)OpCodes.Ldarg_1.Value; + UpdateStackSize_Net10(il, OpCodes.Ldarg_1, 1); + + // il.Emit(OpCodes.Call, meth); + mILStream[mLength++] = (byte)OpCodes.Call.Value; + UpdateStackSize_Net10(il, OpCodes.Call, CalcStackChange(meth, paramCount)); + + // Access m_scope via reflection (DynamicILGenerator.m_scope returns DynamicScope which is a non-public type, + // so UnsafeAccessorType cannot currently be used for the return value). + // Then use UnsafeAccessorType to access m_tokens on the DynamicScope instance directly. + var scope = mScopeField.GetValue(il); + ref var mTokens = ref GetMTokens_Net10(scope); + mTokens.Add(meth.MethodHandle); + var token = mTokens.Count - 1 | (int)0x06000000; // MetadataTokenType.MethodDef + BinaryPrimitives.WriteInt32LittleEndian(mILStream.AsSpan(mLength), token); + mLength += 4; + + // il.Emit(OpCodes.Ret); + mILStream[mLength++] = (byte)OpCodes.Ret.Value; + UpdateStackSize_Net10(il, OpCodes.Ret, 0); + + return (Func)dynMethod.CreateDelegate(typeof(Func), ExpressionCompiler.EmptyArrayClosure); + } +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int CalcStackChange(MethodInfo meth, int paramCount) { @@ -213,15 +327,15 @@ public static Func Get_DynamicMethod_Hack_Emit_Newobj() // m_tokens.Add(rtConstructor.MethodHandle); // var tk = m_tokens.Count - 1 | (int)MetadataTokenType.MethodDef; - var mScopeField = ilType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); - if (mScopeField == null) + var mScopeFieldLocal = ilType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); + if (mScopeFieldLocal == null) return null; - var mScope = mScopeField.GetValue(il); + var mScope = mScopeFieldLocal.GetValue(il); - var mTokensField = mScope.GetType().GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); - if (mTokensField == null) + var mTokensFieldLocal = mScope.GetType().GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); + if (mTokensFieldLocal == null) return null; - var mTokens = mTokensField.GetValue(mScope); + var mTokens = mTokensFieldLocal.GetValue(mScope); il.Emit(OpCodes.Ret); diff --git a/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj b/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj index bf0aca82..20e60160 100644 --- a/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj +++ b/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj @@ -1,7 +1,7 @@  - net472;net6.0;net8.0;net9.0 - net472;net9.0 + net472;net6.0;net8.0;net9.0;net10.0 + net472;net9.0;net10.0 diff --git a/test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj b/test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj index 293d2df7..b156d30a 100644 --- a/test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj +++ b/test/FastExpressionCompiler.TestsRunner/FastExpressionCompiler.TestsRunner.csproj @@ -1,7 +1,7 @@ - net9.0;net8.0;net6.0 - net9.0 + net9.0;net8.0;net6.0;net10.0 + net9.0;net10.0 Exe false diff --git a/test/FastExpressionCompiler.TestsRunner/Program.cs b/test/FastExpressionCompiler.TestsRunner/Program.cs index b2b11cfa..17c20985 100644 --- a/test/FastExpressionCompiler.TestsRunner/Program.cs +++ b/test/FastExpressionCompiler.TestsRunner/Program.cs @@ -172,7 +172,7 @@ void Run(Func run, string name = null) var issueTests = Task.Run(() => { #if NET8_0_OR_GREATER && !LIGHT_EXPRESSION - // Run(new EmitHacksTest().Run); + Run(new EmitHacksTest().Run); #endif Run(new Issue14_String_constant_comparisons_fail().Run); Run(new LightExpression.IssueTests.Issue14_String_constant_comparisons_fail().Run); From 24115e22fb10c7f72f8dc4e51386b14773e99e5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:12:19 +0000 Subject: [PATCH 3/4] Fix null check and variable naming issues from code review in 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> --- .../EmitHacksTest.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs b/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs index e35bedba..d69ec704 100644 --- a/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs +++ b/test/FastExpressionCompiler.IssueTests/EmitHacksTest.cs @@ -227,6 +227,7 @@ public static Func Get_DynamicMethod_Emit_Hack_Net10() // Access m_scope via reflection (DynamicILGenerator.m_scope returns DynamicScope which is a non-public type, // so UnsafeAccessorType cannot currently be used for the return value). // Then use UnsafeAccessorType to access m_tokens on the DynamicScope instance directly. + if (mScopeField == null) return null; var scope = mScopeField.GetValue(il); ref var mTokens = ref GetMTokens_Net10(scope); mTokens.Add(meth.MethodHandle); @@ -327,15 +328,15 @@ public static Func Get_DynamicMethod_Hack_Emit_Newobj() // m_tokens.Add(rtConstructor.MethodHandle); // var tk = m_tokens.Count - 1 | (int)MetadataTokenType.MethodDef; - var mScopeFieldLocal = ilType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); - if (mScopeFieldLocal == null) + var scopeField = ilType.GetField("m_scope", BindingFlags.Instance | BindingFlags.NonPublic); + if (scopeField == null) return null; - var mScope = mScopeFieldLocal.GetValue(il); + var mScope = scopeField.GetValue(il); - var mTokensFieldLocal = mScope.GetType().GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); - if (mTokensFieldLocal == null) + var tokensField = mScope.GetType().GetField("m_tokens", BindingFlags.Instance | BindingFlags.NonPublic); + if (tokensField == null) return null; - var mTokens = mTokensFieldLocal.GetValue(mScope); + var mTokens = tokensField.GetValue(mScope); il.Emit(OpCodes.Ret); From c0a8a458fcadc310d3ef6d7b4cd092ee82674531 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 19:38:21 +0000 Subject: [PATCH 4/4] Add net10.0 to build.bat and CI workflow Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/c6380203-0960-4499-bfad-edb39dd3fc55 Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com> --- .github/workflows/build.yml | 3 +++ build.bat | 5 +++++ 2 files changed, 8 insertions(+) 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/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