@swiftbrowser/wasm-compiler 0.7.0 → 0.8.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.
|
@@ -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.
|
|
3
|
+
"version": "0.8.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.
|
|
42
|
+
"@swiftbrowser/wasm-tools": "0.8.0"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
45
|
"@types/node": "^26.6.4",
|