Skip to content

Commit 688e8fe

Browse files
Use @bjorn3/browser_wasi_shim as WASI implementation
1 parent 3abdbd2 commit 688e8fe

File tree

4 files changed

+37
-590
lines changed

4 files changed

+37
-590
lines changed

entrypoint/common.ts

Lines changed: 20 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import { WASI } from "@wasmer/wasi";
16-
import { WasmFs } from "@wasmer/wasmfs";
17-
import * as path from "path-browserify";
15+
import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory } from "@bjorn3/browser_wasi_shim";
1816
import type { SwiftRuntime, SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
1917

2018
export type Options = {
@@ -35,38 +33,29 @@ export const WasmRunner = (rawOptions: Options | false, SwiftRuntime: SwiftRunti
3533
swift = new SwiftRuntime();
3634
}
3735

38-
const wasmFs = createWasmFS(
39-
(stdout) => {
36+
const args = options.args || [];
37+
const fds = [
38+
new OpenFile(new File([])), // stdin
39+
ConsoleStdout.lineBuffered((stdout) => {
4040
console.log(stdout);
4141
options.onStdout?.call(undefined, stdout);
42-
},
43-
(stderr) => {
42+
}),
43+
ConsoleStdout.lineBuffered((stderr) => {
4444
console.error(stderr);
4545
options.onStderr?.call(undefined, stderr);
46-
}
47-
);
48-
49-
wasmFs.fs.mkdirSync("/sandbox");
46+
}),
47+
new PreopenDirectory("/", new Map()),
48+
];
5049

51-
const wasi = new WASI({
52-
args: options.args,
53-
env: {},
54-
preopenDirectories: {
55-
"/": "/sandbox",
56-
},
57-
bindings: {
58-
...WASI.defaultBindings,
59-
fs: wasmFs.fs,
60-
path: path,
61-
},
50+
const wasi = new WASI(args, [], fds, {
51+
debug: false
6252
});
6353

6454
const createWasmImportObject = (
6555
extraWasmImports: WebAssembly.Imports,
66-
wasmModule: WebAssembly.Module
6756
): WebAssembly.Imports => {
6857
const importObject: WebAssembly.Imports = {
69-
wasi_snapshot_preview1: wrapWASI(wasi, wasmModule),
58+
wasi_snapshot_preview1: wasi.wasiImport,
7059
};
7160

7261
if (swift) {
@@ -94,19 +83,19 @@ export const WasmRunner = (rawOptions: Options | false, SwiftRuntime: SwiftRunti
9483
},
9584
};
9685
const module = await WebAssembly.compile(wasmBytes);
97-
const importObject = createWasmImportObject(extraWasmImports, module);
86+
const importObject = createWasmImportObject(extraWasmImports);
9887
const instance = await WebAssembly.instantiate(module, importObject);
9988

10089
if (swift && instance.exports.swjs_library_version) {
10190
swift.setInstance(instance);
10291
}
10392

104-
// Start the WebAssembly WASI instance
105-
wasi.start(instance);
106-
107-
// Initialize and start Reactor
108-
if (typeof instance.exports._initialize == "function") {
109-
instance.exports._initialize();
93+
if (typeof instance.exports._start === "function") {
94+
// Start the WebAssembly WASI instance
95+
wasi.start(instance as any);
96+
} else if (typeof instance.exports._initialize == "function") {
97+
// Initialize and start Reactor
98+
wasi.initialize(instance as any);
11099
if (typeof instance.exports.main === "function") {
111100
instance.exports.main();
112101
} else if (typeof instance.exports.__main_argc_argv === "function") {
@@ -131,73 +120,3 @@ const defaultRunnerOptions = (options: Options | false): Options => {
131120
}
132121
return options;
133122
};
134-
135-
const createWasmFS = (
136-
onStdout: (text: string) => void,
137-
onStderr: (text: string) => void
138-
): WasmFs => {
139-
// Instantiate a new WASI Instance
140-
const wasmFs = new WasmFs();
141-
142-
// Output stdout and stderr to console
143-
const originalWriteSync = wasmFs.fs.writeSync;
144-
(wasmFs.fs as any).writeSync = (
145-
fd: number, buffer: Buffer | Uint8Array,
146-
offset?: number, length?: number, position?: number
147-
): number => {
148-
const text = new TextDecoder("utf-8").decode(buffer);
149-
if (text !== "\n") {
150-
switch (fd) {
151-
case 1:
152-
onStdout(text);
153-
break;
154-
case 2:
155-
onStderr(text);
156-
break;
157-
}
158-
}
159-
return originalWriteSync(fd, buffer, offset, length, position);
160-
};
161-
162-
return wasmFs;
163-
};
164-
165-
const wrapWASI = (wasiObject: WASI, wasmModule: WebAssembly.Module): WebAssembly.ModuleImports => {
166-
// PATCH: @wasmer-js/wasi@0.x forgets to call `refreshMemory` in `clock_res_get`,
167-
// which writes its result to memory view. Without the refresh the memory view,
168-
// it accesses a detached array buffer if the memory is grown by malloc.
169-
// But they wasmer team discarded the 0.x codebase at all and replaced it with
170-
// a new implementation written in Rust. The new version 1.x is really unstable
171-
// and not production-ready as far as katei investigated in Apr 2022.
172-
// So override the broken implementation of `clock_res_get` here instead of
173-
// fixing the wasi polyfill.
174-
// Reference: https://github.com/wasmerio/wasmer-js/blob/55fa8c17c56348c312a8bd23c69054b1aa633891/packages/wasi/src/index.ts#L557
175-
const original_clock_res_get = wasiObject.wasiImport["clock_res_get"];
176-
177-
wasiObject.wasiImport["clock_res_get"] = (clockId: number, resolution: number) => {
178-
wasiObject.refreshMemory();
179-
return original_clock_res_get(clockId, resolution);
180-
};
181-
182-
// @wasmer-js/wasi polyfill does not support all WASI syscalls like `sock_accept`.
183-
// So we need to insert a dummy function for unimplemented syscalls.
184-
const __WASI_ERRNO_NOTSUP = 58;
185-
for (const importEntry of WebAssembly.Module.imports(wasmModule)) {
186-
const { module: importModule, name: importName, kind: importKind } = importEntry;
187-
// Skip dummy import entries for non-WASI and already implemented syscalls.
188-
if (
189-
importModule !== "wasi_snapshot_preview1" ||
190-
importKind !== "function" ||
191-
wasiObject.wasiImport[importName]
192-
) {
193-
continue;
194-
}
195-
196-
wasiObject.wasiImport[importName] = () => {
197-
console.warn(`WASI syscall ${importModule}.${importName} is not supported, returning ENOTSUP.`);
198-
return __WASI_ERRNO_NOTSUP;
199-
}
200-
}
201-
202-
return wasiObject.wasiImport;
203-
};

entrypoint/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
import ReconnectingWebSocket from "reconnecting-websocket";
16-
import { WASIExitError } from "@wasmer/wasi";
16+
import { WASIProcExit } from "@bjorn3/browser_wasi_shim";
1717
import { WasmRunner } from "./common.js";
1818
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
1919

@@ -73,7 +73,7 @@ const startWasiTask = async () => {
7373

7474
const handleExitOrError = (error: any) => {
7575
// XCTest always calls `exit` at the end when no crash
76-
if (error instanceof WASIExitError) {
76+
if (error instanceof WASIProcExit) {
7777
// pass the output to the server in any case
7878
socket.send(JSON.stringify({ kind: "testRunOutput", testRunOutput }));
7979
if (error.code === 0) {

0 commit comments

Comments
 (0)