From 04466d0a3ba8769bb97f7b6736d52942ccb32fcc Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sun, 27 Oct 2024 15:12:11 +0200 Subject: [PATCH] Revert back to favoring SearchValues (#1990) --- Jint/Extensions/Character.cs | 7 ++-- Jint/Native/Global/GlobalObject.cs | 32 +++++++++---------- .../TypedArrayConstructor.Uint8Array.cs | 14 +++++--- Jint/Pooling/ValueStringBuilder.cs | 8 ++--- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Jint/Extensions/Character.cs b/Jint/Extensions/Character.cs index 4d65aa1b4..af05873c5 100644 --- a/Jint/Extensions/Character.cs +++ b/Jint/Extensions/Character.cs @@ -5,14 +5,13 @@ namespace Jint.Extensions; internal static class Character { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsInRange(this char c, ushort min, ushort max) => (uint)(c - min) <= (uint)(max - min); - /// /// https://tc39.es/ecma262/#ASCII-word-characters /// + public const string AsciiWordCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAsciiWordCharacter(this char c) => c == '_' || c.IsDecimalDigit() || c.IsInRange('a', 'z') || c.IsInRange('A', 'Z'); + public static bool IsInRange(this char c, ushort min, ushort max) => (uint)(c - min) <= (uint)(max - min); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsOctalDigit(this char c) => c.IsInRange('0', '7'); diff --git a/Jint/Native/Global/GlobalObject.cs b/Jint/Native/Global/GlobalObject.cs index d64b5c84c..8626086e2 100644 --- a/Jint/Native/Global/GlobalObject.cs +++ b/Jint/Native/Global/GlobalObject.cs @@ -274,8 +274,8 @@ private static JsValue IsFinite(JsValue thisObject, JsValue[] arguments) private const string UriReservedString = ";/?:@&=+$,"; private const string UriUnescapedString = "-.!~*'()"; - private static readonly SearchValues UriUnescaped = SearchValues.Create(UriUnescapedString); - private static readonly SearchValues UnescapedUriSet = SearchValues.Create(UriReservedString + UriUnescapedString + '#'); + private static readonly SearchValues UriUnescaped = SearchValues.Create(Character.AsciiWordCharacters + UriUnescapedString); + private static readonly SearchValues UnescapedUriSet = SearchValues.Create(Character.AsciiWordCharacters + UriReservedString + UriUnescapedString + '#'); private static readonly SearchValues ReservedUriSet = SearchValues.Create(UriReservedString + '#'); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -300,7 +300,7 @@ private JsValue EncodeUriComponent(JsValue thisObject, JsValue[] arguments) return Encode(uriString, UriUnescaped); } - private JsValue Encode(string uriString, SearchValues unescapedUriSet) + private JsValue Encode(string uriString, SearchValues allowedCharacters) { var strLen = uriString.Length; var builder = new ValueStringBuilder(uriString.Length); @@ -309,7 +309,7 @@ private JsValue Encode(string uriString, SearchValues unescapedUriSet) for (var k = 0; k < strLen; k++) { var c = uriString[k]; - if (c.IsAsciiWordCharacter() || unescapedUriSet.Contains(c)) + if (allowedCharacters.Contains(c)) { builder.Append(c); } @@ -416,7 +416,7 @@ private JsValue Decode(string uriString, SearchValues? reservedSet) _stringBuilder.Clear(); #if SUPPORTS_SPAN_PARSE - Span octets = stackalloc byte[4]; + Span octets = stackalloc byte[4]; #else var octets = new byte[4]; #endif @@ -576,6 +576,8 @@ private static bool IsDigit(char c, int radix, out int result) return tmp < radix; } + private static readonly SearchValues EscapeAllowList = SearchValues.Create(Character.AsciiWordCharacters + "@*+-./"); + /// /// https://tc39.es/ecma262/#sec-escape-string /// @@ -583,29 +585,27 @@ private JsValue Escape(JsValue thisObject, JsValue[] arguments) { var uriString = TypeConverter.ToString(arguments.At(0)); - var strLen = uriString.Length; - - _stringBuilder.EnsureCapacity(strLen); - _stringBuilder.Clear(); + var builder = new ValueStringBuilder(uriString.Length); - for (var k = 0; k < strLen; k++) + foreach (var c in uriString) { - var c = uriString[k]; - if (c.IsAsciiWordCharacter() || c == '@' || c == '*' || c == '+' || c == '-' || c == '.' || c == '/') + if (EscapeAllowList.Contains(c)) { - _stringBuilder.Append(c); + builder.Append(c); } else if (c < 256) { - _stringBuilder.Append('%').AppendFormat(CultureInfo.InvariantCulture, "{0:X2}", (int) c); + builder.Append('%'); + builder.AppendHex((byte) c); } else { - _stringBuilder.Append("%u").AppendFormat(CultureInfo.InvariantCulture, "{0:X4}", (int) c); + builder.Append("%u"); + builder.Append(((int) c).ToString("X4", CultureInfo.InvariantCulture)); } } - return _stringBuilder.ToString(); + return builder.ToString(); } /// diff --git a/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs b/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs index 64e98a2be..892adc882 100644 --- a/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs +++ b/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs @@ -22,7 +22,12 @@ internal Uint8ArrayConstructor( protected override void Initialize() { const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable; - var properties = new PropertyDictionary(1, checkExistingKeys: false) { ["BYTES_PER_ELEMENT"] = new(new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.AllForbidden)), ["fromBase64"] = new(new ClrFunction(Engine, "fromBase64", FromBase64, 1, PropertyFlag.Configurable), PropertyFlags), ["fromHex"] = new(new ClrFunction(Engine, "fromHex", FromHex, 1, PropertyFlag.Configurable), PropertyFlags), }; + var properties = new PropertyDictionary(3, checkExistingKeys: false) + { + ["BYTES_PER_ELEMENT"] = new(new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.AllForbidden)), + ["fromBase64"] = new(new ClrFunction(Engine, "fromBase64", FromBase64, 1, PropertyFlag.Configurable), PropertyFlags), + ["fromHex"] = new(new ClrFunction(Engine, "fromHex", FromHex, 1, PropertyFlag.Configurable), PropertyFlags), + }; SetProperties(properties); } @@ -92,6 +97,8 @@ internal static JsString GetAndValidateAlphabetOption(Engine engine, ObjectInsta internal readonly record struct FromEncodingResult(byte[] Bytes, JavaScriptException? Error, int Read); + private static readonly SearchValues Base64Alphabet = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + internal static FromEncodingResult FromBase64(Engine engine, string input, string alphabet, string lastChunkHandling, uint maxLength = uint.MaxValue) { if (maxLength == 0) @@ -200,10 +207,7 @@ internal static FromEncodingResult FromBase64(Engine engine, string input, strin } } - if (!currentChar.IsDecimalDigit() - && !char.ToLowerInvariant(currentChar).IsInRange('a', 'z') - && currentChar != '+' - && currentChar != '/') + if (!Base64Alphabet.Contains(currentChar)) { return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 character."), read); } diff --git a/Jint/Pooling/ValueStringBuilder.cs b/Jint/Pooling/ValueStringBuilder.cs index 4252c3518..07763992d 100644 --- a/Jint/Pooling/ValueStringBuilder.cs +++ b/Jint/Pooling/ValueStringBuilder.cs @@ -192,11 +192,11 @@ public void Append(char c) public void AppendHex(byte b) { const string Map = "0123456789ABCDEF"; - Span data = stackalloc char[] - { + ReadOnlySpan data = + [ Map[b / 16], - Map[b % 16], - }; + Map[b % 16] + ]; Append(data); }