@swiftbrowser/wasm-compiler 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,7 +99,8 @@ module this size runs under WASI; `src/node/wasi-host.ts` turns them off
99
99
  makes the CLI (`packages/cli`) build an app with these tools instead of
100
100
  `swift build`, for `build` and `dev` alike, and type-check it for `check`
101
101
  (`packages/cli/src/wasmCompiler.ts`), with no Swift install: the CLI
102
- downloads the Swift SDK for WebAssembly itself and uses the released kit,
102
+ lays out the Swift SDK for WebAssembly itself, from its npm packages
103
+ (`packages/cli/src/sdkPackages.ts`), and uses the released kit,
103
104
  whose macros come built as WebAssembly. It reproduces SwiftPM's release build
104
105
  of the staged package: one whole-module `-O` frontend job against the kit's
105
106
  modules, Foundation's trimmed ICU data as an object written in TypeScript
@@ -0,0 +1,15 @@
1
+ export interface PluginStreams {
2
+ /** Fills `buffer` from `offset` with at most `length` bytes; 0 at the end. */
3
+ read(buffer: Uint8Array, offset: number, length: number): number;
4
+ write(bytes: Uint8Array): void;
5
+ error(bytes: Uint8Array): void;
6
+ }
7
+ /** Stdin, stdout and stderr, read and written synchronously: the compiler waits on each reply. */
8
+ export declare const processStreams: PluginStreams;
9
+ /** The plugin's module: a `.wasm` file, or a brotli-compressed `.wasm.br`. */
10
+ export declare function pluginModule(file: string): WebAssembly.Module;
11
+ /**
12
+ * Serves the compiler's messages until it closes the pipe or sends the empty
13
+ * message that asks a plugin to finish; returns the exit code.
14
+ */
15
+ export declare function servePlugin(module: WebAssembly.Module, name: string, streams?: PluginStreams): number;
@@ -0,0 +1,97 @@
1
+ // A macro plugin built as WebAssembly, run as the executable plugin a native
2
+ // compiler spawns (`-load-plugin-executable`): `node plugin-executable.js
3
+ // <plugin.wasm>`. The compiler's messages arrive on stdin and the replies go
4
+ // to stdout, framed alike (an 8-byte little-endian length, then JSON), and
5
+ // each message is handed to the module's `swift_wasm_macro_v1_pump`, as the
6
+ // WebAssembly compiler's host does (../plugins.ts). The kit's macros reach a
7
+ // native toolchain this way (the CLI's kit.ts), the same file on every host.
8
+ import { readFileSync, readSync, realpathSync, writeSync } from 'node:fs';
9
+ import { pathToFileURL } from 'node:url';
10
+ import { brotliDecompressSync } from 'node:zlib';
11
+ import { ConsoleStdout, WASI, wasi } from '@bjorn3/browser_wasi_shim';
12
+ const HEADER = 8;
13
+ /** Stdin the executable fills with one message before each pump. */
14
+ class MessageInput extends ConsoleStdout {
15
+ queue = new Uint8Array();
16
+ constructor() {
17
+ super(() => { });
18
+ }
19
+ set(bytes) {
20
+ this.queue = bytes;
21
+ }
22
+ fd_read(size) {
23
+ const data = this.queue.slice(0, size);
24
+ this.queue = this.queue.slice(data.length);
25
+ return { ret: wasi.ERRNO_SUCCESS, data };
26
+ }
27
+ }
28
+ /** Stdin, stdout and stderr, read and written synchronously: the compiler waits on each reply. */
29
+ export const processStreams = {
30
+ read: (buffer, offset, length) => readSync(0, buffer, offset, length, null),
31
+ write: (bytes) => {
32
+ for (let written = 0; written < bytes.length;)
33
+ written += writeSync(1, bytes, written);
34
+ },
35
+ error: (bytes) => {
36
+ for (let written = 0; written < bytes.length;)
37
+ written += writeSync(2, bytes, written);
38
+ },
39
+ };
40
+ /** The plugin's module: a `.wasm` file, or a brotli-compressed `.wasm.br`. */
41
+ export function pluginModule(file) {
42
+ const bytes = readFileSync(file);
43
+ return new WebAssembly.Module(file.endsWith('.br') ? brotliDecompressSync(bytes) : bytes);
44
+ }
45
+ /**
46
+ * Serves the compiler's messages until it closes the pipe or sends the empty
47
+ * message that asks a plugin to finish; returns the exit code.
48
+ */
49
+ export function servePlugin(module, name, streams = processStreams) {
50
+ const stdin = new MessageInput();
51
+ const shim = new WASI([name], [], [stdin, new ConsoleStdout(streams.write), new ConsoleStdout(streams.error)], { debug: false });
52
+ const instance = new WebAssembly.Instance(module, { wasi_snapshot_preview1: shim.wasiImport });
53
+ // `main` installs the message handler and returns.
54
+ const code = shim.start(instance);
55
+ if (code !== 0)
56
+ return code;
57
+ const pump = instance.exports['swift_wasm_macro_v1_pump'];
58
+ if (!pump) {
59
+ streams.error(new TextEncoder().encode(`${name}: not a macro plugin built as WebAssembly (no swift_wasm_macro_v1_pump)\n`));
60
+ return 1;
61
+ }
62
+ const readExactly = (size) => {
63
+ const buffer = new Uint8Array(size);
64
+ for (let offset = 0; offset < size;) {
65
+ const count = streams.read(buffer, offset, size - offset);
66
+ if (count === 0)
67
+ return null;
68
+ offset += count;
69
+ }
70
+ return buffer;
71
+ };
72
+ for (;;) {
73
+ const header = readExactly(HEADER);
74
+ if (!header)
75
+ return 0;
76
+ const size = Number(new DataView(header.buffer).getBigUint64(0, true));
77
+ if (size === 0)
78
+ return 0;
79
+ const body = readExactly(size);
80
+ if (!body)
81
+ return 0;
82
+ const message = new Uint8Array(HEADER + size);
83
+ message.set(header);
84
+ message.set(body, HEADER);
85
+ stdin.set(message);
86
+ pump();
87
+ }
88
+ }
89
+ // Run as a script (through a link, too: npm links workspaces).
90
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
91
+ const file = process.argv[2];
92
+ if (!file) {
93
+ process.stderr.write('usage: plugin-executable <plugin.wasm>\n');
94
+ process.exit(2);
95
+ }
96
+ process.exit(servePlugin(pluginModule(file), file));
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/wasm-compiler",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Compiles Swift with the Swift compiler built as WebAssembly: a driver, WASI hosts and a CLI. See docs/in-browser-compiler.md.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,6 +18,10 @@
18
18
  "./node": {
19
19
  "types": "./dist/node/index.d.ts",
20
20
  "default": "./dist/node/index.js"
21
+ },
22
+ "./plugin-executable": {
23
+ "types": "./dist/node/plugin-executable.d.ts",
24
+ "default": "./dist/node/plugin-executable.js"
21
25
  }
22
26
  },
23
27
  "bin": {
@@ -35,7 +39,7 @@
35
39
  },
36
40
  "dependencies": {
37
41
  "@bjorn3/browser_wasi_shim": "^0.4.2",
38
- "@swiftbrowser/wasm-tools": "0.7.0"
42
+ "@swiftbrowser/wasm-tools": "0.9.0"
39
43
  },
40
44
  "devDependencies": {
41
45
  "@types/node": "^26.6.4",