瀏覽代碼

Add NET 8 support (#1686)

* add NET 8 target for Jint
* switch to NET 8 for tests, benchmarks and REPL
* add SearchValues polyfill
Marko Lahma 1 年之前
父節點
當前提交
197ebd8d00

+ 5 - 5
Directory.Packages.props

@@ -8,9 +8,9 @@
     <PackageVersion Include="Esprima" Version="3.0.2" />
     <PackageVersion Include="Esprima" Version="3.0.2" />
     <PackageVersion Include="Flurl.Http.Signed" Version="3.2.4" />
     <PackageVersion Include="Flurl.Http.Signed" Version="3.2.4" />
     <PackageVersion Include="Jurassic" Version="3.2.7" />
     <PackageVersion Include="Jurassic" Version="3.2.7" />
-    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.106" />
+    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.110" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
     <PackageVersion Include="MongoDB.Bson.signed" Version="2.19.0" />
     <PackageVersion Include="MongoDB.Bson.signed" Version="2.19.0" />
     <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageVersion Include="NiL.JS" Version="2.5.1674" />
     <PackageVersion Include="NiL.JS" Version="2.5.1674" />
@@ -21,13 +21,13 @@
     <PackageVersion Include="Spectre.Console.Cli" Version="0.45.0" />
     <PackageVersion Include="Spectre.Console.Cli" Version="0.45.0" />
     <PackageVersion Include="System.Text.Json" Version="6.0.8" />
     <PackageVersion Include="System.Text.Json" Version="6.0.8" />
     <PackageVersion Include="Test262Harness" Version="0.0.22" />
     <PackageVersion Include="Test262Harness" Version="0.0.22" />
-    <PackageVersion Include="xunit" Version="2.6.1" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
+    <PackageVersion Include="xunit" Version="2.6.2" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" PrivateAssets="all" />
     <PackageVersion Include="YantraJS.Core" Version="1.2.203" />
     <PackageVersion Include="YantraJS.Core" Version="1.2.203" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <GlobalPackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
     <GlobalPackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
-    <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
+    <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
     <GlobalPackageReference Include="PolySharp" Version="1.13.2" />
     <GlobalPackageReference Include="PolySharp" Version="1.13.2" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 1 - 1
Jint.Benchmark/Jint.Benchmark.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
     <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
     <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
     <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
     <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>

+ 1 - 1
Jint.Repl/Jint.Repl.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <OutputType>Exe</OutputType>
     <OutputType>Exe</OutputType>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>
     <ImplicitUsings>enable</ImplicitUsings>
     <ImplicitUsings>enable</ImplicitUsings>

+ 3 - 2
Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj

@@ -1,10 +1,11 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
-    <!--<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net461</TargetFrameworks>-->
+    <TargetFrameworks>net8.0</TargetFrameworks>
+    <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
     <IsPackable>false</IsPackable>
     <IsPackable>false</IsPackable>
     <ImplicitUsings>enable</ImplicitUsings>
     <ImplicitUsings>enable</ImplicitUsings>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 3 - 1
Jint.Tests.PublicInterface/ConstraintUsageTests.cs

@@ -8,7 +8,9 @@ public class ConstraintUsageTests
 {
 {
 // this test case is problematic due to nature of cancellation token source in old framework
 // this test case is problematic due to nature of cancellation token source in old framework
 // in NET 6 it's better designed and signals more reliably
 // in NET 6 it's better designed and signals more reliably
-#if NET6_0_OR_GREATER
+
+// TODO NET 8 also has problems with this
+#if NET6_0
     [Fact]
     [Fact]
     public void CanFindAndResetCancellationConstraint()
     public void CanFindAndResetCancellationConstraint()
     {
     {

+ 1 - 1
Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFrameworks>net6.0</TargetFrameworks>
+    <TargetFrameworks>net8.0</TargetFrameworks>
     <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
     <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <SignAssembly>true</SignAssembly>

+ 1 - 1
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <!--<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net461</TargetFrameworks>-->
     <!--<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net461</TargetFrameworks>-->
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <SignAssembly>true</SignAssembly>

+ 2 - 2
Jint.Tests.Test262/Test262Test.cs

@@ -18,7 +18,7 @@ public abstract partial class Test262Test
             cfg.EnableModules(new Test262ModuleLoader(State.Test262Stream.Options.FileSystem, relativePath));
             cfg.EnableModules(new Test262ModuleLoader(State.Test262Stream.Options.FileSystem, relativePath));
         });
         });
 
 
-        if (file.Flags.IndexOf("raw") != -1)
+        if (file.Flags.Contains("raw"))
         {
         {
             // nothing should be loaded
             // nothing should be loaded
             return engine;
             return engine;
@@ -76,7 +76,7 @@ public abstract partial class Test262Test
             engine.Execute(State.Sources[include]);
             engine.Execute(State.Sources[include]);
         }
         }
 
 
-        if (file.Flags.IndexOf("async") != -1)
+        if (file.Flags.Contains("async"))
         {
         {
             engine.Execute(State.Sources["doneprintHandle.js"]);
             engine.Execute(State.Sources["doneprintHandle.js"]);
         }
         }

+ 1 - 1
Jint.Tests/Jint.Tests.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
 
 
   <PropertyGroup>
   <PropertyGroup>
-    <TargetFrameworks>net6.0</TargetFrameworks>
+    <TargetFrameworks>net8.0</TargetFrameworks>
     <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
     <TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net462</TargetFrameworks>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <AssemblyOriginatorKeyFile>..\Jint\Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <SignAssembly>true</SignAssembly>

+ 11 - 5
Jint.Tests/Runtime/DateTests.cs

@@ -74,11 +74,17 @@ public class DateTests
     [Fact]
     [Fact]
     public void ToStringFollowsJavaScriptFormat()
     public void ToStringFollowsJavaScriptFormat()
     {
     {
-        var engine = new Engine(
-            conf =>
-            {
-                conf.LocalTimeZone(TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"));
-            });
+        TimeZoneInfo timeZoneInfo;
+        try
+        {
+            timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
+        }
+        catch
+        {
+            timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
+        }
+
+        var engine = new Engine(options => options.LocalTimeZone(timeZoneInfo));
 
 
         Assert.Equal("Tue Feb 01 2022 00:00:00 GMT+0800 (China Standard Time)", engine.Evaluate("new Date(2022,1,1).toString()"));
         Assert.Equal("Tue Feb 01 2022 00:00:00 GMT+0800 (China Standard Time)", engine.Evaluate("new Date(2022,1,1).toString()"));
         Assert.Equal("Tue Feb 01 2022 00:00:00 GMT+0800 (China Standard Time)", engine.Evaluate("new Date(2022,1,1)").ToString());
         Assert.Equal("Tue Feb 01 2022 00:00:00 GMT+0800 (China Standard Time)", engine.Evaluate("new Date(2022,1,1)").ToString());

+ 2 - 2
Jint.Tests/Runtime/EngineLimitTests.cs

@@ -9,9 +9,9 @@ public class EngineLimitTests
 {
 {
 
 
 #if RELEASE
 #if RELEASE
-    const int FunctionNestingCount = 960;
+    const int FunctionNestingCount = 840;
 #else
 #else
-    const int FunctionNestingCount = 520;
+    const int FunctionNestingCount = 510;
 #endif
 #endif
 
 
     [Fact]
     [Fact]

+ 8 - 13
Jint.Tests/Runtime/EngineTests.cs

@@ -24,40 +24,35 @@ namespace Jint.Tests.Runtime
         private static readonly TimeZoneInfo _tongaTimeZone;
         private static readonly TimeZoneInfo _tongaTimeZone;
         private static readonly TimeZoneInfo _easternTimeZone;
         private static readonly TimeZoneInfo _easternTimeZone;
 
 
-
         static EngineTests()
         static EngineTests()
         {
         {
+            // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way
+            // should be natively supported soon https://github.com/dotnet/runtime/issues/18644
             try
             try
             {
             {
-                _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
+                _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
             }
             }
             catch (TimeZoneNotFoundException)
             catch (TimeZoneNotFoundException)
             {
             {
-                // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way
-                // should be natively supported soon https://github.com/dotnet/runtime/issues/18644
-                _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
+                _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
             }
             }
 
 
             try
             try
             {
             {
-                _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tonga Standard Time");
+                _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Tongatapu");
             }
             }
             catch (TimeZoneNotFoundException)
             catch (TimeZoneNotFoundException)
             {
             {
-                // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way
-                // should be natively supported soon https://github.com/dotnet/runtime/issues/18644
-                _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Tongatapu");
+                _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tonga Standard Time");
             }
             }
 
 
             try
             try
             {
             {
-                _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time");
+                _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
             }
             }
             catch (TimeZoneNotFoundException)
             catch (TimeZoneNotFoundException)
             {
             {
-                // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way
-                // should be natively supported soon https://github.com/dotnet/runtime/issues/18644
-                _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
+                _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time");
             }
             }
         }
         }
 
 

+ 3 - 2
Jint.Tests/Runtime/InteropTests.cs

@@ -2809,8 +2809,9 @@ namespace Jint.Tests.Runtime
         {
         {
             var engine = new Engine(cfg => cfg.CatchClrExceptions());
             var engine = new Engine(cfg => cfg.CatchClrExceptions());
             engine.SetValue("a", new Person());
             engine.SetValue("a", new Person());
-            var ex = Assert.Throws<JavaScriptException>(() => engine.Execute("a.age = \"It won't work, but it's normal\""));
-            Assert.Equal("Input string was not in a correct format.", ex.Message);
+            var ex = Assert.Throws<JavaScriptException>(() => engine.Execute("a.age = 'It will not work, but it is normal'"));
+            Assert.Contains("input string ", ex.Message, StringComparison.OrdinalIgnoreCase);
+            Assert.Contains(" was not in a correct format", ex.Message, StringComparison.OrdinalIgnoreCase);
         }
         }
 
 
         [Fact]
         [Fact]

+ 9 - 0
Jint/Extensions/Polyfills.cs

@@ -1,12 +1,21 @@
+using System.Runtime.CompilerServices;
+
 namespace Jint;
 namespace Jint;
 
 
 internal static class Polyfills
 internal static class Polyfills
 {
 {
 #if NETFRAMEWORK || NETSTANDARD2_0
 #if NETFRAMEWORK || NETSTANDARD2_0
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static bool Contains(this string source, char c) => source.IndexOf(c) != -1;
     internal static bool Contains(this string source, char c) => source.IndexOf(c) != -1;
 #endif
 #endif
 
 
+#if NETFRAMEWORK
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    internal static bool Contains(this ReadOnlySpan<string> source, string c) => source.IndexOf(c) != -1;
+#endif
+
 #if NETFRAMEWORK || NETSTANDARD2_0
 #if NETFRAMEWORK || NETSTANDARD2_0
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
     internal static bool StartsWith(this string source, char c) => source.Length > 0 && source[0] == c;
     internal static bool StartsWith(this string source, char c) => source.Length > 0 && source[0] == c;
 #endif
 #endif
 }
 }

+ 44 - 0
Jint/Extensions/SearchValues.cs

@@ -0,0 +1,44 @@
+#if !NET8_0_OR_GREATER
+
+using System.Runtime.CompilerServices;
+
+namespace System.Buffers;
+
+internal static class SearchValues
+{
+    internal static SearchValues<char> Create(string input) => new(input.AsSpan());
+    internal static SearchValues<char> Create(ReadOnlySpan<char> input) => new(input);
+}
+
+internal sealed class SearchValues<T>
+{
+    private readonly bool[] _data;
+    private readonly char _min;
+    private readonly char _max;
+
+    internal SearchValues(ReadOnlySpan<char> input)
+    {
+        _min = char.MaxValue;
+        _max = char.MinValue;
+        foreach (var c in input)
+        {
+            _min = (char) Math.Min(_min, c);
+            _max = (char) Math.Max(_max, c);
+        }
+
+        _data = new bool[_max - _min + 1];
+        foreach (var c in input)
+        {
+            _data[c - _min] = true;
+        }
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public bool Contains(char c)
+    {
+        var i = (uint) (c - _min);
+        var temp = _data;
+        return i < temp.Length && temp[i];
+    }
+}
+#endif

+ 2 - 2
Jint/Jint.csproj

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
   <PropertyGroup>
     <NeutralLanguage>en-US</NeutralLanguage>
     <NeutralLanguage>en-US</NeutralLanguage>
-    <TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net8.0</TargetFrameworks>
 
 
     <AssemblyOriginatorKeyFile>Jint.snk</AssemblyOriginatorKeyFile>
     <AssemblyOriginatorKeyFile>Jint.snk</AssemblyOriginatorKeyFile>
     <SignAssembly>true</SignAssembly>
     <SignAssembly>true</SignAssembly>
@@ -21,7 +21,7 @@
 
 
   </PropertyGroup>
   </PropertyGroup>
 
 
-  <PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'netstandard2.1' ">
+  <PropertyGroup Condition=" '$(TargetFramework)' != 'net462' and '$(TargetFramework)' != 'netstandard2.0' ">
     <DefineConstants>$(DefineConstants);SUPPORTS_SPAN_PARSE;SUPPORTS_WEAK_TABLE_ADD_OR_UPDATE;SUPPORTS_WEAK_TABLE_CLEAR</DefineConstants>
     <DefineConstants>$(DefineConstants);SUPPORTS_SPAN_PARSE;SUPPORTS_WEAK_TABLE_ADD_OR_UPDATE;SUPPORTS_WEAK_TABLE_CLEAR</DefineConstants>
   </PropertyGroup>
   </PropertyGroup>
 
 

+ 15 - 12
Jint/Native/Global/GlobalObject.cs

@@ -1,3 +1,4 @@
+using System.Buffers;
 using System.Globalization;
 using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
@@ -272,12 +273,11 @@ namespace Jint.Native.Global
             return true;
             return true;
         }
         }
 
 
-        private static readonly string UriReserved = new (new [] { ';', '/', '?', ':', '@', '&', '=', '+', '$', ',' });
-        private static readonly string UriUnescaped = new(new [] { '-', '_', '.', '!', '~', '*', '\'', '(', ')' });
-        private static readonly string UnescapedUriSet = UriReserved + UriUnescaped + '#';
-        private static readonly string ReservedUriSet = UriReserved + '#';
-
-        private const string HexaMap = "0123456789ABCDEF";
+        private const string UriReservedString = ";/?:@&=+$,";
+        private const string UriUnescapedString = "-_.!~*'()";
+        private static readonly SearchValues<char> UriUnescaped = SearchValues.Create(UriUnescapedString);
+        private static readonly SearchValues<char> UnescapedUriSet = SearchValues.Create(UriReservedString + UriUnescapedString + '#');
+        private static readonly SearchValues<char> ReservedUriSet = SearchValues.Create(UriReservedString + '#');
 
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static bool IsValidHexaChar(char c) => Uri.IsHexDigit(c);
         private static bool IsValidHexaChar(char c) => Uri.IsHexDigit(c);
@@ -309,13 +309,15 @@ namespace Jint.Native.Global
             return Encode(uriString, UriUnescaped);
             return Encode(uriString, UriUnescaped);
         }
         }
 
 
-        private JsValue Encode(string uriString, string unescapedUriSet)
+        private JsValue Encode(string uriString, SearchValues<char> unescapedUriSet)
         {
         {
+            const string HexaMap = "0123456789ABCDEF";
+
             var strLen = uriString.Length;
             var strLen = uriString.Length;
 
 
             _stringBuilder.EnsureCapacity(uriString.Length);
             _stringBuilder.EnsureCapacity(uriString.Length);
             _stringBuilder.Clear();
             _stringBuilder.Clear();
-            var buffer = new byte[4];
+            Span<byte> buffer = stackalloc byte[4];
 
 
             for (var k = 0; k < strLen; k++)
             for (var k = 0; k < strLen; k++)
             {
             {
@@ -421,7 +423,7 @@ uriError:
             return Decode(componentString, null);
             return Decode(componentString, null);
         }
         }
 
 
-        private JsValue Decode(string uriString, string? reservedSet)
+        private JsValue Decode(string uriString, SearchValues<char>? reservedSet)
         {
         {
             var strLen = uriString.Length;
             var strLen = uriString.Length;
 
 
@@ -463,7 +465,7 @@ uriError:
                     {
                     {
                         C = (char)B;
                         C = (char)B;
 #pragma warning disable CA2249
 #pragma warning disable CA2249
-                        if (reservedSet == null || reservedSet.IndexOf(C) == -1)
+                        if (reservedSet == null || !reservedSet.Contains(C))
 #pragma warning restore CA2249
 #pragma warning restore CA2249
                         {
                         {
                             _stringBuilder.Append(C);
                             _stringBuilder.Append(C);
@@ -589,12 +591,13 @@ uriError:
             return tmp < radix;
             return tmp < radix;
         }
         }
 
 
+        private static readonly SearchValues<char> EscapeAllowList = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_ + -./");
+
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.1
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-B.2.1
         /// </summary>
         /// </summary>
         public JsValue Escape(JsValue thisObject, JsValue[] arguments)
         public JsValue Escape(JsValue thisObject, JsValue[] arguments)
         {
         {
-            const string AllowList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_ + -./";
             var uriString = TypeConverter.ToString(arguments.At(0));
             var uriString = TypeConverter.ToString(arguments.At(0));
 
 
             var strLen = uriString.Length;
             var strLen = uriString.Length;
@@ -605,7 +608,7 @@ uriError:
             for (var k = 0; k < strLen; k++)
             for (var k = 0; k < strLen; k++)
             {
             {
                 var c = uriString[k];
                 var c = uriString[k];
-                if (AllowList.Contains(c))
+                if (EscapeAllowList.Contains(c))
                 {
                 {
                     _stringBuilder.Append(c);
                     _stringBuilder.Append(c);
                 }
                 }

+ 7 - 7
Jint/Native/JsString.cs

@@ -247,22 +247,22 @@ public class JsString : JsValue, IEquatable<JsString>, IEquatable<string>
 
 
     public override string ToString() => _value;
     public override string ToString() => _value;
 
 
-    internal int IndexOf(string value, int startIndex = 0)
+    internal bool Contains(char c)
     {
     {
-        if (Length - startIndex < value.Length)
+        if (c == 0)
         {
         {
-            return -1;
+            return false;
         }
         }
-        return ToString().IndexOf(value, startIndex, StringComparison.Ordinal);
+        return ToString().Contains(c);
     }
     }
 
 
-    internal int IndexOf(char value)
+    internal int IndexOf(string value, int startIndex = 0)
     {
     {
-        if (Length == 0)
+        if (Length - startIndex < value.Length)
         {
         {
             return -1;
             return -1;
         }
         }
-        return ToString().IndexOf(value);
+        return ToString().IndexOf(value, startIndex, StringComparison.Ordinal);
     }
     }
 
 
     internal bool StartsWith(string value, int start = 0)
     internal bool StartsWith(string value, int start = 0)

+ 1 - 1
Jint/Native/Json/JsonParser.cs

@@ -106,7 +106,7 @@ namespace Jint.Native.Json
                 if (_index < _length + 1 && IsHexDigit(_source[_index]))
                 if (_index < _length + 1 && IsHexDigit(_source[_index]))
                 {
                 {
                     char ch = _source[_index++];
                     char ch = _source[_index++];
-                    code = code * 16 + "0123456789abcdef".IndexOf(ch.ToString(), StringComparison.OrdinalIgnoreCase);
+                    code = code * 16 + "0123456789abcdef".IndexOf(ch);
                 }
                 }
                 else
                 else
                 {
                 {

+ 5 - 5
Jint/Native/RegExp/RegExpPrototype.cs

@@ -326,7 +326,7 @@ namespace Jint.Native.RegExp
             string replacement)
             string replacement)
         {
         {
             // If there is no pattern, replace the pattern as is.
             // If there is no pattern, replace the pattern as is.
-            if (replacement.IndexOf('$') < 0)
+            if (!replacement.Contains('$'))
             {
             {
                 return replacement;
                 return replacement;
             }
             }
@@ -443,8 +443,8 @@ namespace Jint.Native.RegExp
             var limit = arguments.At(1);
             var limit = arguments.At(1);
             var c = SpeciesConstructor(rx, _realm.Intrinsics.RegExp);
             var c = SpeciesConstructor(rx, _realm.Intrinsics.RegExp);
             var flags = TypeConverter.ToJsString(rx.Get(PropertyFlags));
             var flags = TypeConverter.ToJsString(rx.Get(PropertyFlags));
-            var unicodeMatching = flags.IndexOf('u') > -1;
-            var newFlags = flags.IndexOf('y') > -1 ? flags : new JsString(flags.ToString() + 'y');
+            var unicodeMatching = flags.Contains('u');
+            var newFlags = flags.Contains('y') ? flags : new JsString(flags.ToString() + 'y');
             var splitter = Construct(c, new JsValue[]
             var splitter = Construct(c, new JsValue[]
             {
             {
                 rx,
                 rx,
@@ -789,8 +789,8 @@ namespace Jint.Native.RegExp
             var lastIndex = TypeConverter.ToLength(r.Get(JsRegExp.PropertyLastIndex));
             var lastIndex = TypeConverter.ToLength(r.Get(JsRegExp.PropertyLastIndex));
             matcher.Set(JsRegExp.PropertyLastIndex, lastIndex, true);
             matcher.Set(JsRegExp.PropertyLastIndex, lastIndex, true);
 
 
-            var global = flags.IndexOf('g') != -1;
-            var fullUnicode = flags.IndexOf('u') != -1;
+            var global = flags.Contains('g');
+            var fullUnicode = flags.Contains('u');
 
 
             return _realm.Intrinsics.RegExpStringIteratorPrototype.Construct(matcher, s, global, fullUnicode);
             return _realm.Intrinsics.RegExpStringIteratorPrototype.Construct(matcher, s, global, fullUnicode);
         }
         }

+ 3 - 3
Jint/Native/String/StringPrototype.cs

@@ -595,7 +595,7 @@ namespace Jint.Native.String
                 {
                 {
                     var flags = searchValue.Get(RegExpPrototype.PropertyFlags);
                     var flags = searchValue.Get(RegExpPrototype.PropertyFlags);
                     TypeConverter.CheckObjectCoercible(_engine, flags);
                     TypeConverter.CheckObjectCoercible(_engine, flags);
-                    if (TypeConverter.ToString(flags).IndexOf('g') < 0)
+                    if (!TypeConverter.ToString(flags).Contains('g'))
                     {
                     {
                         ExceptionHelper.ThrowTypeError(_realm, "String.prototype.replaceAll called with a non-global RegExp argument");
                         ExceptionHelper.ThrowTypeError(_realm, "String.prototype.replaceAll called with a non-global RegExp argument");
                     }
                     }
@@ -619,7 +619,7 @@ namespace Jint.Native.String
 
 
                 // check fast case
                 // check fast case
                 var newValue = replaceValue.ToString();
                 var newValue = replaceValue.ToString();
-                if (newValue.IndexOf('$') < 0 && searchString.Length > 0)
+                if (!newValue.Contains('$') && searchString.Length > 0)
                 {
                 {
                     // just plain old string replace
                     // just plain old string replace
                     return thisString.Replace(searchString, newValue);
                     return thisString.Replace(searchString, newValue);
@@ -711,7 +711,7 @@ namespace Jint.Native.String
                 {
                 {
                     var flags = regex.Get(RegExpPrototype.PropertyFlags);
                     var flags = regex.Get(RegExpPrototype.PropertyFlags);
                     TypeConverter.CheckObjectCoercible(_engine, flags);
                     TypeConverter.CheckObjectCoercible(_engine, flags);
-                    if (TypeConverter.ToString(flags).IndexOf('g') < 0)
+                    if (!TypeConverter.ToString(flags).Contains('g'))
                     {
                     {
                         ExceptionHelper.ThrowTypeError(_realm);
                         ExceptionHelper.ThrowTypeError(_realm);
                     }
                     }

+ 0 - 7
Jint/Runtime/JintException.cs

@@ -1,21 +1,14 @@
-using System.Runtime.Serialization;
-
 namespace Jint.Runtime
 namespace Jint.Runtime
 {
 {
     /// <summary>
     /// <summary>
     /// Base class for exceptions thrown by Jint.
     /// Base class for exceptions thrown by Jint.
     /// </summary>
     /// </summary>
-    [Serializable]
     public abstract class JintException : Exception
     public abstract class JintException : Exception
     {
     {
         protected JintException()
         protected JintException()
         {
         {
         }
         }
 
 
-        protected JintException(SerializationInfo info, StreamingContext context) : base(info, context)
-        {
-        }
-
         protected JintException(string? message) : base(message)
         protected JintException(string? message) : base(message)
         {
         {
         }
         }

+ 2 - 0
Jint/Runtime/OrderedDictionary.cs

@@ -1,3 +1,5 @@
+#pragma warning disable CA1863 // Cache a 'CompositeFormat' for repeated use in this formatting operation
+
 #nullable disable
 #nullable disable
 
 
 // based on https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary
 // based on https://github.com/jehugaleahsa/truncon.collections.OrderedDictionary

+ 0 - 41
Jint/Shims.cs

@@ -1,41 +0,0 @@
-namespace Jint;
-
-internal static class Shims
-{
-    public static byte[] BytesFromHexString(this ReadOnlySpan<char> value)
-    {
-#if NET6_0_OR_GREATER
-        return Convert.FromHexString(value);
-#else
-        if ((value.Length & 1) != 0)
-        {
-            throw new FormatException();
-        }
-
-        var byteCount = value.Length >> 1;
-        var result = new byte[byteCount];
-        var index = 0;
-        for (var i = 0; i < byteCount; i++)
-        {
-            int hi, lo;
-            if ((hi = GetDigitValue(value[index++])) < 0
-                || (lo = GetDigitValue(value[index++])) < 0)
-            {
-                throw new FormatException();
-            }
-
-            result[i] = (byte) (hi << 4 | lo);
-        }
-
-        return result;
-
-        static int GetDigitValue(char ch) => ch switch
-        {
-            >= '0' and <= '9' => ch - 0x30,
-            >= 'a' and <= 'f' => ch - 0x57,
-            >= 'A' and <= 'F' => ch - 0x37,
-            _ => -1
-        };
-#endif
-    }
-}