Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Plugins/PackageToJS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ to generate browser and Node platform wrappers backed by `uwasi` instead:
swift package --swift-sdk wasm32-unknown-wasi js --wasi-runtime uwasi
```

The `uwasi` wrappers import only the stdio, args, environ, clock, poll, proc and
random features, so bundlers can drop `uwasi`'s file system and file syscalls return
`ENOSYS`. With `--enable-code-coverage`, the Node wrapper adds an in-memory file
system so the test runner can read the coverage profile. To give an application a
file system, pass your own `wasi` object to `instantiate`.

The generated browser main-thread setup rejects blocking WASI waits. Run guests that
require `poll_oneoff` clock waits in a worker. Node and browser workers use `uwasi`'s
blocking implementation.
Expand Down
1 change: 1 addition & 0 deletions Plugins/PackageToJS/Sources/PackageToJS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,7 @@ struct PackagingPlanner {
let staticConditions: [String: Bool] = [
"USE_WASI_CDN": options.useCDN,
"USE_UWASI": options.wasiRuntime == .uwasi,
"USE_UWASI_FILESYSTEM": options.wasiRuntime == .uwasi && options.enableCodeCoverage,
"HAS_BRIDGE": skeletons.count > 0,
"HAS_IMPORTS": skeletons.count > 0,
"TARGET_DEFAULT_PLATFORM_NODE": options.defaultPlatform == .node,
Expand Down
27 changes: 11 additions & 16 deletions Plugins/PackageToJS/Templates/platforms/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { MODULE_PATH /* #if USE_SHARED_MEMORY */, MEMORY_TYPE /* #endif */} from
/* #if USE_UWASI */
/* #if USE_WASI_CDN */
// @ts-ignore
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'https://cdn.jsdelivr.net/npm/uwasi@1.5.2/+esm';
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'https://cdn.jsdelivr.net/npm/uwasi@1.5.2/+esm';
/* #else */
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'uwasi';
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'uwasi';
/* #endif */
import { browserMainThreadSleep, createUwasi } from './uwasi.js';
const uwasiRuntime = { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom };
/* #else */
/* #if USE_WASI_CDN */
// @ts-ignore
Expand Down Expand Up @@ -38,10 +39,7 @@ export async function defaultBrowserThreadSetup() {

/* #if IS_WASI */
/* #if USE_UWASI */
const { wasi } = createUwasi(
{ WASI, MemoryFileSystem, useAll, lineBuffered },
{ modulePath: MODULE_PATH }
)
const { wasi } = createUwasi(uwasiRuntime, { modulePath: MODULE_PATH })
/* #else */
const wasi = new WASI(/* args */[MODULE_PATH], /* env */[], /* fd */[
new OpenFile(new File([])), // stdin
Expand Down Expand Up @@ -127,16 +125,13 @@ export async function defaultBrowserSetup(options) {
const onStdoutLine = options.onStdoutLine ?? ((line) => console.log(line))
const onStderrLine = options.onStderrLine ?? ((line) => console.error(line))
/* #if USE_UWASI */
const { wasi } = createUwasi(
{ WASI, MemoryFileSystem, useAll, lineBuffered },
{
modulePath: MODULE_PATH,
args,
onStdoutLine,
onStderrLine,
sleep: browserMainThreadSleep(),
}
)
const { wasi } = createUwasi(uwasiRuntime, {
modulePath: MODULE_PATH,
args,
onStdoutLine,
onStderrLine,
sleep: browserMainThreadSleep(),
})
/* #else */
const wasi = new WASI(/* args */[MODULE_PATH, ...args], /* env */[], /* fd */[
new OpenFile(new File([])), // stdin
Expand Down
29 changes: 16 additions & 13 deletions Plugins/PackageToJS/Templates/platforms/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ import { Worker, parentPort } from "node:worker_threads";
import { MODULE_PATH /* #if USE_SHARED_MEMORY */, MEMORY_TYPE /* #endif */} from "../instantiate.js"
/* #if IS_WASI */
/* #if USE_UWASI */
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'uwasi';
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'uwasi';
/* #if USE_UWASI_FILESYSTEM */
import { MemoryFileSystem, useMemoryFS } from 'uwasi';
/* #endif */
import { createUwasi } from './uwasi.js';
const uwasiRuntime = {
WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom,
/* #if USE_UWASI_FILESYSTEM */
MemoryFileSystem, useMemoryFS,
/* #endif */
};
/* #else */
import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory, Directory, Inode } from '@bjorn3/browser_wasi_shim';
/* #endif */
Expand All @@ -28,10 +37,7 @@ export async function defaultNodeThreadSetup() {
}

/* #if USE_UWASI */
const { wasi } = createUwasi(
{ WASI, MemoryFileSystem, useAll, lineBuffered },
{ modulePath: MODULE_PATH }
)
const { wasi } = createUwasi(uwasiRuntime, { modulePath: MODULE_PATH })
/* #else */
const wasi = new WASI(/* args */[MODULE_PATH], /* env */[], /* fd */[
new OpenFile(new File([])), // stdin
Expand Down Expand Up @@ -136,14 +142,11 @@ export async function defaultNodeSetup(options = {}) {

const args = options.args ?? process.argv.slice(2)
/* #if USE_UWASI */
const { wasi } = createUwasi(
{ WASI, MemoryFileSystem, useAll, lineBuffered },
{
modulePath: MODULE_PATH,
args,
withExtractFile: true,
}
)
const { wasi } = createUwasi(uwasiRuntime, {
modulePath: MODULE_PATH,
args,
withExtractFile: true,
})
/* #else */
const rootFs = new Map();
const wasi = new WASI(/* args */[MODULE_PATH, ...args], /* env */[], /* fd */[
Expand Down
50 changes: 35 additions & 15 deletions Plugins/PackageToJS/Templates/platforms/uwasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,19 @@ export function browserMainThreadSleep() {
}

/**
* @param {{ WASI: any, MemoryFileSystem: any, useAll: any, lineBuffered: any }} runtime
* @param {{
* WASI: any,
* lineBuffered: any,
* useStdio: any,
* useArgs: any,
* useEnviron: any,
* useClock: any,
* usePoll: any,
* useProc: any,
* useRandom: any,
* MemoryFileSystem?: any,
* useMemoryFS?: any,
* }} runtime
* @param {{
* modulePath: string,
* args?: string[],
Expand All @@ -32,27 +44,35 @@ export function createUwasi(runtime, options) {
const onStdoutLine = options.onStdoutLine ?? ((line) => console.log(line));
const onStderrLine =
options.onStderrLine ?? ((line) => console.error(line));
const fileSystem = new runtime.MemoryFileSystem({ "/": "/" });
const stdout = runtime.lineBuffered(onStdoutLine);
const stderr = runtime.lineBuffered(onStderrLine);
const stdio = {
stdin: () => "",
stdout: runtime.lineBuffered(onStdoutLine),
stderr: runtime.lineBuffered(onStderrLine),
outputBuffers: true,
};
const fileSystem = runtime.MemoryFileSystem
? new runtime.MemoryFileSystem({ "/": "/" })
: undefined;
const wasi = new runtime.WASI({
args: [options.modulePath, ...args],
env: {},
features: [
runtime.useAll({
withFileSystem: fileSystem,
withStdio: {
stdin: () => "",
stdout,
stderr,
outputBuffers: true,
},
sleep: options.sleep,
}),
fileSystem
? runtime.useMemoryFS({
withFileSystem: fileSystem,
withStdio: stdio,
})
: runtime.useStdio(stdio),
runtime.useArgs(),
runtime.useEnviron(),
runtime.useClock(),
runtime.usePoll({ sleep: options.sleep }),
runtime.useProc(),
runtime.useRandom(),
],
});

if (options.withExtractFile) {
if (fileSystem && options.withExtractFile) {
Object.assign(wasi, {
extractFile(filePath) {
const node = fileSystem.lookup(filePath);
Expand Down
Loading
Loading