Skip to content

Commit

Permalink
Read DataView and ArrayBuffer as byte array from engine (#1786)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceice authored Feb 21, 2024
1 parent 4fe84ea commit c2bb947
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Jint.Tests/Runtime/Domain/TextDecoder.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions Jint.Tests/Runtime/JsValueConversionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Jint.Native;
using Jint.Runtime;

namespace Jint.Tests.Runtime
{
Expand Down Expand Up @@ -164,5 +165,38 @@ public void ShouldBeUndefined()
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 changes: 33 additions & 0 deletions Jint.Tests/Runtime/TextTests.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions Jint/JsValueExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,52 @@ public static string AsString(this JsValue value)
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)
{
Expand Down
18 changes: 18 additions & 0 deletions Jint/Native/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,24 @@ private object ToObject(ObjectTraverseStack stack)
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;
Expand Down

0 comments on commit c2bb947

Please sign in to comment.