瀏覽代碼

Fix issues found with latest test262 suite (#1912)

Marko Lahma 1 年之前
父節點
當前提交
0ce19d88e6

+ 17 - 13
Jint/Extensions/WebEncoders.cs

@@ -106,15 +106,16 @@ internal static class WebEncoders
     /// Encodes <paramref name="input"/> using base64url encoding.
     /// </summary>
     /// <param name="input">The binary input to encode.</param>
+    /// <param name="omitPadding"></param>
     /// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
-    public static string Base64UrlEncode(byte[] input)
+    public static string Base64UrlEncode(byte[] input, bool omitPadding)
     {
         if (input == null)
         {
             throw new ArgumentNullException(nameof(input));
         }
 
-        return Base64UrlEncode(input, offset: 0, count: input.Length);
+        return Base64UrlEncode(input, offset: 0, count: input.Length, omitPadding);
     }
 
     /// <summary>
@@ -123,8 +124,9 @@ internal static class WebEncoders
     /// <param name="input">The binary input to encode.</param>
     /// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
     /// <param name="count">The number of bytes from <paramref name="input"/> to encode.</param>
+    /// <param name="omitPadding"></param>
     /// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
-    public static string Base64UrlEncode(byte[] input, int offset, int count)
+    public static string Base64UrlEncode(byte[] input, int offset, int count, bool omitPadding)
     {
         if (input == null)
         {
@@ -132,7 +134,7 @@ internal static class WebEncoders
         }
 
 #if NETCOREAPP
-        return Base64UrlEncode(input.AsSpan(offset, count));
+        return Base64UrlEncode(input.AsSpan(offset, count), omitPadding);
 #else
             // Special-case empty input
             if (count == 0)
@@ -141,7 +143,7 @@ internal static class WebEncoders
             }
 
             var buffer = new char[GetArraySizeRequiredToEncode(count)];
-            var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count);
+            var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count, omitPadding);
 
             return new string(buffer, startIndex: 0, length: numBase64Chars);
 #endif
@@ -162,10 +164,11 @@ internal static class WebEncoders
     /// <paramref name="input"/>.
     /// </param>
     /// <param name="count">The number of <c>byte</c>s from <paramref name="input"/> to encode.</param>
+    /// <param name="omitPadding"></param>
     /// <returns>
     /// The number of characters written to <paramref name="output"/>, less any padding characters.
     /// </returns>
-    public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count)
+    public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count, bool omitPadding)
     {
         if (input == null)
         {
@@ -188,7 +191,7 @@ internal static class WebEncoders
         }
 
 #if NETCOREAPP
-        return Base64UrlEncode(input.AsSpan(offset, count), output.AsSpan(outputOffset));
+        return Base64UrlEncode(input.AsSpan(offset, count), output.AsSpan(outputOffset), omitPadding);
 #else
             // Special-case empty input.
             if (count == 0)
@@ -213,7 +216,7 @@ internal static class WebEncoders
                 {
                     output[i] = '_';
                 }
-                else if (ch == '=')
+                else if (omitPadding && ch == '=')
                 {
                     // We've reached a padding character; truncate the remainder.
                     return i - outputOffset;
@@ -226,7 +229,7 @@ internal static class WebEncoders
 
     /// <summary>
     /// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
-    /// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
+    /// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int, bool)"/> method.
     /// </summary>
     /// <param name="count">The number of characters to encode.</param>
     /// <returns>
@@ -243,8 +246,9 @@ internal static class WebEncoders
     /// Encodes <paramref name="input"/> using base64url encoding.
     /// </summary>
     /// <param name="input">The binary input to encode.</param>
+    /// <param name="omitPadding"></param>
     /// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
-    public static string Base64UrlEncode(ReadOnlySpan<byte> input)
+    public static string Base64UrlEncode(ReadOnlySpan<byte> input, bool omitPadding)
     {
         if (input.IsEmpty)
         {
@@ -258,7 +262,7 @@ internal static class WebEncoders
             ? stackalloc char[bufferSize]
             : bufferToReturnToPool = ArrayPool<char>.Shared.Rent(bufferSize);
 
-        var numBase64Chars = Base64UrlEncode(input, buffer);
+        var numBase64Chars = Base64UrlEncode(input, buffer, omitPadding);
         var base64Url = new string(buffer.Slice(0, numBase64Chars));
 
         if (bufferToReturnToPool != null)
@@ -269,7 +273,7 @@ internal static class WebEncoders
         return base64Url;
     }
 
-    private static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output)
+    private static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output, bool omitPadding)
     {
         Debug.Assert(output.Length >= GetArraySizeRequiredToEncode(input.Length));
 
@@ -294,7 +298,7 @@ internal static class WebEncoders
             {
                 output[i] = '_';
             }
-            else if (ch == '=')
+            else if (omitPadding && ch == '=')
             {
                 // We've reached a padding character; truncate the remainder.
                 return i;

+ 1 - 1
Jint/Native/Array/ArrayPrototype.cs

@@ -1267,7 +1267,7 @@ namespace Jint.Native.Array
                 return s;
             }
 
-            using var sb = new ValueStringBuilder(stackalloc char[256]);
+            using var sb = new ValueStringBuilder();
             sb.Append(s);
             for (uint k = 1; k < len; k++)
             {

+ 5 - 1
Jint/Native/JsTypedArray.cs

@@ -52,7 +52,11 @@ namespace Jint.Native
 
         public uint Length => GetLength();
 
-        internal override uint GetLength() => IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(this, ArrayBufferOrder.Unordered).TypedArrayLength;
+        internal override uint GetLength()
+        {
+            var record = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(this, ArrayBufferOrder.Unordered);
+            return record.IsTypedArrayOutOfBounds ? 0 : record.TypedArrayLength;
+        }
 
         internal override bool IsArrayLike => true;
 

+ 10 - 10
Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs

@@ -129,14 +129,14 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
                     {
                         if (chunkLength == 1)
                         {
-                            return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 chunk length."), read);
+                            return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 chunk length."), read);
                         }
 
                         DecodeBase64Chunk(engine, bytes, chunk, chunkLength, throwOnExtraBits: false);
                     }
                     else // strict
                     {
-                        return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 chunk length in strict mode."), read);
+                        return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 chunk length in strict mode."), read);
                     }
                 }
 
@@ -150,7 +150,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
             {
                 if (chunkLength < 2)
                 {
-                    return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid '=' placement in base64 string."), read);
+                    return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid '=' placement in base64 string."), read);
                 }
 
                 index = SkipAsciiWhitespace(input, index);
@@ -163,13 +163,13 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
                             return new FromEncodingResult(bytes.ToArray(), Error: null, read);
                         }
 
-                        return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 string termination."), read);
+                        return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 string termination."), read);
                     }
 
                     currentChar = input[index];
                     if (currentChar != '=')
                     {
-                        return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Expected '=' in base64 string."), read);
+                        return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Expected '=' in base64 string."), read);
                     }
 
                     index = SkipAsciiWhitespace(input, index + 1);
@@ -177,7 +177,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
 
                 if (index < length)
                 {
-                    return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Extra characters after base64 string."), read);
+                    return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Extra characters after base64 string."), read);
                 }
 
                 DecodeBase64Chunk(engine, bytes, chunk, chunkLength, throwOnExtraBits);
@@ -188,7 +188,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
             {
                 if (currentChar is '+' or '/')
                 {
-                    return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid character in base64url string."), read);
+                    return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid character in base64url string."), read);
                 }
 
                 if (currentChar == '-')
@@ -204,7 +204,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
 
             if (!Base64Alphabet.Contains(currentChar))
             {
-                return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 character."), read);
+                return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 character."), read);
             }
 
             ulong remaining = maxLength - (ulong) bytes.Count;
@@ -318,7 +318,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
 
         if (length % 2 != 0)
         {
-            return new FromEncodingResult(bytes, ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid hex string"), read);
+            return new FromEncodingResult([], ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid hex string"), read);
         }
 
         var byteIndex = 0;
@@ -327,7 +327,7 @@ public sealed class Uint8ArrayConstructor : TypedArrayConstructor
             var hexits = s.AsSpan(read, 2);
             if (!HexAlphabet.Contains(hexits[0]) || !HexAlphabet.Contains(hexits[1]))
             {
-                return new FromEncodingResult(bytes, ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid hex value"), read);
+                return new FromEncodingResult(bytes.AsSpan(0, byteIndex).ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid hex value"), read);
             }
 
 #if SUPPORTS_SPAN_PARSE

+ 9 - 5
Jint/Native/TypedArray/Uint8ArrayPrototype.cs

@@ -60,13 +60,13 @@ internal sealed class Uint8ArrayPrototype : Prototype
 
         var byteLength = taRecord.TypedArrayLength;
         var result = Uint8ArrayConstructor.FromBase64(_engine, s.ToString(), alphabet.ToString(), lastChunkHandling.ToString(), byteLength);
+        SetUint8ArrayBytes(into, result.Bytes);
+
         if (result.Error is not null)
         {
             throw result.Error;
         }
 
-        SetUint8ArrayBytes(into, result.Bytes);
-
         var resultObject = OrdinaryObjectCreate(_engine, _engine.Intrinsics.Object);
         resultObject.CreateDataPropertyOrThrow("read", result.Read);
         resultObject.CreateDataPropertyOrThrow("written", result.Bytes.Length);
@@ -105,13 +105,13 @@ internal sealed class Uint8ArrayPrototype : Prototype
 
         var byteLength = taRecord.TypedArrayLength;
         var result = Uint8ArrayConstructor.FromHex(_engine, s.ToString(), byteLength);
+        SetUint8ArrayBytes(into, result.Bytes);
+
         if (result.Error is not null)
         {
             throw result.Error;
         }
 
-        SetUint8ArrayBytes(into, result.Bytes);
-
         var resultObject = OrdinaryObjectCreate(_engine, _engine.Intrinsics.Object);
         resultObject.CreateDataPropertyOrThrow("read", result.Read);
         resultObject.CreateDataPropertyOrThrow("written", result.Bytes.Length);
@@ -136,10 +136,14 @@ internal sealed class Uint8ArrayPrototype : Prototype
         if (alphabet == "base64")
         {
             outAscii = Convert.ToBase64String(toEncode);
+            if (omitPadding)
+            {
+                outAscii = outAscii.TrimEnd('=');
+            }
         }
         else
         {
-            outAscii = WebEncoders.Base64UrlEncode(toEncode);
+            outAscii = WebEncoders.Base64UrlEncode(toEncode, omitPadding);
         }
 
         return outAscii;