From e5d60cb58fdcbd84478b819e318b81d9ae8527a2 Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Fri, 13 Oct 2023 08:48:44 +0100 Subject: [PATCH] Support converting JsTypedArray instances under interop (#1646) Also some utility extensions for float typed arrays. Added tests for changes. --- Jint.Tests/Runtime/EngineTests.cs | 15 +++++++++ Jint.Tests/Runtime/TypedArrayInteropTests.cs | 28 ++++++++++++++++ Jint/JsValueExtensions.cs | 34 ++++++++++++++++++++ Jint/Native/Object/ObjectInstance.cs | 22 +++++++++++++ 4 files changed, 99 insertions(+) diff --git a/Jint.Tests/Runtime/EngineTests.cs b/Jint.Tests/Runtime/EngineTests.cs index e4ec411d0f..d898f99a8e 100644 --- a/Jint.Tests/Runtime/EngineTests.cs +++ b/Jint.Tests/Runtime/EngineTests.cs @@ -3037,6 +3037,21 @@ public void ImportModuleShouldTriggerBeforeEvaluateEvents() Assert.Equal(2, beforeEvaluateTriggeredCount); } + [Fact] + public void ShouldConvertJsTypedArraysCorrectly() + { + var engine = new Engine(); + + var float32 = new float [] { 42f, 23 }; + + engine.SetValue("float32", float32); + engine.SetValue("testFloat32Array", new Action(v => Assert.Equal(v, float32))); + + engine.Evaluate(@" + testFloat32Array(new Float32Array(float32)); + "); + } + private static void TestBeforeEvaluateEvent(Action call, string expectedSource) { var engine = new Engine(); diff --git a/Jint.Tests/Runtime/TypedArrayInteropTests.cs b/Jint.Tests/Runtime/TypedArrayInteropTests.cs index 772936d11c..1fee21b382 100644 --- a/Jint.Tests/Runtime/TypedArrayInteropTests.cs +++ b/Jint.Tests/Runtime/TypedArrayInteropTests.cs @@ -120,6 +120,34 @@ public void CanInteropWithBigUint64() Assert.Equal(source, fromEngine.AsBigUint64Array()); } + [Fact] + public void CanInteropWithFloat32() + { + var engine = new Engine(); + var source = new float[] { 42f, 12f }; + + engine.SetValue("testSubject", engine.Realm.Intrinsics.Float32Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Float32Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsFloat32Array()); + Assert.Equal(source, fromEngine.AsFloat32Array()); + } + + [Fact] + public void CanInteropWithFloat64() + { + var engine = new Engine(); + var source = new double[] { 42f, 12f }; + + engine.SetValue("testSubject", engine.Realm.Intrinsics.Float64Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Float64Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsFloat64Array()); + Assert.Equal(source, fromEngine.AsFloat64Array()); + } + private static void ValidateCreatedTypeArray(Engine engine, string arrayName) { Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString()); diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs index ab861f194a..74a9c708c3 100644 --- a/Jint/JsValueExtensions.cs +++ b/Jint/JsValueExtensions.cs @@ -391,6 +391,40 @@ public static ulong[] AsBigUint64Array(this JsValue value) return ((JsTypedArray) value).ToNativeArray(); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFloat32Array(this JsValue value) + { + return value is JsTypedArray { _arrayElementType: TypedArrayElementType.Float32 }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float[] AsFloat32Array(this JsValue value) + { + if (!value.IsFloat32Array()) + { + ThrowWrongTypeException(value, "Float32Array"); + } + + return ((JsTypedArray) value).ToNativeArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFloat64Array(this JsValue value) + { + return value is JsTypedArray { _arrayElementType: TypedArrayElementType.Float64 }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double[] AsFloat64Array(this JsValue value) + { + if (!value.IsFloat64Array()) + { + ThrowWrongTypeException(value, "Float64Array"); + } + + return ((JsTypedArray) value).ToNativeArray(); + } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Jint/Native/Object/ObjectInstance.cs b/Jint/Native/Object/ObjectInstance.cs index b95ab8fd64..4c7d7e8735 100644 --- a/Jint/Native/Object/ObjectInstance.cs +++ b/Jint/Native/Object/ObjectInstance.cs @@ -10,6 +10,7 @@ using Jint.Native.RegExp; using Jint.Native.String; using Jint.Native.Symbol; +using Jint.Native.TypedArray; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; @@ -1054,6 +1055,27 @@ private object ToObject(ObjectTraverseStack stack) converted = result; break; } + + if (this is JsTypedArray typedArrayInstance) + { + converted = typedArrayInstance._arrayElementType switch + { + TypedArrayElementType.Int8 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Int16 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Int32 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.BigInt64 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Float32 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Float64 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Uint8 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Uint8C => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Uint16 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.Uint32 => typedArrayInstance.ToNativeArray(), + TypedArrayElementType.BigUint64 => typedArrayInstance.ToNativeArray(), + _ => throw new ArgumentOutOfRangeException() + }; + + break; + } if (this is BigIntInstance bigIntInstance) {