From 443bc3cc6c3c6e316df0cea5c5eb6d85d8c13ce8 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 25 Mar 2025 14:46:56 +0000 Subject: [PATCH] Add `JSTypedArray.init(buffer:)` initializer Without this initializer, the only way to create a TypedArray with a UnsafeBufferPointer was to convert it to an Array first and then create a new TypedArray from it. --- .../BasicObjects/JSTypedArray.swift | 24 ++++++++++++++----- .../JSTypedArrayTests.swift | 12 ++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift index 19602a314..b9d8d520c 100644 --- a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift +++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift @@ -46,13 +46,10 @@ public class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiteral wh /// /// - Parameter array: The array that will be copied to create a new instance of TypedArray public convenience init(_ array: [Element]) { - let jsArrayRef = array.withUnsafeBufferPointer { ptr in - // Retain the constructor function to avoid it being released before calling `swjs_create_typed_array` - withExtendedLifetime(Self.constructor!) { ctor in - swjs_create_typed_array(ctor.id, ptr.baseAddress, Int32(array.count)) - } + let object = array.withUnsafeBufferPointer { buffer in + Self.createTypedArray(from: buffer) } - self.init(unsafelyWrapping: JSObject(id: jsArrayRef)) + self.init(unsafelyWrapping: object) } /// Convenience initializer for `Sequence`. @@ -60,6 +57,21 @@ public class JSTypedArray: JSBridgedClass, ExpressibleByArrayLiteral wh self.init(Array(sequence)) } + /// Initialize a new instance of TypedArray in JavaScript environment with given buffer contents. + /// + /// - Parameter buffer: The buffer that will be copied to create a new instance of TypedArray + public convenience init(buffer: UnsafeBufferPointer) { + self.init(unsafelyWrapping: Self.createTypedArray(from: buffer)) + } + + private static func createTypedArray(from buffer: UnsafeBufferPointer) -> JSObject { + // Retain the constructor function to avoid it being released before calling `swjs_create_typed_array` + let jsArrayRef = withExtendedLifetime(Self.constructor!) { ctor in + swjs_create_typed_array(ctor.id, buffer.baseAddress, Int32(buffer.count)) + } + return JSObject(id: jsArrayRef) + } + /// Length (in bytes) of the typed array. /// The value is established when a TypedArray is constructed and cannot be changed. /// If the TypedArray is not specifying a `byteOffset` or a `length`, the `length` of the referenced `ArrayBuffer` will be returned. diff --git a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift index 8e2556f8d..dcc5fb26a 100644 --- a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift +++ b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift @@ -97,4 +97,16 @@ final class JSTypedArrayTests: XCTestCase { XCTAssertEqual(toString(array.jsValue.object!), jsStringify(Array(0..<100))) } + + func testInitWithBufferPointer() { + let buffer = UnsafeMutableBufferPointer.allocate(capacity: 20) + defer { buffer.deallocate() } + for i in 0..<20 { + buffer[i] = Float32(i) + } + let typedArray = JSTypedArray(buffer: UnsafeBufferPointer(buffer)) + for i in 0..<20 { + XCTAssertEqual(typedArray[i], Float32(i)) + } + } }