瀏覽代碼

Read DataView and ArrayBuffer as byte array from engine (#1786)

Michael Kriese 1 年之前
父節點
當前提交
c2bb947c18

+ 15 - 0
Jint.Tests/Runtime/Domain/TextDecoder.cs

@@ -0,0 +1,15 @@
+using System.Text;
+
+namespace Jint.Tests.Runtime.Domain;
+
+/// <summary>
+/// https://encoding.spec.whatwg.org/#textdecoder
+/// </summary>
+/// <remarks>Public API, do not make internal!</remarks>
+public sealed class TextDecoder
+{
+    public string Decode() => string.Empty;
+
+
+    public string Decode(byte[] buff) => Encoding.UTF8.GetString(buff);
+}

+ 34 - 0
Jint.Tests/Runtime/JsValueConversionTests.cs

@@ -1,4 +1,5 @@
 using Jint.Native;
+using Jint.Runtime;
 
 namespace Jint.Tests.Runtime
 {
@@ -164,5 +165,38 @@ namespace Jint.Tests.Runtime
             Assert.Equal(false, value.IsString());
             Assert.Equal(true, value.IsUndefined());
         }
+
+        [Fact]
+        public void ShouldConvertArrayBuffer()
+        {
+            var value = _engine.Evaluate("new Uint8Array([102, 111, 111]).buffer");
+            Assert.Equal(true, value.IsArrayBuffer());
+            Assert.Equal([102, 111, 111], value.AsArrayBuffer());
+            Assert.Equal([102, 111, 111], value.ToObject() as byte[]);
+
+            (value as JsArrayBuffer).DetachArrayBuffer();
+
+            Assert.Equal(true, value.IsArrayBuffer());
+            Assert.Equal(null, value.AsArrayBuffer());
+            Assert.Throws<JavaScriptException>(value.ToObject);
+            Assert.Throws<ArgumentException>(JsValue.Undefined.AsArrayBuffer);
+        }
+
+        [Fact]
+        public void ShouldConvertDataView()
+        {
+            var value = _engine.Evaluate("new DataView(new Uint8Array([102, 102, 111, 111, 111]).buffer, 1, 3)");
+
+            Assert.Equal(true, value.IsDataView());
+            Assert.Equal([102, 111, 111], value.AsDataView());
+            Assert.Equal([102, 111, 111], value.ToObject() as byte[]);
+
+            (value as JsDataView)._viewedArrayBuffer.DetachArrayBuffer();
+
+            Assert.Equal(true, value.IsDataView());
+            Assert.Equal(null, value.AsDataView());
+            Assert.Throws<JavaScriptException>(value.ToObject);
+            Assert.Throws<ArgumentException>(JsValue.Undefined.AsDataView);
+        }
     }
 }

+ 33 - 0
Jint.Tests/Runtime/TextTests.cs

@@ -0,0 +1,33 @@
+using Jint.Runtime.Interop;
+using Jint.Tests.Runtime.Domain;
+
+namespace Jint.Tests.Runtime;
+
+public sealed class TextTests
+{
+    private readonly Engine _engine;
+
+    public TextTests()
+    {
+        _engine = new Engine()
+               .SetValue("log", new Action<object>(Console.WriteLine))
+               .SetValue("assert", new Action<bool>(Assert.True))
+               .SetValue("equal", new Action<object, object>(Assert.Equal));
+        _engine
+               .SetValue("TextDecoder", TypeReference.CreateTypeReference<TextDecoder>(_engine))
+           ;
+    }
+    private object RunTest(string source)
+    {
+        return _engine.Evaluate(source).ToObject();
+    }
+
+    [Fact]
+    public void CanDecode()
+    {
+        Assert.Equal("", RunTest($"new TextDecoder().decode()"));
+        Assert.Equal("foo", RunTest($"new TextDecoder().decode(new Uint8Array([102,111,111]))"));
+        Assert.Equal("foo", RunTest($"new TextDecoder().decode(new Uint8Array([102,111,111]).buffer)"));
+        Assert.Equal("foo", RunTest($"new TextDecoder().decode(new DataView(new Uint8Array([0,102,111,111,0]).buffer,1,3))"));
+    }
+}

+ 46 - 0
Jint/JsValueExtensions.cs

@@ -246,6 +246,52 @@ public static class JsValueExtensions
         return value.ToString();
     }
 
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool IsArrayBuffer(this JsValue value)
+    {
+        return value is JsArrayBuffer;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static byte[]? AsArrayBuffer(this JsValue value)
+    {
+        if (!value.IsArrayBuffer())
+        {
+            ThrowWrongTypeException(value, "ArrayBuffer");
+        }
+
+        return ((JsArrayBuffer) value)._arrayBufferData;
+    }
+
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static bool IsDataView(this JsValue value)
+    {
+        return value is JsDataView;
+    }
+
+    [MethodImpl(MethodImplOptions.AggressiveInlining)]
+    public static byte[]? AsDataView(this JsValue value)
+    {
+        if (!value.IsDataView())
+        {
+            ThrowWrongTypeException(value, "DataView");
+        }
+
+        var dataView = (JsDataView) value;
+
+        if (dataView._viewedArrayBuffer?._arrayBufferData == null)
+        {
+            return null; // should not happen
+        }
+
+        // create view
+        var res = new byte[dataView._byteLength];
+        Array.Copy(dataView._viewedArrayBuffer._arrayBufferData!, dataView._byteOffset, res, 0, dataView._byteLength);
+        return res;
+    }
+
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     public static bool IsUint8Array(this JsValue value)
     {

+ 18 - 0
Jint/Native/Object/ObjectInstance.cs

@@ -1081,6 +1081,24 @@ namespace Jint.Native.Object
                         break;
                     }
 
+                    if (this is JsArrayBuffer arrayBuffer)
+                    {
+                        // TODO: What to do here when buffer is detached? We're not allowed to return null
+                        arrayBuffer.AssertNotDetached();
+                        converted = arrayBuffer.ArrayBufferData;
+                        break;
+                    }
+
+                    if (this is JsDataView dataView)
+                    {
+                        // TODO: What to do here when buffer is detached? We're not allowed to return null
+                        dataView._viewedArrayBuffer!.AssertNotDetached();
+                        var res = new byte[dataView._byteLength];
+                        System.Array.Copy(dataView._viewedArrayBuffer._arrayBufferData!, dataView._byteOffset, res, 0, dataView._byteLength);
+                        converted = res;
+                        break;
+                    }
+
                     if (this is BigIntInstance bigIntInstance)
                     {
                         converted = bigIntInstance.BigIntData._value;