From 2bac14da0ae8c66bf2f313050e5bad57a42381d0 Mon Sep 17 00:00:00 2001 From: yonihemi Date: Tue, 25 Jan 2022 14:19:09 +0800 Subject: [PATCH 1/2] Fix detached ArrayBuffer errors --- Runtime/src/index.ts | 2 +- Runtime/src/memory.ts | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 74abed94d..baf9ffd17 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -177,7 +177,7 @@ export class SwiftRuntime { }, swjs_decode_string: (bytes_ptr: pointer, length: number) => { - const bytes = this.memory.bytes.subarray( + const bytes = this.memory.bytes().subarray( bytes_ptr, bytes_ptr + length ); diff --git a/Runtime/src/memory.ts b/Runtime/src/memory.ts index a8391cfd1..3c010a5f5 100644 --- a/Runtime/src/memory.ts +++ b/Runtime/src/memory.ts @@ -3,29 +3,27 @@ import { pointer } from "./types"; export class Memory { readonly rawMemory: WebAssembly.Memory; - readonly bytes: Uint8Array; - readonly dataView: DataView; private readonly heap = new SwiftRuntimeHeap(); constructor(exports: WebAssembly.Exports) { this.rawMemory = exports.memory as WebAssembly.Memory; - this.bytes = new Uint8Array(this.rawMemory.buffer); - this.dataView = new DataView(this.rawMemory.buffer); } retain = (value: any) => this.heap.retain(value); getObject = (ref: number) => this.heap.referenceHeap(ref); release = (ref: number) => this.heap.release(ref); - writeBytes = (ptr: pointer, bytes: Uint8Array) => - this.bytes.set(bytes, ptr); + bytes = () => new Uint8Array(this.rawMemory.buffer); + dataView = () => new DataView(this.rawMemory.buffer); - readUint32 = (ptr: pointer) => this.dataView.getUint32(ptr, true); - readFloat64 = (ptr: pointer) => this.dataView.getFloat64(ptr, true); + writeBytes = (ptr: pointer, bytes: Uint8Array) => this.bytes().set(bytes, ptr); + + readUint32 = (ptr: pointer) => this.dataView().getUint32(ptr, true); + readFloat64 = (ptr: pointer) => this.dataView().getFloat64(ptr, true); writeUint32 = (ptr: pointer, value: number) => - this.dataView.setUint32(ptr, value, true); + this.dataView().setUint32(ptr, value, true); writeFloat64 = (ptr: pointer, value: number) => - this.dataView.setFloat64(ptr, value, true); + this.dataView().setFloat64(ptr, value, true); } From b7a43ea4495963f769cf8594bbb028fdc8f234fb Mon Sep 17 00:00:00 2001 From: yonihemi Date: Tue, 25 Jan 2022 15:17:48 +0800 Subject: [PATCH 2/2] Add test for detached ArrayBuffer --- IntegrationTests/TestSuites/Package.swift | 6 +++++- .../TestSuites/Sources/CHelpers/helpers.c | 4 ++++ .../TestSuites/Sources/CHelpers/include/helpers.h | 5 +++++ .../Sources/CHelpers/include/module.modulemap | 4 ++++ .../TestSuites/Sources/PrimaryTests/main.swift | 13 ++++++++++++- 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 IntegrationTests/TestSuites/Sources/CHelpers/helpers.c create mode 100644 IntegrationTests/TestSuites/Sources/CHelpers/include/helpers.h create mode 100644 IntegrationTests/TestSuites/Sources/CHelpers/include/module.modulemap diff --git a/IntegrationTests/TestSuites/Package.swift b/IntegrationTests/TestSuites/Package.swift index 1bb48648b..0344d0499 100644 --- a/IntegrationTests/TestSuites/Package.swift +++ b/IntegrationTests/TestSuites/Package.swift @@ -23,7 +23,11 @@ let package = Package( ], dependencies: [.package(name: "JavaScriptKit", path: "../../")], targets: [ - .target(name: "PrimaryTests", dependencies: ["JavaScriptKit"]), + .target(name: "CHelpers"), + .target(name: "PrimaryTests", dependencies: [ + "JavaScriptKit", + "CHelpers", + ]), .target( name: "ConcurrencyTests", dependencies: [ diff --git a/IntegrationTests/TestSuites/Sources/CHelpers/helpers.c b/IntegrationTests/TestSuites/Sources/CHelpers/helpers.c new file mode 100644 index 000000000..8922cb735 --- /dev/null +++ b/IntegrationTests/TestSuites/Sources/CHelpers/helpers.c @@ -0,0 +1,4 @@ +int growMemory(int pages) { + return __builtin_wasm_memory_grow(0, pages); +} + diff --git a/IntegrationTests/TestSuites/Sources/CHelpers/include/helpers.h b/IntegrationTests/TestSuites/Sources/CHelpers/include/helpers.h new file mode 100644 index 000000000..c5505a5a4 --- /dev/null +++ b/IntegrationTests/TestSuites/Sources/CHelpers/include/helpers.h @@ -0,0 +1,5 @@ +/// Ask host to grow WebAssembly module's allocated memory +/// +/// @param pages Number of memory pages to increase memory by. +int growMemory(int pages); + diff --git a/IntegrationTests/TestSuites/Sources/CHelpers/include/module.modulemap b/IntegrationTests/TestSuites/Sources/CHelpers/include/module.modulemap new file mode 100644 index 000000000..3503a233f --- /dev/null +++ b/IntegrationTests/TestSuites/Sources/CHelpers/include/module.modulemap @@ -0,0 +1,4 @@ +module CHelpers { + header "helpers.h" + export * +} diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift index 3f7758e16..5110001fe 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift @@ -1,5 +1,5 @@ import JavaScriptKit - +import CHelpers try test("Literal Conversion") { let global = JSObject.global @@ -735,4 +735,15 @@ try test("Exception") { try expectNotNil(errorObject3) } +/// If WebAssembly.Memory is not accessed correctly (i.e. creating a new view each time), +/// this test will fail with `TypeError: Cannot perform Construct on a detached ArrayBuffer`, +/// since asking to grow memory will detach the backing ArrayBuffer. +/// See https://github.com/swiftwasm/JavaScriptKit/pull/153 +try test("Grow Memory") { + let string = "Hello" + let jsString = JSValue.string(string) + growMemory(1) + try expectEqual(string, jsString.description) +} + Expectation.wait(expectations)