VS Code WASI Implementation

This npm module implements the WASI specification against the VS Code extension host API. This allows a WASM to read and write files from the workspace as well as reading from and writing to the terminal using stdin and stdout.
Example
The following example is written for the Desktop version of VS Code since it is easier to execute and test. Main reason is that WebAssembly execution for the browser needs cross origin isolation to be enabled.
To enable this for the browser version of VS Code add the query string ?vscode-coi=
to the URL.
The C file
#include <stdio.h>
int main(void)
{
printf("Hello, World\n");
return 0;
}
This file needs to be compiled to the wasm-wasi
target. This is best done using the WASI-SDK. When installed use the following command to compile it
clang hello.c -o hello.wasm
The WASM worker
Web assembly execution in VS Code is only supported when executing the web assembly in a separate worker. I can't be executed in the same worker as the extension code is running. A corresponding worker looks like this:
import * as fs from 'fs';
import * as path from 'path';
import { parentPort } from 'worker_threads';
import { ClientConnection } from '@vscode/sync-api-common/node';
import { ApiClient, Requests } from '@vscode/sync-api-client';
import { WASI } from '@vscode/wasm-wasi/node';
if (parentPort === null) {
process.exit();
}
const connection = new ClientConnection<Requests>(parentPort);
connection.serviceReady().then(async (params) => {
const name = 'Run C Example';
const apiClient = new ApiClient(connection);
const exitHandler = (rval: number): void => {
apiClient.process.procExit(rval);
};
const wasi = WASI.create(name, apiClient, exitHandler, {
mapDir: []
});
const wasmFile = path.join(__dirname, 'hello.wasm');
const binary = fs.readFileSync(wasmFile);
const { instance } = await WebAssembly.instantiate(binary, {
wasi_snapshot_preview1: wasi
});
wasi.initialize(instance);
(instance.exports._start as Function)();
apiClient.process.procExit(0);
}).catch(console.error);
The actual extension code
The actual extension sets up the sync version of the VS Code api and starts the worker thread executing the web assembly code.
import * as path from 'path';
import { Worker } from 'worker_threads';
import { commands, ExtensionContext, window } from 'vscode';
import { ServiceConnection } from '@vscode/sync-api-common/node';
import { ApiService, Requests } from '@vscode/sync-api-service';
export async function activate(_context: ExtensionContext) {
commands.registerCommand('vscode-wasm-wasi-c-example.run', () => {
const name = 'Run C Example';
const worker = new Worker(path.join(__dirname, './worker.js'));
const connection = new ServiceConnection<Requests>(worker);
const apiService = new ApiService(name, connection, {
exitHandler: (_rval) => {
process.nextTick(() => worker.terminate());
}
});
const terminal = window.createTerminal({ name, pty: apiService.getPty() });
terminal.show();
connection.signalReady();
});
}
export function deactivate() {
}
The complete example, include a package.json
and a tsconfig.json
file, can be found here