Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,7 @@

// Misc intl402 tests requiring full locale support
"intl402/fallback-locales-are-supported.js",
"intl402/supportedLocalesOf-consistent-with-resolvedOptions.js",

// Performance: This test iterates through all locales with all constructors - very slow
"intl402/supportedLocalesOf-unicode-extensions-ignored.js"
"intl402/supportedLocalesOf-consistent-with-resolvedOptions.js"

]
}
112 changes: 112 additions & 0 deletions Jint/Extensions/Lock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// https://raw.githubusercontent.com/SimonCropp/Polyfill/refs/heads/main/src/Polyfill/Lock.cs

// <auto-generated />
#pragma warning disable

#nullable enable

#if !NET9_0_OR_GREATER

namespace System.Threading;

using Diagnostics;
using Diagnostics.CodeAnalysis;

/// <summary>
/// Provides a way to get mutual exclusion in regions of code between different threads. A lock may be held by one thread at
/// a time.
/// </summary>
[ExcludeFromCodeCoverage]
[DebuggerNonUserCode]
//Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.lock
#if PolyPublic
public
#endif
class Lock
{
#if NETCOREAPP || NETFRAMEWORK || NETSTANDARD
public bool IsHeldByCurrentThread => Monitor.IsEntered(this);
#endif

/// <summary>
/// Enters the lock. Once the method returns, the calling thread would be the only thread that holds the lock.
/// </summary>
public void Enter() => Monitor.Enter(this);

/// <summary>
/// Tries to enter the lock without waiting. If the lock is entered, the calling thread would be the only thread that
/// holds the lock.
/// </summary>
/// <returns>
/// <code>true</code> if the lock was entered, <code>false</code> otherwise.
/// </returns>
public bool TryEnter() => Monitor.TryEnter(this);

/// <summary>
/// Tries to enter the lock, waiting for roughly the specified duration. If the lock is entered, the calling thread
/// would be the only thread that holds the lock.
/// </summary>
/// <param name="timeout">
/// The rough duration for which the method will wait if the lock is not available. The timeout is converted to a number
/// of milliseconds by casting <see cref="TimeSpan.TotalMilliseconds"/> of the timeout to an integer value. A value
/// representing <code>0</code> milliseconds specifies that the method should not wait, and a value representing
/// <see cref="Timeout.Infinite"/> or <code>-1</code> milliseconds specifies that the method should wait indefinitely
/// until the lock is entered.
/// </param>
/// <returns>
/// <code>true</code> if the lock was entered, <code>false</code> otherwise.
/// </returns>
public bool TryEnter(TimeSpan timeout) =>
Monitor.TryEnter(this, timeout);

/// <summary>
/// Tries to enter the lock, waiting for roughly the specified duration. If the lock is entered, the calling thread
/// would be the only thread that holds the lock.
/// </summary>
/// <param name="millisecondsTimeout">
/// The rough duration in milliseconds for which the method will wait if the lock is not available. A value of
/// <code>0</code> specifies that the method should not wait, and a value of <see cref="Timeout.Infinite"/> or
/// <code>-1</code> specifies that the method should wait indefinitely until the lock is entered.
/// </param>
/// <returns>
/// <code>true</code> if the lock was entered, <code>false</code> otherwise.
/// </returns>
public bool TryEnter(int millisecondsTimeout) =>
TryEnter(TimeSpan.FromMilliseconds(millisecondsTimeout));

/// <summary>
/// Exits the lock.
/// </summary>
public void Exit() => Monitor.Exit(this);

/// <summary>
/// Enters the lock and returns a <see cref="Scope"/> that may be disposed to exit the lock. Once the method returns,
/// the calling thread would be the only thread that holds the lock. This method is intended to be used along with a
/// language construct that would automatically dispose the <see cref="Scope"/>, such as with the C# <code>using</code>
/// statement.
/// </summary>
/// <returns>
/// A <see cref="Scope"/> that may be disposed to exit the lock.
/// </returns>
public Scope EnterScope()
{
Enter();
return new Scope(this);
}

/// <summary>
/// A disposable structure that is returned by <see cref="EnterScope()"/>, which when disposed, exits the lock.
/// </summary>
public readonly ref struct Scope(Lock owner)
{
/// <summary>
/// Exits the lock.
/// </summary>
public void Dispose() => owner.Exit();
}
}

#else
using System.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(System.Threading.Lock))]
#endif
64 changes: 53 additions & 11 deletions Jint/Extensions/SearchValues.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,86 @@
#if !NET8_0_OR_GREATER

using System.Runtime.CompilerServices;

namespace System.Buffers;

#if !NET8_0_OR_GREATER
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 static SearchValues<char> Create(string input) => new CharSearchValues(input.AsSpan());
internal static SearchValues<char> Create(ReadOnlySpan<char> input) => new CharSearchValues(input);
}
#endif

#if !NET8_0_OR_GREATER
internal abstract class SearchValues<T>
{
public abstract bool Contains(T data);
}

internal sealed class SearchValues<T>
file sealed class CharSearchValues : SearchValues<char>
{
private readonly bool[] _data;
private readonly char _min;
private readonly char _max;

internal SearchValues(ReadOnlySpan<char> input)
internal CharSearchValues(ReadOnlySpan<char> input)
{
_min = char.MaxValue;
_max = char.MinValue;
var max = char.MinValue;
foreach (var c in input)
{
_min = (char) Math.Min(_min, c);
_max = (char) Math.Max(_max, c);
max = (char) Math.Max(max, c);
}

_data = new bool[_max - _min + 1];
_data = new bool[max - _min + 1];
foreach (var c in input)
{
_data[c - _min] = true;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(char c)
public override bool Contains(char c)
{
var i = (uint) (c - _min);
var temp = _data;
return i < temp.Length && temp[i];
}
}
#endif

#if !NET9_0_OR_GREATER
internal readonly struct StringSearchValues
{
private readonly HashSet<string> _data;

internal StringSearchValues(ReadOnlySpan<string> input, StringComparison comparer)
{
if (comparer != StringComparison.Ordinal)
{
Jint.Runtime.Throw.ArgumentException("comparer", nameof(comparer));
}

_data = new HashSet<string>(StringComparer.Ordinal);
foreach (var s in input)
{
_data.Add(s);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(string s) => _data.Contains(s);
}
#else
internal readonly struct StringSearchValues
{
private readonly SearchValues<string> _data;

internal StringSearchValues(ReadOnlySpan<string> input, StringComparison comparer)
{
_data = SearchValues.Create(input, comparer);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(string s) => _data.Contains(s);
}
#endif
5 changes: 4 additions & 1 deletion Jint/Jint.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<NeutralLanguage>en-US</NeutralLanguage>
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net8.0;net10.0</TargetFrameworks>

<AssemblyOriginatorKeyFile>Jint.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
Expand All @@ -19,6 +19,9 @@

<NoWarn>$(NoWarn);1591</NoWarn>

<!-- TODO AOT improvements -->
<NoWarn>$(NoWarn);IL3050;IL2080;IL2075;IL2072;IL2070;IL2069;IL2067;IL2060;</NoWarn>

<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>

<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Atomics/AtomicsInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public WaiterKey(object buffer, int byteIndex)

private sealed class WaiterList
{
private readonly object _lock = new();
private readonly Lock _lock = new();
private readonly List<Waiter> _syncWaiters = [];
private readonly List<AsyncWaiter> _asyncWaiters = [];

Expand Down
22 changes: 10 additions & 12 deletions Jint/Native/Intl/CollatorConstructor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Buffers;
using System.Globalization;
using Jint.Native.Function;
using Jint.Native.Object;
Expand All @@ -13,17 +14,17 @@ namespace Jint.Native.Intl;
internal sealed class CollatorConstructor : Constructor
{
private static readonly JsString _functionName = new("Collator");
private static readonly HashSet<string> LocaleMatcherValues = ["lookup", "best fit"];
private static readonly HashSet<string> UsageValues = ["sort", "search"];
private static readonly HashSet<string> SensitivityValues = ["base", "accent", "case", "variant"];
private static readonly HashSet<string> CaseFirstValues = ["upper", "lower", "false"];
private static readonly StringSearchValues LocaleMatcherValues = new(["lookup", "best fit"], StringComparison.Ordinal);
private static readonly StringSearchValues UsageValues = new(["sort", "search"], StringComparison.Ordinal);
private static readonly StringSearchValues SensitivityValues = new(["base", "accent", "case", "variant"], StringComparison.Ordinal);
private static readonly StringSearchValues CaseFirstValues = new(["upper", "lower", "false"], StringComparison.Ordinal);

// Valid collation types per CLDR (excluding "standard" and "search" which are special)
private static readonly HashSet<string> ValidCollationTypes = [
private static readonly StringSearchValues ValidCollationTypes = new([
"big5han", "compat", "dict", "direct", "ducet", "emoji", "eor",
"gb2312", "phonebk", "phonebook", "phonetic", "pinyin", "reformed",
"searchjl", "stroke", "trad", "unihan", "zhuyin", "default"
];
], StringComparison.Ordinal);

public CollatorConstructor(
Engine engine,
Expand Down Expand Up @@ -169,7 +170,7 @@ private static string BuildLocaleWithExtensions(string baseLocale,
return baseLocale + "-u-" + string.Join("-", extensions);
}

private string GetStringOption(ObjectInstance options, string property, HashSet<string> values, string fallback)
private string GetStringOption(ObjectInstance options, string property, in StringSearchValues values, string fallback)
{
var value = options.Get(property);
if (value.IsUndefined())
Expand All @@ -179,12 +180,9 @@ private string GetStringOption(ObjectInstance options, string property, HashSet<

var stringValue = TypeConverter.ToString(value);

if (values != null && values.Count > 0)
if (!values.Contains(stringValue))
{
if (!values.Contains(stringValue))
{
Throw.RangeError(_realm, $"Invalid value '{stringValue}' for option '{property}'");
}
Throw.RangeError(_realm, $"Invalid value '{stringValue}' for option '{property}'");
}

return stringValue;
Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/Intl/Data/CompactPatterns.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;

namespace Jint.Native.Intl.Data;

/// <summary>
Expand All @@ -6,7 +8,7 @@ namespace Jint.Native.Intl.Data;
/// </summary>
internal static class CompactPatterns
{
private static readonly object _lock = new object();
private static readonly Lock _lock = new();
private static Dictionary<string, LocaleCompactData>? _patterns;
private static volatile bool _loaded;

Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/Intl/Data/ListPatternsData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;

namespace Jint.Native.Intl.Data;

/// <summary>
Expand All @@ -6,7 +8,7 @@ namespace Jint.Native.Intl.Data;
/// </summary>
internal static class ListPatternsData
{
private static readonly object _lock = new();
private static readonly Lock _lock = new();
private static Dictionary<string, Dictionary<string, ListPatterns>>? _patterns;
private static volatile bool _loaded;

Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/Intl/Data/LocaleData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;

namespace Jint.Native.Intl.Data;

/// <summary>
Expand All @@ -6,7 +8,7 @@ namespace Jint.Native.Intl.Data;
/// </summary>
internal static class LocaleData
{
private static readonly object _lock = new object();
private static readonly Lock _lock = new();
private static Dictionary<string, string>? _tagMappings;
private static Dictionary<string, string>? _languageMappings;
private static Dictionary<string, ComplexLanguageMapping>? _complexLanguageMappings;
Expand Down
4 changes: 2 additions & 2 deletions Jint/Native/Intl/Data/RelativeTimePatternsData.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Threading;

namespace Jint.Native.Intl.Data;

Expand All @@ -8,7 +8,7 @@ namespace Jint.Native.Intl.Data;
/// </summary>
internal static class RelativeTimePatternsData
{
private static readonly object _lock = new();
private static readonly Lock _lock = new();
private static Dictionary<string, LocaleRelativeTimeData>? _data;
private static volatile bool _loaded;

Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/Intl/Data/TimeZoneData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;

namespace Jint.Native.Intl.Data;

/// <summary>
Expand All @@ -8,7 +10,7 @@ internal static class TimeZoneData
{
// Lazy initialization to avoid startup cost
private static Dictionary<string, string>? _caseInsensitiveLookup;
private static readonly object _lock = new();
private static readonly Lock _lock = new();

/// <summary>
/// Canonical IANA timezone identifiers (Zone names only - primary identifiers).
Expand Down
4 changes: 3 additions & 1 deletion Jint/Native/Intl/Data/UnitPatternsData.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Threading;

namespace Jint.Native.Intl.Data;

/// <summary>
Expand All @@ -6,7 +8,7 @@ namespace Jint.Native.Intl.Data;
/// </summary>
internal static class UnitPatternsData
{
private static readonly object _lock = new();
private static readonly Lock _lock = new();
private static Dictionary<string, Dictionary<string, string>>? _patterns;
private static volatile bool _loaded;

Expand Down
Loading