From 25a730853c880470edf8be47fb1ba55964d29c6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Fri, 8 Sep 2023 21:07:55 -0400
Subject: [PATCH 1/7] Run tests on .NET 8.0

---
 .github/workflows/main.yml                            |  8 ++++++++
 Build/steps.yml                                       | 11 +++++++++--
 Tests/Metadata/Metadata.csproj                        |  2 +-
 .../Microsoft.Dynamic.Test.csproj                     |  2 +-
 .../Microsoft.Scripting.Test.csproj                   |  2 +-
 make.ps1                                              |  2 +-
 6 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4fdd10c4..8eb387a2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -22,6 +22,11 @@ jobs:
       uses: actions/setup-dotnet@v1
       with:
         dotnet-version: '6.0.x'
+    - name: Setup .NET 8.0
+      uses: actions/setup-dotnet@v1
+      with:
+        dotnet-version: '8.0.x'
+        include-prerelease: true
     - name: Build
       run: pwsh make.ps1
     - name: Package
@@ -39,3 +44,6 @@ jobs:
     - name: Test (net6.0)
       run: ./make.ps1 -frameworks net6.0 test-all
       shell: pwsh
+    - name: Test (net8.0)
+      run: ./make.ps1 -frameworks net8.0 test-all
+      shell: pwsh
diff --git a/Build/steps.yml b/Build/steps.yml
index 0b09ce1e..fef07f0f 100644
--- a/Build/steps.yml
+++ b/Build/steps.yml
@@ -28,11 +28,18 @@ steps:
       version: '3.1.x'
 
   - task: UseDotNet@2
-    displayName: Install .NET 6.0 SDK for build
+    displayName: Install .NET 6.0 runtime for testing
     inputs:
-      packageType: 'sdk'
+      packageType: 'runtime'
       version: '6.0.x'
 
+  - task: UseDotNet@2
+    displayName: Install .NET 8.0 SDK for build
+    inputs:
+      packageType: 'sdk'
+      version: '8.0.x'
+      includePreviewVersions: true
+
   # Set Mono version on macOS
   - ${{ if eq(parameters.os, 'macOS') }}:
     - task: ms-devlabs.utilitytasks.task-Shellpp.Shell++@0
diff --git a/Tests/Metadata/Metadata.csproj b/Tests/Metadata/Metadata.csproj
index 4d7374f9..ae90f462 100644
--- a/Tests/Metadata/Metadata.csproj
+++ b/Tests/Metadata/Metadata.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
     <OutputType>Exe</OutputType>
diff --git a/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj b/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
index 39eef772..a244b8e5 100644
--- a/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
+++ b/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
diff --git a/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj b/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
index 59cf05a4..3fd1c99c 100644
--- a/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
+++ b/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
diff --git a/make.ps1 b/make.ps1
index 0deaa250..d41161f2 100755
--- a/make.ps1
+++ b/make.ps1
@@ -4,7 +4,7 @@ Param(
     [Parameter(Position=1)]
     [String] $target = "release",
     [String] $configuration = "Release",
-    [String[]] $frameworks=@('net462','netcoreapp3.1','net6.0'),
+    [String[]] $frameworks=@('net462','netcoreapp3.1','net6.0','net8.0'),
     [String] $platform = "x64",
     [switch] $runIgnored
 )

From 1972d82d93f7dfac59c5fd577a925b7d09f7f36d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:22:47 -0400
Subject: [PATCH 2/7] Add .NET 8 target

---
 .editorconfig                                 |  9 ++++-
 .github/workflows/main.yml                    |  1 -
 Build.proj                                    |  2 +-
 Build/net8.0.props                            | 37 +++++++++++++++++++
 Dlr.sln                                       |  1 +
 Package/nuget/DynamicLanguageRuntime.nuspec   |  3 ++
 .../Actions/Calls/ArgumentBinding.cs          |  2 +-
 .../Actions/MemberTracker.cs                  |  2 +-
 .../Actions/NamespaceTracker.cs               | 22 +++++------
 .../ComInterop/ComInterop.cs                  |  4 ++
 .../ComInterop/ComParamDesc.cs                |  2 +-
 .../ComInterop/ComRuntimeHelpers.cs           |  8 ++--
 .../ComInterop/ComTypeEnumDesc.cs             |  2 +-
 Src/Microsoft.Dynamic/ComInterop/ExcepInfo.cs |  4 +-
 .../ComInterop/IDispatchComObject.cs          |  2 +-
 Src/Microsoft.Dynamic/ComInterop/Variant.cs   |  4 +-
 .../Debugging/ThreadLocal.cs                  |  2 +-
 .../Hosting/Shell/ConsoleHostOptionsParser.cs |  4 +-
 .../Interpreter/LightCompiler.cs              |  2 +-
 .../Microsoft.Dynamic.csproj                  |  2 +-
 .../Runtime/SavableScriptCode.cs              |  2 +-
 Src/Microsoft.Dynamic/SerializationStubs.cs   | 31 ++++++++++++++++
 Src/Microsoft.Dynamic/StringExtensions.cs     | 19 ++++++++++
 .../Utils/CollectionExtensions.cs             |  4 +-
 .../Utils/ReflectionUtils.cs                  |  2 +-
 Src/Microsoft.Dynamic/Utils/ThreadLocal.cs    |  2 +-
 Src/Microsoft.Dynamic/Utils/WeakDictionary.cs |  4 +-
 .../Microsoft.Scripting.csproj                |  9 ++++-
 .../Runtime/LanguageContext.cs                |  2 +-
 .../Runtime/ScopeExtension.cs                 |  2 +-
 Src/Microsoft.Scripting/Stubs.cs              | 32 ----------------
 .../Utils/CollectionExtensions.cs             |  4 +-
 Tests/Metadata/TypeNestings.cs                |  2 +-
 .../Microsoft.Dynamic.Test/InterpreterTest.cs |  2 +-
 34 files changed, 152 insertions(+), 80 deletions(-)
 create mode 100644 Build/net8.0.props
 create mode 100644 Src/Microsoft.Dynamic/SerializationStubs.cs
 create mode 100644 Src/Microsoft.Dynamic/StringExtensions.cs
 delete mode 100644 Src/Microsoft.Scripting/Stubs.cs

diff --git a/.editorconfig b/.editorconfig
index 40cdffa5..391bcc01 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -57,6 +57,9 @@ dotnet_diagnostic.CA1305.severity = none        # CA1305: Specify IFormatProvide
 dotnet_diagnostic.CA1307.severity = suggestion  # CA1307: Specify StringComparison for clarity
 dotnet_diagnostic.CA1309.severity = suggestion  # CA1309: Use ordinal string comparison
 dotnet_diagnostic.CA1310.severity = warning     # CA1310: Specify StringComparison for correctness
+dotnet_diagnostic.CA1510.severity = none        # CA1510: Use ArgumentNullException throw helper
+dotnet_diagnostic.CA1512.severity = none        # CA1512: Use ArgumentOutOfRangeException throw helper
+dotnet_diagnostic.CA1513.severity = none        # CA1513: Use ObjectDisposedException throw helper
 dotnet_diagnostic.CA1707.severity = none        # CA1707: Identifiers should not contain underscores
 dotnet_diagnostic.CA1708.severity = none        # CA1708: Identifiers should differ by more than case
 dotnet_diagnostic.CA1710.severity = none        # CA1710: Identifiers should have correct suffix
@@ -70,7 +73,6 @@ dotnet_diagnostic.CA1805.severity = suggestion  # CA1805: Do not initialize unne
 dotnet_diagnostic.CA1806.severity = none        # CA1806: Do not ignore method results
 dotnet_diagnostic.CA1816.severity = suggestion  # CA1816: Dispose methods should call SuppressFinalize
 dotnet_diagnostic.CA1822.severity = none        # CA1822: Mark members as static
-dotnet_diagnostic.CA1825.severity = none        # CA1825: Avoid zero-length array allocations
 dotnet_diagnostic.CA1830.severity = suggestion  # CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder
 dotnet_diagnostic.CA1834.severity = suggestion  # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
 dotnet_diagnostic.CA1837.severity = suggestion  # CA1837: Use 'Environment.ProcessId'
@@ -79,9 +81,9 @@ dotnet_diagnostic.CA1845.severity = none        # CA1845: Use span-based 'string
 dotnet_diagnostic.CA1846.severity = none        # CA1846: Prefer 'AsSpan' over 'Substring'
 dotnet_diagnostic.CA1847.severity = none        # CA1847: Use char literal for a single character lookup
 dotnet_diagnostic.CA1852.severity = suggestion  # CA1852: Seal internal types
-dotnet_diagnostic.CA1854.severity = suggestion  # CA1854: Prefer the 'IDictionary.TryGetValue(TKey, out TValue)' method
 dotnet_diagnostic.CA1859.severity = suggestion  # CA1859: Use concrete types when possible for improved performance
 dotnet_diagnostic.CA1861.severity = suggestion  # CA1861: Avoid constant arrays as arguments
+dotnet_diagnostic.CA1863.severity = none        # CA1863: Use 'CompositeFormat'
 dotnet_diagnostic.CA2101.severity = suggestion  # CA2101: Specify marshaling for P/Invoke string arguments
 dotnet_diagnostic.CA2201.severity = none        # CA2201: Do not raise reserved exception types
 dotnet_diagnostic.CA2208.severity = suggestion  # CA2208: Instantiate argument exceptions correctly
@@ -94,3 +96,6 @@ dotnet_diagnostic.CA5350.severity = suggestion  # CA5350: Do Not Use Weak Crypto
 dotnet_diagnostic.CA5351.severity = suggestion  # CA5351: Do Not Use Broken Cryptographic Algorithms
 dotnet_diagnostic.CA5359.severity = suggestion  # CA5359: Do Not Disable Certificate Validation
 dotnet_diagnostic.CA5372.severity = suggestion  # CA5372: Use XmlReader For XPathDocument
+
+dotnet_diagnostic.SYSLIB1045.severity = suggestion # SYSLIB1045: Use 'RegexGeneratorAttribute' to generate the regular expression implementation at compile-time
+dotnet_diagnostic.SYSLIB1054.severity = suggestion # SYSLIB1054: Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8eb387a2..73b4ba7f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -26,7 +26,6 @@ jobs:
       uses: actions/setup-dotnet@v1
       with:
         dotnet-version: '8.0.x'
-        include-prerelease: true
     - name: Build
       run: pwsh make.ps1
     - name: Package
diff --git a/Build.proj b/Build.proj
index 1b81dbec..6ba917cf 100644
--- a/Build.proj
+++ b/Build.proj
@@ -108,7 +108,7 @@
           Outputs="$(PackageDir)\DynamicLanguageRuntime.$(PackageVersion).zip">
     <ItemGroup>
       <ZipFiles Include="$(StageDir)\**\*.dll;$(StageDir)\**\*.xml;$(StageDir)\README.md;$(StageDir)\LICENSE"
-                Exclude="$(StageDir)\netcoreapp3.1\*;$(StageDir)\net7.0*\*;$(StageDir)\net8.0*\*" />
+                Exclude="$(StageDir)\netcoreapp3.1\*;$(StageDir)\net7.0*\*;$(StageDir)\net8.0*\*;$(StageDir)\net9.0*\*" />
     </ItemGroup>
     <Message Text="$(ZipFiles)" />
     <Zip Files="@(ZipFiles)" ZipFileName="$(PackageDir)\DynamicLanguageRuntime.$(PackageVersion).zip" WorkingDirectory="$(StageDir)"/>
diff --git a/Build/net8.0.props b/Build/net8.0.props
new file mode 100644
index 00000000..97529b54
--- /dev/null
+++ b/Build/net8.0.props
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <IsFullFramework>false</IsFullFramework>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <Features>$(Features);FEATURE_APARTMENTSTATE</Features>
+    <Features>$(Features);FEATURE_ASSEMBLY_GETFORWARDEDTYPES</Features>
+    <Features>$(Features);FEATURE_ASSEMBLY_RESOLVE</Features>
+    <Features>$(Features);FEATURE_ASSEMBLYBUILDER_DEFINEDYNAMICASSEMBLY</Features>
+    <Features>$(Features);FEATURE_CODEDOM</Features>
+    <Features>$(Features);FEATURE_COM</Features>
+    <Features>$(Features);FEATURE_CONFIGURATION</Features>
+    <Features>$(Features);FEATURE_CUSTOM_TYPE_DESCRIPTOR</Features>
+    <Features>$(Features);FEATURE_EXCEPTION_STATE</Features>
+    <Features>$(Features);FEATURE_FILESYSTEM</Features>
+    <Features>$(Features);FEATURE_FULL_CRYPTO</Features>
+    <Features>$(Features);FEATURE_FULL_NET</Features>
+    <Features>$(Features);FEATURE_LCG</Features>
+    <Features>$(Features);FEATURE_LOADWITHPARTIALNAME</Features>
+    <Features>$(Features);FEATURE_METADATA_READER</Features>
+    <Features>$(Features);FEATURE_MMAP</Features>
+    <Features>$(Features);FEATURE_NATIVE</Features>
+    <Features>$(Features);FEATURE_PIPES</Features>
+    <Features>$(Features);FEATURE_PROCESS</Features>
+    <Features>$(Features);FEATURE_REFEMIT</Features>
+    <Features>$(Features);FEATURE_REGISTRY</Features>
+    <Features>$(Features);FEATURE_SECURITY_RULES</Features>
+    <Features>$(Features);FEATURE_STACK_TRACE</Features>
+    <Features>$(Features);FEATURE_SYNC_SOCKETS</Features>
+    <Features>$(Features);FEATURE_THREAD</Features>
+    <Features>$(Features);FEATURE_TYPE_EQUIVALENCE</Features>
+    <Features>$(Features);FEATURE_TYPECONVERTER</Features>
+    <Features>$(Features);FEATURE_XMLDOC</Features>
+  </PropertyGroup>
+</Project>
diff --git a/Dlr.sln b/Dlr.sln
index a371de9b..de18fe0e 100644
--- a/Dlr.sln
+++ b/Dlr.sln
@@ -37,6 +37,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{60056F49
 		Build\After.targets = Build\After.targets
 		Build\net462.props = Build\net462.props
 		Build\net6.0.props = Build\net6.0.props
+		Build\net8.0.props = Build\net8.0.props
 		Build\netstandard2.0.props = Build\netstandard2.0.props
 		Build\steps.yml = Build\steps.yml
 	EndProjectSection
diff --git a/Package/nuget/DynamicLanguageRuntime.nuspec b/Package/nuget/DynamicLanguageRuntime.nuspec
index f89f6e54..970b0204 100644
--- a/Package/nuget/DynamicLanguageRuntime.nuspec
+++ b/Package/nuget/DynamicLanguageRuntime.nuspec
@@ -26,6 +26,9 @@
       <group targetFramework="net6.0">
         <dependency id="System.CodeDom" version="6.0.0" />
       </group>
+      <group targetFramework="net8.0">
+        <dependency id="System.CodeDom" version="8.0.0" />
+      </group>
     </dependencies>
   </metadata>
   <files>
diff --git a/Src/Microsoft.Dynamic/Actions/Calls/ArgumentBinding.cs b/Src/Microsoft.Dynamic/Actions/Calls/ArgumentBinding.cs
index 082682b7..edea0c67 100644
--- a/Src/Microsoft.Dynamic/Actions/Calls/ArgumentBinding.cs
+++ b/Src/Microsoft.Dynamic/Actions/Calls/ArgumentBinding.cs
@@ -7,7 +7,7 @@
 namespace Microsoft.Scripting.Actions.Calls {
     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] // TODO
     public struct ArgumentBinding {
-        private static readonly int[] _EmptyBinding = new int[0];
+        private static readonly int[] _EmptyBinding = System.Array.Empty<int>();
 
         private readonly int _positionalArgCount;
         private readonly int[] _binding; // immutable
diff --git a/Src/Microsoft.Dynamic/Actions/MemberTracker.cs b/Src/Microsoft.Dynamic/Actions/MemberTracker.cs
index c9f06f0e..a9e6ec7b 100644
--- a/Src/Microsoft.Dynamic/Actions/MemberTracker.cs
+++ b/Src/Microsoft.Dynamic/Actions/MemberTracker.cs
@@ -24,7 +24,7 @@ namespace Microsoft.Scripting.Actions {
     /// </summary>
     public abstract class MemberTracker {
         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
-        public static readonly MemberTracker[] EmptyTrackers = new MemberTracker[0];
+        public static readonly MemberTracker[] EmptyTrackers = Array.Empty<MemberTracker>();
 
         private static readonly Dictionary<MemberKey, MemberTracker> _trackers = new Dictionary<MemberKey, MemberTracker>();
 
diff --git a/Src/Microsoft.Dynamic/Actions/NamespaceTracker.cs b/Src/Microsoft.Dynamic/Actions/NamespaceTracker.cs
index eea45589..5c76c1c0 100644
--- a/Src/Microsoft.Dynamic/Actions/NamespaceTracker.cs
+++ b/Src/Microsoft.Dynamic/Actions/NamespaceTracker.cs
@@ -54,7 +54,7 @@ internal NamespaceTracker GetOrMakeChildPackage(string childName, Assembly assem
 
             if (_dict.TryGetValue(childName, out MemberTracker ret)) {
                 // If we have a module, then we add the assembly to the InnerModule
-                // If it's not a module, we'll wipe it out below, eg "def System(): pass" then 
+                // If it's not a module, we'll wipe it out below, eg "def System(): pass" then
                 // "import System" will result in the namespace being visible.
                 if (ret is NamespaceTracker package) {
                     if (!package._packageAssemblies.Contains(assem)) {
@@ -102,10 +102,11 @@ internal void AddTypeName(string typeName, Assembly assem) {
             Assert.NotNull(typeName, assem);
             Debug.Assert(typeName.IndexOf('.') == -1); // This is the simple name, not the full name
 
-            if (!_typeNames.ContainsKey(assem)) {
-                _typeNames[assem] = new TypeNames(assem, _fullName);
+            if (!_typeNames.TryGetValue(assem, out TypeNames typeNames)) {
+                typeNames = new TypeNames(assem, _fullName);
+                _typeNames[assem] = typeNames;
             }
-            _typeNames[assem].AddTypeName(typeName);
+            typeNames.AddTypeName(typeName);
 
             string normalizedTypeName = ReflectionUtils.GetNormalizedTypeName(typeName);
             if (_dict.ContainsKey(normalizedTypeName)) {
@@ -194,9 +195,9 @@ private NamespaceTracker GetOrMakePackageHierarchy(Assembly assem, string fullNa
         }
         /// <summary>
         /// As a fallback, so if the type does exist in any assembly. This would happen if a new type was added
-        /// that was not in the hardcoded list of types. 
+        /// that was not in the hardcoded list of types.
         /// This code is not accurate because:
-        /// 1. We dont deal with generic types (TypeCollision). 
+        /// 1. We dont deal with generic types (TypeCollision).
         /// 2. Previous calls to GetCustomMemberNames (eg. "from foo import *" in Python) would not have included this type.
         /// 3. This does not deal with new namespaces added to the assembly
         /// </summary>
@@ -215,7 +216,7 @@ private MemberTracker CheckForUnlistedType(string nameString) {
                     continue;
                 }
 
-                // We dont use TypeCollision.UpdateTypeEntity here because we do not handle generic type names                    
+                // We dont use TypeCollision.UpdateTypeEntity here because we do not handle generic type names
                 return TypeTracker.GetTypeTracker(type);
             }
 
@@ -309,7 +310,7 @@ private IList AddKeys(IList res) {
                     }
                 }
             }
-            
+
             return res;
         }
 
@@ -405,10 +406,7 @@ internal void AddTypeName(string typeName) {
                 if (normalizedName == typeName) {
                     _simpleTypeNames.Add(typeName);
                 } else {
-                    List<string> actualNames;
-                    if (_genericTypeNames.ContainsKey(normalizedName)) {
-                        actualNames = _genericTypeNames[normalizedName];
-                    } else {
+                    if (!_genericTypeNames.TryGetValue(normalizedName, out List<string> actualNames)) {
                         actualNames = new List<string>();
                         _genericTypeNames[normalizedName] = actualNames;
                     }
diff --git a/Src/Microsoft.Dynamic/ComInterop/ComInterop.cs b/Src/Microsoft.Dynamic/ComInterop/ComInterop.cs
index 0dcff69c..55c26dd3 100644
--- a/Src/Microsoft.Dynamic/ComInterop/ComInterop.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/ComInterop.cs
@@ -25,7 +25,9 @@ internal interface IDispatchForReflection {
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
     Guid("00020400-0000-0000-C000-000000000046"),
     ]
+#pragma warning disable SYSLIB1096 // Convert to 'GeneratedComInterface'
     internal interface IDispatch {
+#pragma warning restore SYSLIB1096 // Convert to 'GeneratedComInterface'
 
         [PreserveSig]
         int TryGetTypeInfoCount(out uint pctinfo);
@@ -75,7 +77,9 @@ internal enum IDispatchMethodIndices {
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
     Guid("B196B283-BAB4-101A-B69C-00AA00341D07")
     ]
+#pragma warning disable SYSLIB1096 // Convert to 'GeneratedComInterface'
     internal interface IProvideClassInfo {
+#pragma warning restore SYSLIB1096 // Convert to 'GeneratedComInterface'
         void GetClassInfo(out IntPtr info);
     }
 
diff --git a/Src/Microsoft.Dynamic/ComInterop/ComParamDesc.cs b/Src/Microsoft.Dynamic/ComInterop/ComParamDesc.cs
index 41f92086..35b2f917 100644
--- a/Src/Microsoft.Dynamic/ComInterop/ComParamDesc.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/ComParamDesc.cs
@@ -60,7 +60,7 @@ internal ComParamDesc(ref ELEMDESC elemDesc, string name) {
                     break;
                 }
 
-                TYPEDESC childTypeDesc = (TYPEDESC)Marshal.PtrToStructure(typeDesc.lpValue, typeof(TYPEDESC));
+                TYPEDESC childTypeDesc = Marshal.PtrToStructure<TYPEDESC>(typeDesc.lpValue);
                 _vt = (VarEnum)childTypeDesc.vt;
                 typeDesc = childTypeDesc;
             }
diff --git a/Src/Microsoft.Dynamic/ComInterop/ComRuntimeHelpers.cs b/Src/Microsoft.Dynamic/ComInterop/ComRuntimeHelpers.cs
index 9ac8902a..24f0bbbb 100644
--- a/Src/Microsoft.Dynamic/ComInterop/ComRuntimeHelpers.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/ComRuntimeHelpers.cs
@@ -186,7 +186,7 @@ internal static ComTypes.TYPEATTR GetTypeAttrForTypeInfo(ComTypes.ITypeInfo type
             }
 
             try {
-                return (ComTypes.TYPEATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPEATTR));
+                return Marshal.PtrToStructure<ComTypes.TYPEATTR>(pAttrs);
             } finally {
                 typeInfo.ReleaseTypeAttr(pAttrs);
             }
@@ -203,7 +203,7 @@ internal static ComTypes.TYPELIBATTR GetTypeAttrForTypeLib(ComTypes.ITypeLib typ
             }
 
             try {
-                return (ComTypes.TYPELIBATTR)Marshal.PtrToStructure(pAttrs, typeof(ComTypes.TYPELIBATTR));
+                return Marshal.PtrToStructure<ComTypes.TYPELIBATTR>(pAttrs);
             } finally {
                 typeLib.ReleaseTLibAttr(pAttrs);
             }
@@ -541,7 +541,7 @@ private static IUnknownReleaseDelegate Create_IUnknownRelease() {
             method.Emit(OpCodes.Ldarg_0);
 
             // functionPtr = *(IntPtr*)(*(interfacePointer) + VTABLE_OFFSET)
-            int iunknownReleaseOffset = ((int)IDispatchMethodIndices.IUnknown_Release) * Marshal.SizeOf(typeof(IntPtr));
+            int iunknownReleaseOffset = ((int)IDispatchMethodIndices.IUnknown_Release) * Marshal.SizeOf<IntPtr>();
             method.Emit(OpCodes.Ldarg_0);
             method.Emit(OpCodes.Ldind_I);
             method.Emit(OpCodes.Ldc_I4, iunknownReleaseOffset);
@@ -653,7 +653,7 @@ private static IDispatchInvokeDelegate Create_IDispatchInvoke(bool returnResult)
             EmitLoadArg(method, argErrIndex);
 
             // functionPtr = *(IntPtr*)(*(dispatchPointer) + VTABLE_OFFSET)
-            int idispatchInvokeOffset = ((int)IDispatchMethodIndices.IDispatch_Invoke) * Marshal.SizeOf(typeof(IntPtr));
+            int idispatchInvokeOffset = ((int)IDispatchMethodIndices.IDispatch_Invoke) * Marshal.SizeOf<IntPtr>();
             EmitLoadArg(method, dispatchPointerIndex);
             method.Emit(OpCodes.Ldind_I);
             method.Emit(OpCodes.Ldc_I4, idispatchInvokeOffset);
diff --git a/Src/Microsoft.Dynamic/ComInterop/ComTypeEnumDesc.cs b/Src/Microsoft.Dynamic/ComInterop/ComTypeEnumDesc.cs
index 0983ae83..f3f00627 100644
--- a/Src/Microsoft.Dynamic/ComInterop/ComTypeEnumDesc.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/ComTypeEnumDesc.cs
@@ -40,7 +40,7 @@ internal ComTypeEnumDesc(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc
                 ComTypes.VARDESC varDesc;
 
                 try {
-                    varDesc = (ComTypes.VARDESC)Marshal.PtrToStructure(p, typeof(ComTypes.VARDESC));
+                    varDesc = Marshal.PtrToStructure<ComTypes.VARDESC>(p);
 
                     if (varDesc.varkind == ComTypes.VARKIND.VAR_CONST) {
                         memberValues[i] = Marshal.GetObjectForNativeVariant(varDesc.desc.lpvarValue);
diff --git a/Src/Microsoft.Dynamic/ComInterop/ExcepInfo.cs b/Src/Microsoft.Dynamic/ComInterop/ExcepInfo.cs
index c344eebe..65110aa4 100644
--- a/Src/Microsoft.Dynamic/ComInterop/ExcepInfo.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/ExcepInfo.cs
@@ -31,7 +31,7 @@ internal struct ExcepInfo {
 #if DEBUG
         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2207:InitializeValueTypeStaticFieldsInline")]
         static ExcepInfo() {
-            Debug.Assert(Marshal.SizeOf(typeof(ExcepInfo)) == Marshal.SizeOf(typeof(ComTypes.EXCEPINFO)));
+            Debug.Assert(Marshal.SizeOf<ExcepInfo>() == Marshal.SizeOf<ComTypes.EXCEPINFO>());
         }
 #endif
 
@@ -99,4 +99,4 @@ internal Exception GetException() {
     }
 }
 
-#endif
\ No newline at end of file
+#endif
diff --git a/Src/Microsoft.Dynamic/ComInterop/IDispatchComObject.cs b/Src/Microsoft.Dynamic/ComInterop/IDispatchComObject.cs
index f2d7bada..7dc37142 100644
--- a/Src/Microsoft.Dynamic/ComInterop/IDispatchComObject.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/IDispatchComObject.cs
@@ -335,7 +335,7 @@ private static void GetFuncDescForDescIndex(ComTypes.ITypeInfo typeInfo, int fun
                 throw Error.CannotRetrieveTypeInformation();
             }
 
-            funcDesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC));
+            funcDesc = Marshal.PtrToStructure<ComTypes.FUNCDESC>(pFuncDesc);
             funcDescHandle = pFuncDesc;
         }
 
diff --git a/Src/Microsoft.Dynamic/ComInterop/Variant.cs b/Src/Microsoft.Dynamic/ComInterop/Variant.cs
index cc6a6894..53d13605 100644
--- a/Src/Microsoft.Dynamic/ComInterop/Variant.cs
+++ b/Src/Microsoft.Dynamic/ComInterop/Variant.cs
@@ -34,8 +34,8 @@ internal struct Variant {
         static Variant() {
             // Variant size is the size of 4 pointers (16 bytes) on a 32-bit processor,
             // and 3 pointers (24 bytes) on a 64-bit processor.
-            int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
-            int variantSize = Marshal.SizeOf(typeof(Variant));
+            int intPtrSize = Marshal.SizeOf<IntPtr>();
+            int variantSize = Marshal.SizeOf<Variant>();
             if (intPtrSize == 4) {
                 Debug.Assert(variantSize == (4 * intPtrSize));
             } else {
diff --git a/Src/Microsoft.Dynamic/Debugging/ThreadLocal.cs b/Src/Microsoft.Dynamic/Debugging/ThreadLocal.cs
index e7927459..1cba52d2 100644
--- a/Src/Microsoft.Dynamic/Debugging/ThreadLocal.cs
+++ b/Src/Microsoft.Dynamic/Debugging/ThreadLocal.cs
@@ -10,7 +10,7 @@
 namespace Microsoft.Scripting.Debugging {
     internal class ThreadLocal<T> {
         private StorageInfo[] _stores;                                         // array of storage indexed by managed thread ID
-        private static readonly StorageInfo[] Updating = new StorageInfo[0];   // a marker used when updating the array
+        private static readonly StorageInfo[] Updating = Array.Empty<StorageInfo>();   // a marker used when updating the array
 
         internal T Value {
             get {
diff --git a/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs b/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
index f24d1fdf..84892902 100644
--- a/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
+++ b/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
@@ -110,8 +110,8 @@ private void ParseOption(string arg, out string name, out string value) {
             }
 
             if (name.StartsWith("--", StringComparison.Ordinal)) name = name.Substring("--".Length);
-            else if (name.StartsWith("-", StringComparison.Ordinal) && name.Length > 1) name = name.Substring("-".Length);
-            else if (name.StartsWith("/", StringComparison.Ordinal) && name.Length > 1) name = name.Substring("/".Length);
+            else if (name.StartsWith('-') && name.Length > 1) name = name.Substring("-".Length);
+            else if (name.StartsWith('/') && name.Length > 1) name = name.Substring("/".Length);
             else {
                 value = name;
                 name = null;
diff --git a/Src/Microsoft.Dynamic/Interpreter/LightCompiler.cs b/Src/Microsoft.Dynamic/Interpreter/LightCompiler.cs
index 724dc915..f610b4f3 100644
--- a/Src/Microsoft.Dynamic/Interpreter/LightCompiler.cs
+++ b/Src/Microsoft.Dynamic/Interpreter/LightCompiler.cs
@@ -164,7 +164,7 @@ public sealed class LightCompiler {
 
         private readonly LightCompiler _parent;
 
-        private static readonly LocalDefinition[] EmptyLocals = new LocalDefinition[0];
+        private static readonly LocalDefinition[] EmptyLocals = Array.Empty<LocalDefinition>();
 
         internal LightCompiler(int compilationThreshold) {
             Instructions = new InstructionList();
diff --git a/Src/Microsoft.Dynamic/Microsoft.Dynamic.csproj b/Src/Microsoft.Dynamic/Microsoft.Dynamic.csproj
index 402d7ddd..2590bf12 100644
--- a/Src/Microsoft.Dynamic/Microsoft.Dynamic.csproj
+++ b/Src/Microsoft.Dynamic/Microsoft.Dynamic.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netstandard2.0;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0;net6.0;net8.0</TargetFrameworks>
     <RootNamespace>Microsoft.Scripting</RootNamespace>
     <BaseAddress>859832320</BaseAddress>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
diff --git a/Src/Microsoft.Dynamic/Runtime/SavableScriptCode.cs b/Src/Microsoft.Dynamic/Runtime/SavableScriptCode.cs
index 4a165676..3f83745c 100644
--- a/Src/Microsoft.Dynamic/Runtime/SavableScriptCode.cs
+++ b/Src/Microsoft.Dynamic/Runtime/SavableScriptCode.cs
@@ -173,7 +173,7 @@ public static ScriptCode[] LoadFromAssembly(ScriptDomainManager runtime, Assembl
             // get the type which has our cached code...
             Type t = assembly.GetType("DLRCachedCode");
             if (t == null) {
-                return new ScriptCode[0];
+                return Array.Empty<ScriptCode>();
             }
 
             List<ScriptCode> codes = new List<ScriptCode>();
diff --git a/Src/Microsoft.Dynamic/SerializationStubs.cs b/Src/Microsoft.Dynamic/SerializationStubs.cs
new file mode 100644
index 00000000..b460fbfb
--- /dev/null
+++ b/Src/Microsoft.Dynamic/SerializationStubs.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 license.
+// See the LICENSE file in the project root for more information.
+
+#if !FEATURE_SERIALIZATION
+
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.Scripting {
+    [Conditional("STUB")]
+    internal class SerializableAttribute : Attribute {
+    }
+
+    [Conditional("STUB")]
+    internal class NonSerializedAttribute : Attribute {
+    }
+
+    namespace Runtime {
+        internal interface ISerializable {
+        }
+
+        internal interface IDeserializationCallback {
+        }
+    }
+
+    internal class SerializationException : Exception {
+    }
+}
+
+#endif
diff --git a/Src/Microsoft.Dynamic/StringExtensions.cs b/Src/Microsoft.Dynamic/StringExtensions.cs
new file mode 100644
index 00000000..eb5373da
--- /dev/null
+++ b/Src/Microsoft.Dynamic/StringExtensions.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Microsoft.Scripting
+{
+    internal static class StringExtensions
+    {
+#if !NETCOREAPP
+        public static bool EndsWith(this string str, char value)
+        {
+            return str.EndsWith(value.ToString(), StringComparison.Ordinal);
+        }
+
+        public static bool StartsWith(this string str, char value)
+        {
+            return str.StartsWith(value.ToString(), StringComparison.Ordinal);
+        }
+#endif
+    }
+}
diff --git a/Src/Microsoft.Dynamic/Utils/CollectionExtensions.cs b/Src/Microsoft.Dynamic/Utils/CollectionExtensions.cs
index a7eb9a4b..25b6c52a 100644
--- a/Src/Microsoft.Dynamic/Utils/CollectionExtensions.cs
+++ b/Src/Microsoft.Dynamic/Utils/CollectionExtensions.cs
@@ -138,7 +138,7 @@ internal static T[] RotateRight<T>(this T[] array, int count) {
 
 
     internal static class EmptyReadOnlyCollection<T> {
-        internal static readonly ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new T[0]);
+        internal static readonly ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(Array.Empty<T>());
     }
 
     internal static class EmptyReadOnlyDictionary<TKey, TValue> {
@@ -151,6 +151,6 @@ internal static readonly IReadOnlyDictionary<TKey, TValue> Instance
     }
 
     internal static class EmptyArray<T> {
-        internal static readonly T[] Instance = new T[0];
+        internal static readonly T[] Instance = Array.Empty<T>();
     }
 }
diff --git a/Src/Microsoft.Dynamic/Utils/ReflectionUtils.cs b/Src/Microsoft.Dynamic/Utils/ReflectionUtils.cs
index d19802f4..393aa6fa 100644
--- a/Src/Microsoft.Dynamic/Utils/ReflectionUtils.cs
+++ b/Src/Microsoft.Dynamic/Utils/ReflectionUtils.cs
@@ -615,7 +615,7 @@ public static GenericParameterAttributes GetGenericParameterAttributes(this Type
             return type.GenericParameterAttributes;
         }
 
-        public static readonly Type[] EmptyTypes = new Type[0];
+        public static readonly Type[] EmptyTypes = Array.Empty<TypeInfo>();
 
         public static object GetRawConstantValue(this FieldInfo field) {
             if (!field.IsLiteral) {
diff --git a/Src/Microsoft.Dynamic/Utils/ThreadLocal.cs b/Src/Microsoft.Dynamic/Utils/ThreadLocal.cs
index d624449c..ba7b55a7 100644
--- a/Src/Microsoft.Dynamic/Utils/ThreadLocal.cs
+++ b/Src/Microsoft.Dynamic/Utils/ThreadLocal.cs
@@ -13,7 +13,7 @@ namespace Microsoft.Scripting.Utils {
     /// </summary>
     public class ThreadLocal<T> {
         private StorageInfo[] _stores;                                         // array of storage indexed by managed thread ID
-        private static readonly StorageInfo[] Updating = new StorageInfo[0];   // a marker used when updating the array
+        private static readonly StorageInfo[] Updating = Array.Empty<StorageInfo>();   // a marker used when updating the array
         private readonly bool _refCounted;
 
         public ThreadLocal() {
diff --git a/Src/Microsoft.Dynamic/Utils/WeakDictionary.cs b/Src/Microsoft.Dynamic/Utils/WeakDictionary.cs
index 5b15db56..3f477dff 100644
--- a/Src/Microsoft.Dynamic/Utils/WeakDictionary.cs
+++ b/Src/Microsoft.Dynamic/Utils/WeakDictionary.cs
@@ -35,7 +35,7 @@ public class WeakDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
 
         static WeakDictionary()
         {
-            valueConstructor = typeof(TValue).GetConstructor(new Type[] { });
+            valueConstructor = typeof(TValue).GetConstructor(Array.Empty<Type>());
         }
 
         #region IDictionary<TKey,TValue> Members
@@ -78,7 +78,7 @@ public TValue GetOrCreateValue(TKey key) {
                     throw new InvalidOperationException($"{typeof(TValue).Name} does not have a default constructor.");
                 }
             
-                value = (TValue)valueConstructor.Invoke(new object[] { });
+                value = (TValue)valueConstructor.Invoke(Array.Empty<object>());
                 Add(key, value);
             }
 
diff --git a/Src/Microsoft.Scripting/Microsoft.Scripting.csproj b/Src/Microsoft.Scripting/Microsoft.Scripting.csproj
index 358d3dd6..4be4bff9 100644
--- a/Src/Microsoft.Scripting/Microsoft.Scripting.csproj
+++ b/Src/Microsoft.Scripting/Microsoft.Scripting.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netstandard2.0;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0;net6.0;net8.0</TargetFrameworks>
     <BaseAddress>857735168</BaseAddress>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>
@@ -25,6 +25,13 @@
     <PackageReference Include="System.CodeDom" Version="6.0.0" />
   </ItemGroup>
 
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
+    <PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0">
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
+    <PackageReference Include="System.CodeDom" Version="8.0.0" />
+  </ItemGroup>
+
   <ItemGroup>
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
   </ItemGroup>
diff --git a/Src/Microsoft.Scripting/Runtime/LanguageContext.cs b/Src/Microsoft.Scripting/Runtime/LanguageContext.cs
index 114ab419..13e3d48d 100644
--- a/Src/Microsoft.Scripting/Runtime/LanguageContext.cs
+++ b/Src/Microsoft.Scripting/Runtime/LanguageContext.cs
@@ -256,7 +256,7 @@ public virtual string FormatException(Exception exception) {
         }
 
         public virtual IList<DynamicStackFrame> GetStackFrames(Exception exception) {
-            return new DynamicStackFrame[0];
+            return Array.Empty<DynamicStackFrame>();
         }
         
         public virtual LanguageOptions Options => new LanguageOptions();
diff --git a/Src/Microsoft.Scripting/Runtime/ScopeExtension.cs b/Src/Microsoft.Scripting/Runtime/ScopeExtension.cs
index d47d8ae3..f1e064a5 100644
--- a/Src/Microsoft.Scripting/Runtime/ScopeExtension.cs
+++ b/Src/Microsoft.Scripting/Runtime/ScopeExtension.cs
@@ -9,7 +9,7 @@ namespace Microsoft.Scripting.Runtime {
     // TODO: this class should be abstract
     public class ScopeExtension {
         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
-        public static readonly ScopeExtension[] EmptyArray = new ScopeExtension[0];
+        public static readonly ScopeExtension[] EmptyArray = System.Array.Empty<ScopeExtension>();
 
         public Scope Scope { get; }
 
diff --git a/Src/Microsoft.Scripting/Stubs.cs b/Src/Microsoft.Scripting/Stubs.cs
deleted file mode 100644
index 5766f6e7..00000000
--- a/Src/Microsoft.Scripting/Stubs.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the Apache 2.0 License.
-// See the LICENSE file in the project root for more information.
-
-using System.Reflection;
-
-#if !FEATURE_SERIALIZATION
-
-namespace System {
-    using System.Diagnostics;
-
-    [Conditional("STUB")]
-    public class SerializableAttribute : Attribute {
-    }
-
-    [Conditional("STUB")]
-    public class NonSerializedAttribute : Attribute {
-    }
-
-    namespace Runtime.Serialization {
-        public interface ISerializable {
-        }
-
-        public interface IDeserializationCallback {
-        }
-    }
-
-    public class SerializationException : Exception {
-    }
-}
-
-#endif
diff --git a/Src/Microsoft.Scripting/Utils/CollectionExtensions.cs b/Src/Microsoft.Scripting/Utils/CollectionExtensions.cs
index 0c4a5580..1c01a962 100644
--- a/Src/Microsoft.Scripting/Utils/CollectionExtensions.cs
+++ b/Src/Microsoft.Scripting/Utils/CollectionExtensions.cs
@@ -88,10 +88,10 @@ internal static bool TrueForAll<T>(this IEnumerable<T> collection, Predicate<T>
 
 
     internal static class EmptyReadOnlyCollection<T> {
-        internal static readonly ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new T[0]);
+        internal static readonly ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(Array.Empty<T>());
     }
 
     internal static class EmptyArray<T> {
-        internal static readonly T[] Instance = new T[0];
+        internal static readonly T[] Instance = Array.Empty<T>();
     }
 }
diff --git a/Tests/Metadata/TypeNestings.cs b/Tests/Metadata/TypeNestings.cs
index 53e56397..8a83def9 100644
--- a/Tests/Metadata/TypeNestings.cs
+++ b/Tests/Metadata/TypeNestings.cs
@@ -14,7 +14,7 @@ namespace Metadata {
     public sealed class TypeNestings {
         private readonly MetadataTables _tables;
         private readonly Dictionary<MetadataToken, List<MetadataToken>> _mapping;
-        private static readonly TypeDef[] _EmptyTypeDefs = new TypeDef[0];
+        private static readonly TypeDef[] _EmptyTypeDefs = Array.Empty<TypeDef>();
 
         public TypeNestings(MetadataTables tables) {
             ContractUtils.Requires(tables != null);
diff --git a/Tests/Microsoft.Dynamic.Test/InterpreterTest.cs b/Tests/Microsoft.Dynamic.Test/InterpreterTest.cs
index 701460ff..f4671bd0 100644
--- a/Tests/Microsoft.Dynamic.Test/InterpreterTest.cs
+++ b/Tests/Microsoft.Dynamic.Test/InterpreterTest.cs
@@ -30,7 +30,7 @@ public void PopTest() {
             // Equal gets converted to an EqualReference instruction which does two pops and a push.
             // If pop doesn't clear the stack one of the two TestGc objects should remain alive.
             var lambda = Expression.Lambda(Expression.Block(
-                Expression.Equal(Expression.New(typeof(TestGc).GetConstructor(new Type[0])), Expression.New(typeof(TestGc).GetConstructor(new Type[0]))),
+                Expression.Equal(Expression.New(typeof(TestGc).GetConstructor(Array.Empty<Type>())), Expression.New(typeof(TestGc).GetConstructor(Array.Empty<Type>()))),
                 Expression.Call(typeof(TestGc).GetMethod(nameof(TestGc.CheckCount)))
             ));
 

From 332ed8d921df7084a8ae952206500a2de78cd598 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:42:32 -0400
Subject: [PATCH 3/7] Add .NET 9 target for testing

---
 .github/workflows/main.yml                                | 8 ++++++++
 Build.proj                                                | 2 +-
 Build/steps.yml                                           | 6 ++++++
 Dlr.sln                                                   | 1 +
 Package/nuget/DynamicLanguageRuntime.nuspec               | 6 +++---
 .../Hosting/Shell/ConsoleHostOptionsParser.cs             | 2 +-
 Src/Microsoft.Dynamic/StringExtensions.cs                 | 5 +++++
 Tests/Metadata/Metadata.csproj                            | 2 +-
 .../Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj  | 2 +-
 .../Microsoft.Scripting.Test.csproj                       | 2 +-
 make.ps1                                                  | 2 +-
 11 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 73b4ba7f..0d133b44 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -26,6 +26,11 @@ jobs:
       uses: actions/setup-dotnet@v1
       with:
         dotnet-version: '8.0.x'
+    - name: Setup .NET 9.0
+      uses: actions/setup-dotnet@v1
+      with:
+        dotnet-version: '9.0.x'
+        include-prerelease: true
     - name: Build
       run: pwsh make.ps1
     - name: Package
@@ -46,3 +51,6 @@ jobs:
     - name: Test (net8.0)
       run: ./make.ps1 -frameworks net8.0 test-all
       shell: pwsh
+    - name: Test (net9.0)
+      run: ./make.ps1 -frameworks net9.0 test-all
+      shell: pwsh
diff --git a/Build.proj b/Build.proj
index 6ba917cf..17a033e1 100644
--- a/Build.proj
+++ b/Build.proj
@@ -108,7 +108,7 @@
           Outputs="$(PackageDir)\DynamicLanguageRuntime.$(PackageVersion).zip">
     <ItemGroup>
       <ZipFiles Include="$(StageDir)\**\*.dll;$(StageDir)\**\*.xml;$(StageDir)\README.md;$(StageDir)\LICENSE"
-                Exclude="$(StageDir)\netcoreapp3.1\*;$(StageDir)\net7.0*\*;$(StageDir)\net8.0*\*;$(StageDir)\net9.0*\*" />
+                Exclude="$(StageDir)\netcoreapp3.1\*;$(StageDir)\net7.0*\*;$(StageDir)\net9.0*\*" />
     </ItemGroup>
     <Message Text="$(ZipFiles)" />
     <Zip Files="@(ZipFiles)" ZipFileName="$(PackageDir)\DynamicLanguageRuntime.$(PackageVersion).zip" WorkingDirectory="$(StageDir)"/>
diff --git a/Build/steps.yml b/Build/steps.yml
index fef07f0f..7f63edd1 100644
--- a/Build/steps.yml
+++ b/Build/steps.yml
@@ -38,6 +38,12 @@ steps:
     inputs:
       packageType: 'sdk'
       version: '8.0.x'
+
+  - task: UseDotNet@2
+    displayName: Install .NET 9.0 SDK for build
+    inputs:
+      packageType: 'sdk'
+      version: '9.0.x'
       includePreviewVersions: true
 
   # Set Mono version on macOS
diff --git a/Dlr.sln b/Dlr.sln
index de18fe0e..a0fdd4d8 100644
--- a/Dlr.sln
+++ b/Dlr.sln
@@ -38,6 +38,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{60056F49
 		Build\net462.props = Build\net462.props
 		Build\net6.0.props = Build\net6.0.props
 		Build\net8.0.props = Build\net8.0.props
+		Build\net9.0.props = Build\net9.0.props
 		Build\netstandard2.0.props = Build\netstandard2.0.props
 		Build\steps.yml = Build\steps.yml
 	EndProjectSection
diff --git a/Package/nuget/DynamicLanguageRuntime.nuspec b/Package/nuget/DynamicLanguageRuntime.nuspec
index 970b0204..40fbbdd2 100644
--- a/Package/nuget/DynamicLanguageRuntime.nuspec
+++ b/Package/nuget/DynamicLanguageRuntime.nuspec
@@ -32,9 +32,9 @@
     </dependencies>
   </metadata>
   <files>
-    <file src="**\*.dll" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net8.0*\*" />
-    <file src="**\*.pdb" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net8.0*\*" />
-    <file src="**\*.xml" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net8.0*\*" />
+    <file src="**\*.dll" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net9.0*\*" />
+    <file src="**\*.pdb" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net9.0*\*" />
+    <file src="**\*.xml" target="lib" exclude="netcoreapp3.1\*;net7.0*\*;net9.0*\*" />
     <file src="README.md;LICENSE" />
   </files>
 </package>
diff --git a/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs b/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
index 84892902..66c3408e 100644
--- a/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
+++ b/Src/Microsoft.Dynamic/Hosting/Shell/ConsoleHostOptionsParser.cs
@@ -99,7 +99,7 @@ public void Parse(string[] args) {
         private void ParseOption(string arg, out string name, out string value) {
             Debug.Assert(arg != null);
 
-            int colon = arg.IndexOf(':');
+            int colon = arg.IndexOf(':', StringComparison.Ordinal);
 
             if (colon >= 0) {
                 name = arg.Substring(0, colon);
diff --git a/Src/Microsoft.Dynamic/StringExtensions.cs b/Src/Microsoft.Dynamic/StringExtensions.cs
index eb5373da..a1963924 100644
--- a/Src/Microsoft.Dynamic/StringExtensions.cs
+++ b/Src/Microsoft.Dynamic/StringExtensions.cs
@@ -14,6 +14,11 @@ public static bool StartsWith(this string str, char value)
         {
             return str.StartsWith(value.ToString(), StringComparison.Ordinal);
         }
+
+        public static int IndexOf(this string str, char value, StringComparison comparisonType) {
+            if (comparisonType == StringComparison.Ordinal) return str.IndexOf(value);
+            return str.IndexOf(value.ToString(), comparisonType);
+        }
 #endif
     }
 }
diff --git a/Tests/Metadata/Metadata.csproj b/Tests/Metadata/Metadata.csproj
index ae90f462..009de736 100644
--- a/Tests/Metadata/Metadata.csproj
+++ b/Tests/Metadata/Metadata.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0;net9.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
     <OutputType>Exe</OutputType>
diff --git a/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj b/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
index a244b8e5..1fa19c51 100644
--- a/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
+++ b/Tests/Microsoft.Dynamic.Test/Microsoft.Dynamic.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0;net9.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
diff --git a/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj b/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
index 3fd1c99c..616b0f04 100644
--- a/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
+++ b/Tests/Microsoft.Scripting.Test/Microsoft.Scripting.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;netcoreapp3.1;net6.0;net8.0;net9.0</TargetFrameworks>
     <!-- EOL netcoreapp3.1 is used to test netstandard2.0 assemblies -->
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
diff --git a/make.ps1 b/make.ps1
index d41161f2..8a97f4bf 100755
--- a/make.ps1
+++ b/make.ps1
@@ -4,7 +4,7 @@ Param(
     [Parameter(Position=1)]
     [String] $target = "release",
     [String] $configuration = "Release",
-    [String[]] $frameworks=@('net462','netcoreapp3.1','net6.0','net8.0'),
+    [String[]] $frameworks=@('net462','netcoreapp3.1','net6.0','net8.0','net9.0'),
     [String] $platform = "x64",
     [switch] $runIgnored
 )

From 9cd5a766ca9367bfa502eba2e0ecedfc5c2aa4d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:42:54 -0400
Subject: [PATCH 4/7] Add missing props

---
 Build/net9.0.props | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Build/net9.0.props

diff --git a/Build/net9.0.props b/Build/net9.0.props
new file mode 100644
index 00000000..97529b54
--- /dev/null
+++ b/Build/net9.0.props
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <IsFullFramework>false</IsFullFramework>
+  </PropertyGroup>
+
+  <PropertyGroup>
+    <Features>$(Features);FEATURE_APARTMENTSTATE</Features>
+    <Features>$(Features);FEATURE_ASSEMBLY_GETFORWARDEDTYPES</Features>
+    <Features>$(Features);FEATURE_ASSEMBLY_RESOLVE</Features>
+    <Features>$(Features);FEATURE_ASSEMBLYBUILDER_DEFINEDYNAMICASSEMBLY</Features>
+    <Features>$(Features);FEATURE_CODEDOM</Features>
+    <Features>$(Features);FEATURE_COM</Features>
+    <Features>$(Features);FEATURE_CONFIGURATION</Features>
+    <Features>$(Features);FEATURE_CUSTOM_TYPE_DESCRIPTOR</Features>
+    <Features>$(Features);FEATURE_EXCEPTION_STATE</Features>
+    <Features>$(Features);FEATURE_FILESYSTEM</Features>
+    <Features>$(Features);FEATURE_FULL_CRYPTO</Features>
+    <Features>$(Features);FEATURE_FULL_NET</Features>
+    <Features>$(Features);FEATURE_LCG</Features>
+    <Features>$(Features);FEATURE_LOADWITHPARTIALNAME</Features>
+    <Features>$(Features);FEATURE_METADATA_READER</Features>
+    <Features>$(Features);FEATURE_MMAP</Features>
+    <Features>$(Features);FEATURE_NATIVE</Features>
+    <Features>$(Features);FEATURE_PIPES</Features>
+    <Features>$(Features);FEATURE_PROCESS</Features>
+    <Features>$(Features);FEATURE_REFEMIT</Features>
+    <Features>$(Features);FEATURE_REGISTRY</Features>
+    <Features>$(Features);FEATURE_SECURITY_RULES</Features>
+    <Features>$(Features);FEATURE_STACK_TRACE</Features>
+    <Features>$(Features);FEATURE_SYNC_SOCKETS</Features>
+    <Features>$(Features);FEATURE_THREAD</Features>
+    <Features>$(Features);FEATURE_TYPE_EQUIVALENCE</Features>
+    <Features>$(Features);FEATURE_TYPECONVERTER</Features>
+    <Features>$(Features);FEATURE_XMLDOC</Features>
+  </PropertyGroup>
+</Project>

From 7ba8bbbb6b5e8201ae76a2b8c657e4574575014f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:50:12 -0400
Subject: [PATCH 5/7] Replace Shell++ task with Bash

---
 Build/steps.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Build/steps.yml b/Build/steps.yml
index 7f63edd1..ddbdf45d 100644
--- a/Build/steps.yml
+++ b/Build/steps.yml
@@ -48,7 +48,7 @@ steps:
 
   # Set Mono version on macOS
   - ${{ if eq(parameters.os, 'macOS') }}:
-    - task: ms-devlabs.utilitytasks.task-Shellpp.Shell++@0
+    - task: Bash@3
       displayName: Set Mono Version
       inputs:
         type: InlineScript
@@ -62,7 +62,7 @@ steps:
 
   # Install mono when running on Linux
   - ${{ if eq(parameters.os, 'Linux') }}:
-    - task: ms-devlabs.utilitytasks.task-Shellpp.Shell++@0
+    - task: Bash@3
       displayName: Version Information
       inputs:
         type: InlineScript
@@ -76,7 +76,7 @@ steps:
 
   # Dump version info on macOS
   - ${{ if eq(parameters.os, 'macOS') }}:
-    - task: ms-devlabs.utilitytasks.task-Shellpp.Shell++@0
+    - task: Bash@3
       displayName: Version Information
       inputs:
         type: InlineScript

From 0910e6ad07f92349c3d3c79dc8fdb2dbbc626e7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:53:50 -0400
Subject: [PATCH 6/7] Version information

---
 Build/steps.yml | 34 +++++++++-------------------------
 1 file changed, 9 insertions(+), 25 deletions(-)

diff --git a/Build/steps.yml b/Build/steps.yml
index ddbdf45d..1f670f4e 100644
--- a/Build/steps.yml
+++ b/Build/steps.yml
@@ -60,31 +60,15 @@ steps:
           echo "##vso[task.setvariable variable=PKG_CONFIG_PATH;]$MONOPREFIX/lib/pkgconfig:$MONOPREFIX/share/pkgconfig:$PKG_CONFIG_PATH"
           echo "##vso[task.setvariable variable=PATH;]$MONOPREFIX/bin:$PATH"
 
-  # Install mono when running on Linux
-  - ${{ if eq(parameters.os, 'Linux') }}:
-    - task: Bash@3
-      displayName: Version Information
-      inputs:
-        type: InlineScript
-        script: |
-
-          # Dump some info about the tools
-          mono --version
-          msbuild /version
-          dotnet --info
-          df -Th
-
-  # Dump version info on macOS
-  - ${{ if eq(parameters.os, 'macOS') }}:
-    - task: Bash@3
-      displayName: Version Information
-      inputs:
-        type: InlineScript
-        script: |
-          # Dump some info about the tools
-          mono --version
-          msbuild /version
-          dotnet --info
+  # Dump version info
+  - task: PowerShell@2
+    displayName: Version Information
+    inputs:
+      targetType: inline
+      script: |
+        dotnet --info
+        try { msbuild -version } catch { }
+        try { mono --version } catch { }
 
   - powershell: ./make.ps1
     displayName: Build

From 8c79f45f01d3febe288e4c592b9ab7b5d25d811c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Lozier?= <slozier@users.noreply.github.com>
Date: Mon, 24 Jun 2024 22:58:46 -0400
Subject: [PATCH 7/7] Try to fix macOS build

---
 .github/workflows/main.yml | 2 +-
 Build/steps.yml            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0d133b44..656679d8 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,7 +10,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [windows-latest, ubuntu-latest, macos-latest]
+        os: [windows-latest, ubuntu-latest, macos-latest-large]
 
     steps:
     - uses: actions/checkout@v2
diff --git a/Build/steps.yml b/Build/steps.yml
index 1f670f4e..9eed332a 100644
--- a/Build/steps.yml
+++ b/Build/steps.yml
@@ -51,7 +51,7 @@ steps:
     - task: Bash@3
       displayName: Set Mono Version
       inputs:
-        type: InlineScript
+        targetType: inline
         script: |
           # use Mono 6.4.0 version
           SYMLINK=6.4.0