From 253cfc9ae2aebe5754800fb2cb8349df809c2c96 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 8 May 2024 12:14:22 +0000 Subject: [PATCH 1/5] Allow async main entry point with JavaScriptEventLoop This change allows the Swift program to have an async main entry point when the JavaScriptEventLoop is installed as the global executor. --- Runtime/src/index.ts | 39 +++++++++++++++++++ Runtime/src/js-value.ts | 8 +++- Runtime/src/types.ts | 1 + .../JavaScriptEventLoop.swift | 9 +++++ Sources/JavaScriptKit/Runtime/index.js | 38 ++++++++++++++++++ Sources/JavaScriptKit/Runtime/index.mjs | 38 ++++++++++++++++++ .../include/_CJavaScriptEventLoop.h | 19 +++++++-- .../_CJavaScriptKit/include/_CJavaScriptKit.h | 3 ++ 8 files changed, 150 insertions(+), 5 deletions(-) diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index a9da3eb9f..ac0adde2a 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -45,6 +45,27 @@ export class SwiftRuntime { } } + main() { + const instance = this.instance; + try { + if (typeof instance.exports.main === "function") { + instance.exports.main(); + } else if ( + typeof instance.exports.__main_argc_argv === "function" + ) { + // Swift 6.0 and later use `__main_argc_argv` instead of `main`. + instance.exports.__main_argc_argv(0, 0); + } + } catch (error) { + if (error instanceof UnsafeEventLoopYield) { + // Ignore the error + return; + } + // Rethrow other errors + throw error; + } + } + private get instance() { if (!this._instance) throw new Error("WebAssembly instance is not set yet"); @@ -419,5 +440,23 @@ export class SwiftRuntime { signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value) ); }, + swjs_unsafe_event_loop_yield: () => { + throw new UnsafeEventLoopYield(); + }, }; } + +/// This error is thrown when yielding event loop control from `swift_task_asyncMainDrainQueue` +/// to JavaScript. This is usually thrown when: +/// - The entry point of the Swift program is `func main() async` +/// - The Swift Concurrency's global executor is hooked by `JavaScriptEventLoop.installGlobalExecutor()` +/// - Calling exported `main` or `__main_argc_argv` function from JavaScript +/// +/// This exception must be caught by the caller of the exported function and the caller should +/// catch this exception and just ignore it. +/// +/// FAQ: Why this error is thrown? +/// This error is thrown to unwind the call stack of the Swift program and return the control to +/// the JavaScript side. Otherwise, the `swift_task_asyncMainDrainQueue` ends up with `abort()` +/// because the event loop expects `exit()` call before the end of the event loop. +class UnsafeEventLoopYield extends Error {} diff --git a/Runtime/src/js-value.ts b/Runtime/src/js-value.ts index 9ff3d065e..1b142de05 100644 --- a/Runtime/src/js-value.ts +++ b/Runtime/src/js-value.ts @@ -82,7 +82,13 @@ export const write = ( is_exception: boolean, memory: Memory ) => { - const kind = writeAndReturnKindBits(value, payload1_ptr, payload2_ptr, is_exception, memory); + const kind = writeAndReturnKindBits( + value, + payload1_ptr, + payload2_ptr, + is_exception, + memory + ); memory.writeUint32(kind_ptr, kind); }; diff --git a/Runtime/src/types.ts b/Runtime/src/types.ts index ff20999ea..55f945b64 100644 --- a/Runtime/src/types.ts +++ b/Runtime/src/types.ts @@ -102,6 +102,7 @@ export interface ImportedFunctions { swjs_i64_to_bigint(value: bigint, signed: bool): ref; swjs_bigint_to_i64(ref: ref, signed: bool): bigint; swjs_i64_to_bigint_slow(lower: number, upper: number, signed: bool): ref; + swjs_unsafe_event_loop_yield: () => void; } export const enum LibraryFeatures { diff --git a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift index 7f6783062..22935bd05 100644 --- a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift +++ b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift @@ -1,5 +1,6 @@ import JavaScriptKit import _CJavaScriptEventLoop +import _CJavaScriptKit // NOTE: `@available` annotations are semantically wrong, but they make it easier to develop applications targeting WebAssembly in Xcode. @@ -90,6 +91,14 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable { public static func installGlobalExecutor() { guard !didInstallGlobalExecutor else { return } + #if compiler(>=5.9) + typealias swift_task_asyncMainDrainQueue_hook_Fn = @convention(thin) (swift_task_asyncMainDrainQueue_original, swift_task_asyncMainDrainQueue_override) -> Void + let swift_task_asyncMainDrainQueue_hook_impl: swift_task_asyncMainDrainQueue_hook_Fn = { _, _ in + _unsafe_event_loop_yield() + } + swift_task_asyncMainDrainQueue_hook = unsafeBitCast(swift_task_asyncMainDrainQueue_hook_impl, to: UnsafeMutableRawPointer?.self) + #endif + typealias swift_task_enqueueGlobal_hook_Fn = @convention(thin) (UnownedJob, swift_task_enqueueGlobal_original) -> Void let swift_task_enqueueGlobal_hook_impl: swift_task_enqueueGlobal_hook_Fn = { job, original in JavaScriptEventLoop.shared.unsafeEnqueue(job) diff --git a/Sources/JavaScriptKit/Runtime/index.js b/Sources/JavaScriptKit/Runtime/index.js index 02dc9382e..9cd1995ac 100644 --- a/Sources/JavaScriptKit/Runtime/index.js +++ b/Sources/JavaScriptKit/Runtime/index.js @@ -366,6 +366,9 @@ (BigInt.asUintN(32, BigInt(upper)) << BigInt(32)); return this.memory.retain(signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value)); }, + swjs_unsafe_event_loop_yield: () => { + throw new UnsafeEventLoopYield(); + }, }; this._instance = null; this._memory = null; @@ -384,6 +387,26 @@ WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); } } + main() { + const instance = this.instance; + try { + if (typeof instance.exports.main === "function") { + instance.exports.main(); + } + else if (typeof instance.exports.__main_argc_argv === "function") { + // Swift 6.0 and later use `__main_argc_argv` instead of `main`. + instance.exports.__main_argc_argv(0, 0); + } + } + catch (error) { + if (error instanceof UnsafeEventLoopYield) { + // Ignore the error + return; + } + // Rethrow other errors + throw error; + } + } get instance() { if (!this._instance) throw new Error("WebAssembly instance is not set yet"); @@ -430,6 +453,21 @@ return output; } } + /// This error is thrown when yielding event loop control from `swift_task_asyncMainDrainQueue` + /// to JavaScript. This is usually thrown when: + /// - The entry point of the Swift program is `func main() async` + /// - The Swift Concurrency's global executor is hooked by `JavaScriptEventLoop.installGlobalExecutor()` + /// - Calling exported `main` or `__main_argc_argv` function from JavaScript + /// + /// This exception must be caught by the caller of the exported function and the caller should + /// catch this exception and just ignore it. + /// + /// FAQ: Why this error is thrown? + /// This error is thrown to unwind the call stack of the Swift program and return the control to + /// the JavaScript side. Otherwise, the `swift_task_asyncMainDrainQueue` ends up with `abort()` + /// because the event loop expects `exit()` call before the end of the event loop. + class UnsafeEventLoopYield extends Error { + } exports.SwiftRuntime = SwiftRuntime; diff --git a/Sources/JavaScriptKit/Runtime/index.mjs b/Sources/JavaScriptKit/Runtime/index.mjs index 823ffca60..78c99457c 100644 --- a/Sources/JavaScriptKit/Runtime/index.mjs +++ b/Sources/JavaScriptKit/Runtime/index.mjs @@ -360,6 +360,9 @@ class SwiftRuntime { (BigInt.asUintN(32, BigInt(upper)) << BigInt(32)); return this.memory.retain(signed ? BigInt.asIntN(64, value) : BigInt.asUintN(64, value)); }, + swjs_unsafe_event_loop_yield: () => { + throw new UnsafeEventLoopYield(); + }, }; this._instance = null; this._memory = null; @@ -378,6 +381,26 @@ class SwiftRuntime { WebAssembly runtime ${this.exports.swjs_library_version()} != JS runtime ${this.version}`); } } + main() { + const instance = this.instance; + try { + if (typeof instance.exports.main === "function") { + instance.exports.main(); + } + else if (typeof instance.exports.__main_argc_argv === "function") { + // Swift 6.0 and later use `__main_argc_argv` instead of `main`. + instance.exports.__main_argc_argv(0, 0); + } + } + catch (error) { + if (error instanceof UnsafeEventLoopYield) { + // Ignore the error + return; + } + // Rethrow other errors + throw error; + } + } get instance() { if (!this._instance) throw new Error("WebAssembly instance is not set yet"); @@ -424,5 +447,20 @@ class SwiftRuntime { return output; } } +/// This error is thrown when yielding event loop control from `swift_task_asyncMainDrainQueue` +/// to JavaScript. This is usually thrown when: +/// - The entry point of the Swift program is `func main() async` +/// - The Swift Concurrency's global executor is hooked by `JavaScriptEventLoop.installGlobalExecutor()` +/// - Calling exported `main` or `__main_argc_argv` function from JavaScript +/// +/// This exception must be caught by the caller of the exported function and the caller should +/// catch this exception and just ignore it. +/// +/// FAQ: Why this error is thrown? +/// This error is thrown to unwind the call stack of the Swift program and return the control to +/// the JavaScript side. Otherwise, the `swift_task_asyncMainDrainQueue` ends up with `abort()` +/// because the event loop expects `exit()` call before the end of the event loop. +class UnsafeEventLoopYield extends Error { +} export { SwiftRuntime }; diff --git a/Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h b/Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h index b24d19d04..2880772d6 100644 --- a/Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h +++ b/Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h @@ -27,13 +27,13 @@ typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)( Job *_Nonnull job); SWIFT_EXPORT_FROM(swift_Concurrency) -void *_Nullable swift_task_enqueueGlobal_hook; +extern void *_Nullable swift_task_enqueueGlobal_hook; /// A hook to take over global enqueuing with delay. typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDelay_original)( unsigned long long delay, Job *_Nonnull job); SWIFT_EXPORT_FROM(swift_Concurrency) -void *_Nullable swift_task_enqueueGlobalWithDelay_hook; +extern void *_Nullable swift_task_enqueueGlobalWithDelay_hook; typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)( long long sec, @@ -42,12 +42,23 @@ typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobalWithDeadline_original)( long long tnsec, int clock, Job *_Nonnull job); SWIFT_EXPORT_FROM(swift_Concurrency) -void *_Nullable swift_task_enqueueGlobalWithDeadline_hook; +extern void *_Nullable swift_task_enqueueGlobalWithDeadline_hook; /// A hook to take over main executor enqueueing. typedef SWIFT_CC(swift) void (*swift_task_enqueueMainExecutor_original)( Job *_Nonnull job); SWIFT_EXPORT_FROM(swift_Concurrency) -void *_Nullable swift_task_enqueueMainExecutor_hook; +extern void *_Nullable swift_task_enqueueMainExecutor_hook; + +/// A hook to override the entrypoint to the main runloop used to drive the +/// concurrency runtime and drain the main queue. This function must not return. +/// Note: If the hook is wrapping the original function and the `compatOverride` +/// is passed in, the `original` function pointer must be passed into the +/// compatibility override function as the original function. +typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_original)(); +typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_override)( + swift_task_asyncMainDrainQueue_original _Nullable original); +SWIFT_EXPORT_FROM(swift_Concurrency) +extern void *_Nullable swift_task_asyncMainDrainQueue_hook; #endif diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index b60007ed0..a9d8738af 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -322,6 +322,9 @@ __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_release"))) extern void _release(const JavaScriptObjectRef ref); +__attribute__((__import_module__("javascript_kit"), + __import_name__("swjs_unsafe_event_loop_yield"))) +extern void _unsafe_event_loop_yield(void); #endif From 69cd8c94552c3ce0f043beefde3dbc99b5c9c4d2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 8 May 2024 12:26:54 +0000 Subject: [PATCH 2/5] Remove Wasmer WASI tests We no longer use it in carton --- .github/workflows/test.yml | 3 - IntegrationTests/lib.js | 37 ---- IntegrationTests/package-lock.json | 328 +---------------------------- IntegrationTests/package.json | 2 - 4 files changed, 1 insertion(+), 369 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 121d22236..be06d493f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,6 @@ jobs: # Ensure that test succeeds with all toolchains and wasi backend combinations - { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: Node } - { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: Node } - - { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: Wasmer } - - { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: Wasmer } - - { os: ubuntu-20.04, toolchain: wasm-5.9.1-RELEASE, wasi-backend: Wasmer } - { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: MicroWASI } - { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: MicroWASI } - { os: ubuntu-20.04, toolchain: wasm-5.9.1-RELEASE, wasi-backend: MicroWASI } diff --git a/IntegrationTests/lib.js b/IntegrationTests/lib.js index 1d7b6342d..a68f2bdec 100644 --- a/IntegrationTests/lib.js +++ b/IntegrationTests/lib.js @@ -1,6 +1,4 @@ const SwiftRuntime = require("javascript-kit-swift").SwiftRuntime; -const WasmerWASI = require("@wasmer/wasi").WASI; -const WasmFs = require("@wasmer/wasmfs").WasmFs; const NodeWASI = require("wasi").WASI; const { WASI: MicroWASI, useAll } = require("uwasi"); @@ -9,41 +7,6 @@ const fs = require("fs"); const readFile = promisify(fs.readFile); const WASI = { - Wasmer: ({ programName }) => { - // Instantiate a new WASI Instance - const wasmFs = new WasmFs(); - // Output stdout and stderr to console - const originalWriteSync = wasmFs.fs.writeSync; - wasmFs.fs.writeSync = (fd, buffer, offset, length, position) => { - const text = new TextDecoder("utf-8").decode(buffer); - switch (fd) { - case 1: - console.log(text); - break; - case 2: - console.error(text); - break; - } - return originalWriteSync(fd, buffer, offset, length, position); - }; - const wasi = new WasmerWASI({ - args: [programName], - env: {}, - bindings: { - ...WasmerWASI.defaultBindings, - fs: wasmFs.fs, - }, - }); - - return { - wasiImport: wasi.wasiImport, - start(instance) { - wasi.start(instance); - instance.exports._initialize(); - instance.exports.main(); - } - } - }, MicroWASI: ({ programName }) => { const wasi = new MicroWASI({ args: [programName], diff --git a/IntegrationTests/package-lock.json b/IntegrationTests/package-lock.json index 8aff33b98..d0b914f04 100644 --- a/IntegrationTests/package-lock.json +++ b/IntegrationTests/package-lock.json @@ -5,15 +5,13 @@ "packages": { "": { "dependencies": { - "@wasmer/wasi": "^0.12.0", - "@wasmer/wasmfs": "^0.12.0", "javascript-kit-swift": "file:..", "uwasi": "^1.2.0" } }, "..": { "name": "javascript-kit-swift", - "version": "0.19.1", + "version": "0.19.2", "license": "MIT", "devDependencies": { "@rollup/plugin-typescript": "^8.3.1", @@ -46,264 +44,17 @@ "node": ">=4.2.0" } }, - "node_modules/@wasmer/wasi": { - "version": "0.12.0", - "integrity": "sha512-FJhLZKAfLWm/yjQI7eCRHNbA8ezmb7LSpUYFkHruZXs2mXk2+DaQtSElEtOoNrVQ4vApTyVaAd5/b7uEu8w6wQ==", - "dependencies": { - "browser-process-hrtime": "^1.0.0", - "buffer-es6": "^4.9.3", - "path-browserify": "^1.0.0", - "randomfill": "^1.0.4" - } - }, - "node_modules/@wasmer/wasmfs": { - "version": "0.12.0", - "integrity": "sha512-m1ftchyQ1DfSenm5XbbdGIpb6KJHH5z0gODo3IZr6lATkj4WXfX/UeBTZ0aG9YVShBp+kHLdUHvOkqjy6p/GWw==", - "dependencies": { - "memfs": "3.0.4", - "pako": "^1.0.11", - "tar-stream": "^2.1.0" - } - }, - "node_modules/base64-js": { - "version": "1.3.1", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "node_modules/bl": { - "version": "4.0.3", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "node_modules/buffer": { - "version": "5.6.0", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/buffer-es6": { - "version": "4.9.3", - "integrity": "sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ=" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/fast-extend": { - "version": "1.0.2", - "integrity": "sha512-XXA9RmlPatkFKUzqVZAFth18R4Wo+Xug/S+C7YlYA3xrXwfPlW3dqNwOb4hvQo7wZJ2cNDYhrYuPzVOfHy5/uQ==" - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "node_modules/fs-monkey": { - "version": "0.3.3", - "integrity": "sha512-FNUvuTAJ3CqCQb5ELn+qCbGR/Zllhf2HtwsdAtBi59s1WeCjKMT81fHcSu7dwIskqGVK+MmOrb7VOBlq3/SItw==" - }, - "node_modules/ieee754": { - "version": "1.1.13", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/inherits": { - "version": "2.0.4", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/javascript-kit-swift": { "resolved": "..", "link": true }, - "node_modules/memfs": { - "version": "3.0.4", - "integrity": "sha512-OcZEzwX9E5AoY8SXjuAvw0DbIAYwUzV/I236I8Pqvrlv7sL/Y0E9aRCon05DhaV8pg1b32uxj76RgW0s5xjHBA==", - "dependencies": { - "fast-extend": "1.0.2", - "fs-monkey": "0.3.3" - } - }, - "node_modules/once": { - "version": "1.4.0", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/tar-stream": { - "version": "2.1.4", - "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "node_modules/uwasi": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/uwasi/-/uwasi-1.2.0.tgz", "integrity": "sha512-+U3ajjQgx/Xh1/ZNrgH0EzM5qI2czr94oz3DPDwTvUIlM4SFpDjTqJzDA3xcqlTmpp2YGpxApmjwZfablMUoOg==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } }, "dependencies": { - "@wasmer/wasi": { - "version": "0.12.0", - "integrity": "sha512-FJhLZKAfLWm/yjQI7eCRHNbA8ezmb7LSpUYFkHruZXs2mXk2+DaQtSElEtOoNrVQ4vApTyVaAd5/b7uEu8w6wQ==", - "requires": { - "browser-process-hrtime": "^1.0.0", - "buffer-es6": "^4.9.3", - "path-browserify": "^1.0.0", - "randomfill": "^1.0.4" - } - }, - "@wasmer/wasmfs": { - "version": "0.12.0", - "integrity": "sha512-m1ftchyQ1DfSenm5XbbdGIpb6KJHH5z0gODo3IZr6lATkj4WXfX/UeBTZ0aG9YVShBp+kHLdUHvOkqjy6p/GWw==", - "requires": { - "memfs": "3.0.4", - "pako": "^1.0.11", - "tar-stream": "^2.1.0" - } - }, - "base64-js": { - "version": "1.3.1", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "bl": { - "version": "4.0.3", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "buffer": { - "version": "5.6.0", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-es6": { - "version": "4.9.3", - "integrity": "sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ=" - }, - "end-of-stream": { - "version": "1.4.4", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "fast-extend": { - "version": "1.0.2", - "integrity": "sha512-XXA9RmlPatkFKUzqVZAFth18R4Wo+Xug/S+C7YlYA3xrXwfPlW3dqNwOb4hvQo7wZJ2cNDYhrYuPzVOfHy5/uQ==" - }, - "fs-constants": { - "version": "1.0.0", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "fs-monkey": { - "version": "0.3.3", - "integrity": "sha512-FNUvuTAJ3CqCQb5ELn+qCbGR/Zllhf2HtwsdAtBi59s1WeCjKMT81fHcSu7dwIskqGVK+MmOrb7VOBlq3/SItw==" - }, - "ieee754": { - "version": "1.1.13", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "inherits": { - "version": "2.0.4", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "javascript-kit-swift": { "version": "file:..", "requires": { @@ -326,87 +77,10 @@ } } }, - "memfs": { - "version": "3.0.4", - "integrity": "sha512-OcZEzwX9E5AoY8SXjuAvw0DbIAYwUzV/I236I8Pqvrlv7sL/Y0E9aRCon05DhaV8pg1b32uxj76RgW0s5xjHBA==", - "requires": { - "fast-extend": "1.0.2", - "fs-monkey": "0.3.3" - } - }, - "once": { - "version": "1.4.0", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "pako": { - "version": "1.0.11", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "path-browserify": { - "version": "1.0.1", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" - }, - "randombytes": { - "version": "2.1.0", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "string_decoder": { - "version": "1.3.0", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "tar-stream": { - "version": "2.1.4", - "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "uwasi": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/uwasi/-/uwasi-1.2.0.tgz", "integrity": "sha512-+U3ajjQgx/Xh1/ZNrgH0EzM5qI2czr94oz3DPDwTvUIlM4SFpDjTqJzDA3xcqlTmpp2YGpxApmjwZfablMUoOg==" - }, - "wrappy": { - "version": "1.0.2", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } } diff --git a/IntegrationTests/package.json b/IntegrationTests/package.json index a7d756165..29a3597c7 100644 --- a/IntegrationTests/package.json +++ b/IntegrationTests/package.json @@ -1,8 +1,6 @@ { "private": true, "dependencies": { - "@wasmer/wasi": "^0.12.0", - "@wasmer/wasmfs": "^0.12.0", "uwasi": "^1.2.0", "javascript-kit-swift": "file:.." } From cbd2074120566276f68f9a7cb2c40307a26873c0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 8 May 2024 12:39:11 +0000 Subject: [PATCH 3/5] Support the latest nightly snapshot --- .github/workflows/test.yml | 1 + IntegrationTests/Makefile | 3 ++- IntegrationTests/lib.js | 13 ++++++++----- Makefile | 2 +- scripts/test-harness.js | 7 ++++++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be06d493f..1767b1385 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: - { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: MicroWASI } - { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: MicroWASI } - { os: ubuntu-20.04, toolchain: wasm-5.9.1-RELEASE, wasi-backend: MicroWASI } + - { os: ubuntu-22.04, toolchain: wasm-DEVELOPMENT-SNAPSHOT-2024-05-02-a, wasi-backend: Node } runs-on: ${{ matrix.entry.os }} env: diff --git a/IntegrationTests/Makefile b/IntegrationTests/Makefile index 57b99c8da..3329225f1 100644 --- a/IntegrationTests/Makefile +++ b/IntegrationTests/Makefile @@ -11,7 +11,8 @@ TestSuites/.build/$(CONFIGURATION)/%.wasm: FORCE --triple wasm32-unknown-wasi \ --configuration $(CONFIGURATION) \ -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor \ - -Xlinker --export=main \ + -Xlinker --export-if-defined=main -Xlinker --export-if-defined=__main_argc_argv \ + --static-swift-stdlib -Xswiftc -static-stdlib \ $(SWIFT_BUILD_FLAGS) dist/%.wasm: TestSuites/.build/$(CONFIGURATION)/%.wasm diff --git a/IntegrationTests/lib.js b/IntegrationTests/lib.js index a68f2bdec..71570bfee 100644 --- a/IntegrationTests/lib.js +++ b/IntegrationTests/lib.js @@ -16,9 +16,9 @@ const WASI = { return { wasiImport: wasi.wasiImport, - start(instance) { + start(instance, swift) { wasi.initialize(instance); - instance.exports.main(); + swift.main(); } } }, @@ -26,15 +26,18 @@ const WASI = { const wasi = new NodeWASI({ args: [programName], env: {}, + preopens: { + "/": "./", + }, returnOnExit: false, version: "preview1", }) return { wasiImport: wasi.wasiImport, - start(instance) { + start(instance, swift) { wasi.initialize(instance); - instance.exports.main(); + swift.main(); } } }, @@ -69,7 +72,7 @@ const startWasiTask = async (wasmPath, wasiConstructor = selectWASIBackend()) => swift.setInstance(instance); // Start the WebAssembly WASI instance! - wasi.start(instance); + wasi.start(instance, swift); }; module.exports = { startWasiTask, WASI }; diff --git a/Makefile b/Makefile index ccf22798d..35530b261 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ test: .PHONY: unittest unittest: @echo Running unit tests - swift build --build-tests --triple wasm32-unknown-wasi -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor -Xlinker --export=main + swift build --build-tests --triple wasm32-unknown-wasi -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor -Xlinker --export-if-defined=main -Xlinker --export-if-defined=__main_argc_argv --static-swift-stdlib -Xswiftc -static-stdlib node --experimental-wasi-unstable-preview1 scripts/test-harness.js ./.build/wasm32-unknown-wasi/debug/JavaScriptKitPackageTests.wasm .PHONY: benchmark_setup diff --git a/scripts/test-harness.js b/scripts/test-harness.js index 39a7dbe9a..27f9e93a4 100644 --- a/scripts/test-harness.js +++ b/scripts/test-harness.js @@ -1,6 +1,11 @@ Error.stackTraceLimit = Infinity; -const { startWasiTask, WASI } = require("../IntegrationTests/lib"); +const { startWasiTask } = require("../IntegrationTests/lib"); + +if (process.env["JAVASCRIPTKIT_WASI_BACKEND"] === "MicroWASI") { + console.log("Skipping XCTest tests for MicroWASI because it is not supported yet."); + process.exit(0); +} const handleExitOrError = (error) => { console.log(error); From 36408f5ae2068efdbda073819c533f8002374804 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 8 May 2024 13:01:03 +0000 Subject: [PATCH 4/5] Migrate to ESM --- .../TestSuites/Sources/PrimaryTests/main.swift | 2 +- IntegrationTests/bin/benchmark-tests.js | 4 ++-- IntegrationTests/bin/concurrency-tests.js | 2 +- IntegrationTests/bin/primary-tests.js | 2 +- IntegrationTests/lib.js | 17 ++++++----------- IntegrationTests/package.json | 1 + Makefile | 2 +- scripts/{test-harness.js => test-harness.mjs} | 2 +- 8 files changed, 14 insertions(+), 18 deletions(-) rename scripts/{test-harness.js => test-harness.mjs} (84%) diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift index a9d127109..716151034 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift @@ -400,7 +400,7 @@ try test("Call Function With This") { let setName = try expectFunction(getJSValue(this: cat1, name: "setName")) // Direct call without this - try expectEqual(getIsCat(), .undefined) + _ = try expectThrow(try getIsCat.throws()) // Call with this let gotIsCat = getIsCat(this: cat1) diff --git a/IntegrationTests/bin/benchmark-tests.js b/IntegrationTests/bin/benchmark-tests.js index 424ce8199..0d8b5410a 100644 --- a/IntegrationTests/bin/benchmark-tests.js +++ b/IntegrationTests/bin/benchmark-tests.js @@ -1,5 +1,5 @@ -const { startWasiTask } = require("../lib"); -const { performance } = require("perf_hooks"); +import { startWasiTask } from "../lib.js"; +import { performance } from "perf_hooks"; const SAMPLE_ITERATION = 1000000 diff --git a/IntegrationTests/bin/concurrency-tests.js b/IntegrationTests/bin/concurrency-tests.js index 47ef4abda..02489c959 100644 --- a/IntegrationTests/bin/concurrency-tests.js +++ b/IntegrationTests/bin/concurrency-tests.js @@ -1,4 +1,4 @@ -const { startWasiTask } = require("../lib"); +import { startWasiTask } from "../lib.js"; Error.stackTraceLimit = Infinity; diff --git a/IntegrationTests/bin/primary-tests.js b/IntegrationTests/bin/primary-tests.js index 50532ceac..36ac65812 100644 --- a/IntegrationTests/bin/primary-tests.js +++ b/IntegrationTests/bin/primary-tests.js @@ -102,7 +102,7 @@ global.objectDecodingTest = { bi: BigInt(3) }; -const { startWasiTask, WASI } = require("../lib"); +import { startWasiTask } from "../lib.js"; startWasiTask("./dist/PrimaryTests.wasm").catch((err) => { console.log(err); diff --git a/IntegrationTests/lib.js b/IntegrationTests/lib.js index 71570bfee..2ed9a918d 100644 --- a/IntegrationTests/lib.js +++ b/IntegrationTests/lib.js @@ -1,10 +1,7 @@ -const SwiftRuntime = require("javascript-kit-swift").SwiftRuntime; -const NodeWASI = require("wasi").WASI; -const { WASI: MicroWASI, useAll } = require("uwasi"); - -const promisify = require("util").promisify; -const fs = require("fs"); -const readFile = promisify(fs.readFile); +import { SwiftRuntime } from "javascript-kit-swift" +import { WASI as NodeWASI } from "wasi" +import { WASI as MicroWASI, useAll } from "uwasi" +import * as fs from "fs/promises" const WASI = { MicroWASI: ({ programName }) => { @@ -54,10 +51,10 @@ const selectWASIBackend = () => { return WASI.Node; }; -const startWasiTask = async (wasmPath, wasiConstructor = selectWASIBackend()) => { +export const startWasiTask = async (wasmPath, wasiConstructor = selectWASIBackend()) => { const swift = new SwiftRuntime(); // Fetch our Wasm File - const wasmBinary = await readFile(wasmPath); + const wasmBinary = await fs.readFile(wasmPath); const wasi = wasiConstructor({ programName: wasmPath }); // Instantiate the WebAssembly file @@ -74,5 +71,3 @@ const startWasiTask = async (wasmPath, wasiConstructor = selectWASIBackend()) => // Start the WebAssembly WASI instance! wasi.start(instance, swift); }; - -module.exports = { startWasiTask, WASI }; diff --git a/IntegrationTests/package.json b/IntegrationTests/package.json index 29a3597c7..8491e91fb 100644 --- a/IntegrationTests/package.json +++ b/IntegrationTests/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "dependencies": { "uwasi": "^1.2.0", "javascript-kit-swift": "file:.." diff --git a/Makefile b/Makefile index 35530b261..e71734a5f 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ test: unittest: @echo Running unit tests swift build --build-tests --triple wasm32-unknown-wasi -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor -Xlinker --export-if-defined=main -Xlinker --export-if-defined=__main_argc_argv --static-swift-stdlib -Xswiftc -static-stdlib - node --experimental-wasi-unstable-preview1 scripts/test-harness.js ./.build/wasm32-unknown-wasi/debug/JavaScriptKitPackageTests.wasm + node --experimental-wasi-unstable-preview1 scripts/test-harness.mjs ./.build/wasm32-unknown-wasi/debug/JavaScriptKitPackageTests.wasm .PHONY: benchmark_setup benchmark_setup: diff --git a/scripts/test-harness.js b/scripts/test-harness.mjs similarity index 84% rename from scripts/test-harness.js rename to scripts/test-harness.mjs index 27f9e93a4..b0384d4de 100644 --- a/scripts/test-harness.js +++ b/scripts/test-harness.mjs @@ -1,6 +1,6 @@ Error.stackTraceLimit = Infinity; -const { startWasiTask } = require("../IntegrationTests/lib"); +import { startWasiTask } from "../IntegrationTests/lib.js"; if (process.env["JAVASCRIPTKIT_WASI_BACKEND"] === "MicroWASI") { console.log("Skipping XCTest tests for MicroWASI because it is not supported yet."); From d8e3a89119b5a87faf01208e5a308f29a9f42ff3 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 8 May 2024 13:01:41 +0000 Subject: [PATCH 5/5] Fix host build --- Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift | 5 +++++ Sources/JavaScriptKit/XcodeSupport.swift | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift index 22935bd05..04aedb940 100644 --- a/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift +++ b/Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift @@ -225,3 +225,8 @@ public extension JSPromise { } #endif + +// See `Sources/JavaScriptKit/XcodeSupport.swift` for rationale of the stub functions. +#if !arch(wasm32) + func _unsafe_event_loop_yield() { fatalError() } +#endif diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 9689cf3b0..ac5f117b4 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -1,10 +1,10 @@ import _CJavaScriptKit /// Note: -/// Define all runtime function stubs which are imported from JavaScript environment. -/// SwiftPM doesn't support WebAssembly target yet, so we need to define them to -/// avoid link failure. -/// When running with JavaScript runtime library, they are ignored completely. +/// Define stubs for runtime functions which are usually imported from JavaScript environment. +/// JavaScriptKit itself supports only WebAssembly target, but it should be able +/// to be built for host platforms like macOS or Linux for tentative IDE support. +/// (ideally, IDE should build for WebAssembly target though) #if !arch(wasm32) func _set_prop( _: JavaScriptObjectRef,