aboutsummaryrefslogtreecommitdiff
path: root/editors/vscode/client/wasmproc.js
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
commitecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 (patch)
tree70e0af76a1b13affff7c6cc8aac7b3e6ef960478 /editors/vscode/client/wasmproc.js
a
Diffstat (limited to 'editors/vscode/client/wasmproc.js')
-rw-r--r--editors/vscode/client/wasmproc.js96
1 files changed, 96 insertions, 0 deletions
diff --git a/editors/vscode/client/wasmproc.js b/editors/vscode/client/wasmproc.js
new file mode 100644
index 0000000..0aa674a
--- /dev/null
+++ b/editors/vscode/client/wasmproc.js
@@ -0,0 +1,96 @@
+// runs the language server as WebAssembly (server/lsp.wasm, built by
+// src/tools/mklspwasm.sh): the microcore with the server's image inside. it presents
+// the same surface as vmProcess: stdin to write, stdout and stderr to listen to,
+// an exit event and kill(). it is much faster than the javascript vm, because
+// the server's 64-bit words are native here and not bigints.
+//
+// the module is synchronous to load and to run, so the server starts in the
+// extension host's own thread. a step runs until the server waits for input,
+// then returns, the way vmProcess does.
+//
+// opts.log(level, text) gets what happens: the module loaded, each halt, and the
+// messages the server writes to its error stream.
+const fs = require('fs');
+const { EventEmitter } = require('events');
+
+const CHUNK = 65536; // the most a single feed can carry (wasm_chunk_cap)
+
+function wasmProcess(file, opts = {}) {
+ const log = opts.log || (() => {});
+ const t0 = Date.now();
+ const bytes = fs.readFileSync(file);
+ const mod = new WebAssembly.Module(bytes);
+ log('message', 'compiled ' + file + ' (' + bytes.length + ' bytes) in ' + (Date.now() - t0) + ' ms');
+
+ const proc = new EventEmitter();
+ const out = new EventEmitter();
+ const err = new EventEmitter();
+ let outBytes = [], msgBytes = [];
+ let dead = false, steps = 0, bytesIn = 0, bytesOut = 0;
+
+ const inst = new WebAssembly.Instance(mod, {
+ env: {
+ js_putc: c => { outBytes.push(c & 255); },
+ js_msg: c => { msgBytes.push(c & 255); },
+ },
+ });
+ const x = inst.exports;
+ if (x.wasm_chunk_cap() < CHUNK) throw new Error('lsp.wasm has a smaller input chunk than this host expects');
+
+ const flush = () => {
+ if (outBytes.length) {
+ const chunk = Buffer.from(outBytes);
+ outBytes = [];
+ bytesOut += chunk.length;
+ out.emit('data', chunk);
+ }
+ if (msgBytes.length) {
+ const text = Buffer.from(msgBytes).toString('utf8');
+ msgBytes = [];
+ log('problem', 'server says: ' + text.trim().slice(0, 500));
+ err.emit('data', Buffer.from(text, 'utf8'));
+ }
+ };
+ const after = status => {
+ steps++;
+ flush();
+ if (status !== 3 && !dead) { // it halted: the server has exited
+ dead = true;
+ log(status === 0 ? 'message' : 'problem', 'the server halted with status ' + status + ' after ' + steps + ' steps, ' + bytesIn + ' bytes in, ' + bytesOut + ' out');
+ proc.emit('exit', status);
+ }
+ };
+
+ proc.stdin = {
+ writable: true,
+ write(buf) {
+ if (dead) { log('problem', 'write to a halted server dropped (' + buf.length + ' bytes)'); return false; }
+ bytesIn += buf.length;
+ const chunkAt = x.wasm_chunk();
+ let off = 0;
+ while (off < buf.length) {
+ const n = Math.min(CHUNK, buf.length - off);
+ new Uint8Array(x.memory.buffer).set(buf.subarray(off, off + n), chunkAt);
+ if (x.wasm_feed(n) !== 0) { log('problem', 'the server input is full; dropped ' + (buf.length - off) + ' bytes'); return false; }
+ off += n;
+ }
+ after(x.wasm_resume());
+ return true;
+ },
+ };
+ proc.stdout = out;
+ proc.stderr = err;
+ proc.kill = () => {
+ if (dead) return;
+ dead = true;
+ log('message', 'server killed after ' + steps + ' steps');
+ proc.emit('exit', null);
+ };
+
+ const t1 = Date.now();
+ after(x.wasm_start());
+ log('message', 'server ran to its first wait in ' + (Date.now() - t1) + ' ms');
+ return proc;
+}
+
+module.exports = { wasmProcess };