Skip to content

Commit e89d7ae

Browse files
committed
PackageToJS: Import only the UWASI features Swift needs by default
The UWASI platform wrappers imported `useAll` and `MemoryFileSystem`, so bundles always carried UWASI's in-memory file system and came out larger than the browser_wasi_shim default. Import `useStdio`, `useArgs`, `useEnviron`, `useClock`, `usePoll`, `useProc` and `useRandom` instead. File syscalls now return ENOSYS. Test builds keep the file system, because code coverage reads the profile through `extractFile`.
1 parent 0d41cbf commit e89d7ae

18 files changed

Lines changed: 641 additions & 195 deletions

‎Plugins/PackageToJS/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ to generate browser and Node platform wrappers backed by `uwasi` instead:
2222
swift package --swift-sdk wasm32-unknown-wasi js --wasi-runtime uwasi
2323
```
2424

25+
The `uwasi` wrappers import only the stdio, args, environ, clock, poll, proc and
26+
random features, so bundlers can drop `uwasi`'s file system and file syscalls return
27+
`ENOSYS`. Test builds keep an in-memory file system for code coverage. To give an
28+
application a file system, pass your own `wasi` object to `instantiate`.
29+
2530
The generated browser main-thread setup rejects blocking WASI waits. Run guests that
2631
require `poll_oneoff` clock waits in a worker. Node and browser workers use `uwasi`'s
2732
blocking implementation.

‎Plugins/PackageToJS/Sources/PackageToJS.swift‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ struct PackageToJS {
2525
var defaultPlatform: Platform = .browser
2626
/// WASI runtime used by generated JavaScript (default: browser-wasi-shim)
2727
var wasiRuntime: WASIRuntime = .browserWASIShim
28+
/// Whether the uwasi runtime provides an in-memory file system (default: false; true for tests)
29+
var wasiFilesystem: Bool = false
2830
/// Whether to explain the build plan (default: false)
2931
var explain: Bool = false
3032
/// Whether to print verbose output
@@ -957,6 +959,7 @@ struct PackagingPlanner {
957959
let staticConditions: [String: Bool] = [
958960
"USE_WASI_CDN": options.useCDN,
959961
"USE_UWASI": options.wasiRuntime == .uwasi,
962+
"USE_UWASI_FILESYSTEM": options.wasiRuntime == .uwasi && options.wasiFilesystem,
960963
"HAS_BRIDGE": skeletons.count > 0,
961964
"HAS_IMPORTS": skeletons.count > 0,
962965
"TARGET_DEFAULT_PLATFORM_NODE": options.defaultPlatform == .node,

‎Plugins/PackageToJS/Sources/PackageToJSPlugin.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ extension PackageToJS.TestOptions {
731731
if !options.buildOnly, !options.packageOptions.useCDN {
732732
options.packageOptions.useCDN = true
733733
}
734+
options.packageOptions.wasiFilesystem = true
734735

735736
return options
736737
}

‎Plugins/PackageToJS/Templates/platforms/browser.js‎

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
import { MODULE_PATH /* #if USE_SHARED_MEMORY */, MEMORY_TYPE /* #endif */} from "../instantiate.js"
33
/* #if IS_WASI */
44
/* #if USE_UWASI */
5+
import { browserMainThreadSleep, createUwasi } from './uwasi.js';
6+
/* #if USE_UWASI_FILESYSTEM */
57
/* #if USE_WASI_CDN */
68
// @ts-ignore
7-
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'https://cdn.jsdelivr.net/npm/uwasi@1.5.2/+esm';
9+
import { WASI, lineBuffered, MemoryFileSystem, useAll } from 'https://cdn.jsdelivr.net/npm/uwasi@1.5.2/+esm';
810
/* #else */
9-
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'uwasi';
11+
import { WASI, lineBuffered, MemoryFileSystem, useAll } from 'uwasi';
12+
/* #endif */
13+
const uwasiRuntime = { WASI, lineBuffered, MemoryFileSystem, useAll };
14+
/* #else */
15+
/* #if USE_WASI_CDN */
16+
// @ts-ignore
17+
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'https://cdn.jsdelivr.net/npm/uwasi@1.5.2/+esm';
18+
/* #else */
19+
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'uwasi';
20+
/* #endif */
21+
const uwasiRuntime = { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom };
1022
/* #endif */
11-
import { browserMainThreadSleep, createUwasi } from './uwasi.js';
1223
/* #else */
1324
/* #if USE_WASI_CDN */
1425
// @ts-ignore
@@ -38,10 +49,7 @@ export async function defaultBrowserThreadSetup() {
3849

3950
/* #if IS_WASI */
4051
/* #if USE_UWASI */
41-
const { wasi } = createUwasi(
42-
{ WASI, MemoryFileSystem, useAll, lineBuffered },
43-
{ modulePath: MODULE_PATH }
44-
)
52+
const { wasi } = createUwasi(uwasiRuntime, { modulePath: MODULE_PATH })
4553
/* #else */
4654
const wasi = new WASI(/* args */[MODULE_PATH], /* env */[], /* fd */[
4755
new OpenFile(new File([])), // stdin
@@ -127,16 +135,13 @@ export async function defaultBrowserSetup(options) {
127135
const onStdoutLine = options.onStdoutLine ?? ((line) => console.log(line))
128136
const onStderrLine = options.onStderrLine ?? ((line) => console.error(line))
129137
/* #if USE_UWASI */
130-
const { wasi } = createUwasi(
131-
{ WASI, MemoryFileSystem, useAll, lineBuffered },
132-
{
133-
modulePath: MODULE_PATH,
134-
args,
135-
onStdoutLine,
136-
onStderrLine,
137-
sleep: browserMainThreadSleep(),
138-
}
139-
)
138+
const { wasi } = createUwasi(uwasiRuntime, {
139+
modulePath: MODULE_PATH,
140+
args,
141+
onStdoutLine,
142+
onStderrLine,
143+
sleep: browserMainThreadSleep(),
144+
})
140145
/* #else */
141146
const wasi = new WASI(/* args */[MODULE_PATH, ...args], /* env */[], /* fd */[
142147
new OpenFile(new File([])), // stdin

‎Plugins/PackageToJS/Templates/platforms/node.js‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ import { Worker, parentPort } from "node:worker_threads";
44
import { MODULE_PATH /* #if USE_SHARED_MEMORY */, MEMORY_TYPE /* #endif */} from "../instantiate.js"
55
/* #if IS_WASI */
66
/* #if USE_UWASI */
7-
import { WASI, MemoryFileSystem, useAll, lineBuffered } from 'uwasi';
87
import { createUwasi } from './uwasi.js';
8+
/* #if USE_UWASI_FILESYSTEM */
9+
import { WASI, lineBuffered, MemoryFileSystem, useAll } from 'uwasi';
10+
const uwasiRuntime = { WASI, lineBuffered, MemoryFileSystem, useAll };
11+
/* #else */
12+
import { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom } from 'uwasi';
13+
const uwasiRuntime = { WASI, lineBuffered, useStdio, useArgs, useEnviron, useClock, usePoll, useProc, useRandom };
14+
/* #endif */
915
/* #else */
1016
import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory, Directory, Inode } from '@bjorn3/browser_wasi_shim';
1117
/* #endif */
@@ -28,10 +34,7 @@ export async function defaultNodeThreadSetup() {
2834
}
2935

3036
/* #if USE_UWASI */
31-
const { wasi } = createUwasi(
32-
{ WASI, MemoryFileSystem, useAll, lineBuffered },
33-
{ modulePath: MODULE_PATH }
34-
)
37+
const { wasi } = createUwasi(uwasiRuntime, { modulePath: MODULE_PATH })
3538
/* #else */
3639
const wasi = new WASI(/* args */[MODULE_PATH], /* env */[], /* fd */[
3740
new OpenFile(new File([])), // stdin
@@ -136,14 +139,11 @@ export async function defaultNodeSetup(options = {}) {
136139

137140
const args = options.args ?? process.argv.slice(2)
138141
/* #if USE_UWASI */
139-
const { wasi } = createUwasi(
140-
{ WASI, MemoryFileSystem, useAll, lineBuffered },
141-
{
142-
modulePath: MODULE_PATH,
143-
args,
144-
withExtractFile: true,
145-
}
146-
)
142+
const { wasi } = createUwasi(uwasiRuntime, {
143+
modulePath: MODULE_PATH,
144+
args,
145+
withExtractFile: true,
146+
})
147147
/* #else */
148148
const rootFs = new Map();
149149
const wasi = new WASI(/* args */[MODULE_PATH, ...args], /* env */[], /* fd */[

‎Plugins/PackageToJS/Templates/platforms/uwasi.js‎

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,19 @@ export function browserMainThreadSleep() {
1717
}
1818

1919
/**
20-
* @param {{ WASI: any, MemoryFileSystem: any, useAll: any, lineBuffered: any }} runtime
20+
* @param {{
21+
* WASI: any,
22+
* lineBuffered: any,
23+
* MemoryFileSystem?: any,
24+
* useAll?: any,
25+
* useStdio?: any,
26+
* useArgs?: any,
27+
* useEnviron?: any,
28+
* useClock?: any,
29+
* usePoll?: any,
30+
* useProc?: any,
31+
* useRandom?: any,
32+
* }} runtime
2133
* @param {{
2234
* modulePath: string,
2335
* args?: string[],
@@ -32,27 +44,39 @@ export function createUwasi(runtime, options) {
3244
const onStdoutLine = options.onStdoutLine ?? ((line) => console.log(line));
3345
const onStderrLine =
3446
options.onStderrLine ?? ((line) => console.error(line));
35-
const fileSystem = new runtime.MemoryFileSystem({ "/": "/" });
36-
const stdout = runtime.lineBuffered(onStdoutLine);
37-
const stderr = runtime.lineBuffered(onStderrLine);
47+
const stdio = {
48+
stdin: () => "",
49+
stdout: runtime.lineBuffered(onStdoutLine),
50+
stderr: runtime.lineBuffered(onStderrLine),
51+
outputBuffers: true,
52+
};
53+
const fileSystem = runtime.MemoryFileSystem
54+
? new runtime.MemoryFileSystem({ "/": "/" })
55+
: undefined;
56+
const features = fileSystem
57+
? [
58+
runtime.useAll({
59+
withFileSystem: fileSystem,
60+
withStdio: stdio,
61+
sleep: options.sleep,
62+
}),
63+
]
64+
: [
65+
runtime.useStdio(stdio),
66+
runtime.useArgs(),
67+
runtime.useEnviron(),
68+
runtime.useClock(),
69+
runtime.usePoll({ sleep: options.sleep }),
70+
runtime.useProc(),
71+
runtime.useRandom(),
72+
];
3873
const wasi = new runtime.WASI({
3974
args: [options.modulePath, ...args],
4075
env: {},
41-
features: [
42-
runtime.useAll({
43-
withFileSystem: fileSystem,
44-
withStdio: {
45-
stdin: () => "",
46-
stdout,
47-
stderr,
48-
outputBuffers: true,
49-
},
50-
sleep: options.sleep,
51-
}),
52-
],
76+
features,
5377
});
5478

55-
if (options.withExtractFile) {
79+
if (fileSystem && options.withExtractFile) {
5680
Object.assign(wasi, {
5781
extractFile(filePath) {
5882
const node = fileSystem.lookup(filePath);

0 commit comments

Comments
 (0)