vscode-wasm

repository·main·Indexed 19 days ago

https://github.com/microsoft/vscode-wasm

Infrastructure and APIs for running VS Code components and extensions within a WebAssembly (WASM) environment using WASI and the WASM Component Model. Includes modules for synchronous communication between workers (@vscode/sync-api-common, @vscode/sync-api-client, @vscode/sync-api-service), a WASI implementation using the extension host (vscode-wasi), and tools for generating TypeScript bindings from WIT files via wit2ts.

Tokens
77.1K
Snippets
235
Records
331
Agent score
65%

What's inside vscode-wasm

  1. What is the Web Shell extension?

    main
    The Web Shell is a WASM-based implementation for VS Code for the Web that enables the execution of Unix-like commands (e.g., ls, cat) within the browser environment. It provides extension points that allow other extensions to contribute commands and file systems, which are then stored within the extension's location folder inside the web shell.
  2. Overview of the WASM Component Model implementation

    main

    This package provides VS Code's implementation of the WASM component model. It includes an implementation of the canonical ABI and the wit2ts tool, which is used to generate TypeScript bindings from a WIT (WebAssembly Interface Type) file.

    Developers can use this to integrate WebAssembly modules into VS Code extensions, allowing for high-performance logic (e.g., written in Rust) to be called via typed TypeScript interfaces.

  3. What is VS Code API Common?

    main

    The @vscode/sync-api-common npm module implements a synchronous communication mechanism between two web workers. This allows a secondary worker to access asynchronous APIs from a main worker (running in Node.js or a Browser) in a synchronous manner.

    Requirements and Constraints

    • Shared Memory: The implementation relies on SharedArrayBuffers and Atomics.
    • Environment Setup:
      • Node.js: Requires a modern version of Node that supports these features.
      • Browser: Requires specific security headers to be enabled (refer to MDN for details on SharedArrayBuffer requirements).
    • Data Transfer: The library works most efficiently when using typed arrays (e.g., Uint8Array). When passing JSON structures, the library may perform two calls to the service to receive the data.
  4. Understand the performance implications of WASI Preview 2 in VS Code

    main

    WASI Preview 2 uses the WASM Component Model, which relies heavily on resources (conceptually objects). Interacting with these resources (e.g., setting an HTTP header) requires frequent calls from the WASM environment into the JavaScript host.

    In a VS Code environment, this creates significant overhead because:

    1. Extension Host Isolation: WASM code must run in a separate worker to avoid blocking the VS Code extension host.
    2. Context Switching: Accessing the VS Code API requires a message-passing cycle between the WASM worker and the extension host worker.
    3. Multi-threading Constraints: While WASM threads share a memory region, JavaScript workers (which back WASM threads) have independent heaps. Sharing host-side state (like a stream or a resource) between workers requires expensive synchronization or data copying.

    Performance Gap: Retrieving a number via the current context-switching mechanism can be up to 3000x slower than using SharedArrayBuffers in a single thread (e.g., ~30 seconds for 1 million calls vs ~10ms).

  5. Configure Emscripten Memory and Threads

    main

    Control how memory is allocated and how threading is handled using these flags:

    • USE_PTHREADS: Enables support for pthreads (equivalent to the -pthread flag). This requires IMPORTED_MEMORY to be enabled in many configurations.
    • IMPORTED_MEMORY: Set to 1 to define the WebAssembly.Memory object in JavaScript outside of the Wasm module. This is required for:
      • USE_PTHREADS
      • RELOCATABLE
      • ASYNCIFY_LAZY_LOAD_CODE
      • WASM2JS (where WASM=0)
  6. Implement synchronous RPC requests with @vscode/sync-api-common

    main

    To use the sync API, you must define a shared request type that describes the method name, parameters, and the expected result shape using the VariableResult type. This type acts as the contract between the ClientConnection and the ServiceConnection.

    import { VariableResult } from '@vscode/sync-api-common';
    
    export type Requests = {
    	method: 'getValue';
    	params: {
    		arg: number;
    	};
    	result: VariableResult<{ value: string }>;
    }
  7. How calling into the extension host works (Current Mechanism)

    main

    To call the VS Code extension host from a WASM worker, the following lifecycle is currently required:

    1. Proxy Installation: Install a function proxy on the WASM side.
    2. Serialization: When the proxy is called, serialize arguments and copy data from WASM memory into a SharedArrayBuffer (the only currently viable way to share data with the extension host).
    3. Message Passing: Post a message to the extension host thread.
    4. Suspension: Suspend the WASM thread (note: this will be mitigated once WASM has native async support).
    5. Execution: The extension host thread computes the necessary data.
    6. Storage: The result is stored back into the SharedArrayBuffer.
    7. Resumption: Resume the WASM worker thread.
  8. Use MODULARIZE to instantiate multiple modules

    main

    Setting MODULARIZE = 1 changes the output from a global Module object to a factory function (default name Module, but configurable via EXPORT_NAME). This factory function returns a Promise that resolves with the module instance.

    Usage Patterns:

    // Using async/await (Recommended)
    const module = await createModule({ option: value });
    
    // Using .then()
    let module;
    createModule({ option: value }).then(instance => {
      module = instance;
    });

    Important Notes:

    • When using MODULARIZE, default values for the module instance must be passed as an argument to the factory function, as the compiler will not look for a global Module object.
    • If WASM_ASYNC_COMPILATION is disabled (synchronous compilation), the factory function returns the Module object directly instead of a Promise.
    const module = await createModule({ option: value });
  9. Compile dynamic linking examples with Emscripten

    main

    Emscripten provides a method for 'faux dynamic linking' using SIDE_MODULE and MAIN_MODULE flags. This allows you to compile a side module and link it to a main module for execution in a Node.js or browser environment.

    emcc -c func1.c -o out/func1.o
    emcc -sSIDE_MODULE out/func1.o -o func1.wasm
    emcc -sMAIN_MODULE main.c func1.wasm -o main.js
    node main.js
  10. Build a WASM module with static linking using wasi-sdk

    main

    To create a WebAssembly module that uses static linking with the wasi-sdk, you must compile individual C source files into object files and then link them together into a single .wasm file. This process requires the clang tool provided by the wasi-sdk.

    Workflow

    1. Compile the dependency module (e.g., module1.c) to an object file.
    2. Compile the main entry point (e.g., main.c) to an object file.
    3. Link the object files into the final main.wasm binary.
    4. Run the resulting module using a WASM runtime like wasmtime.
    ~/bin/wasi-sdk/bin/clang module1.c -c -o out/module1.o
    ~/bin/wasi-sdk/bin/clang main.c -c -o out/main.o
    ~/bin/wasi-sdk/bin/clang -o main.wasm out/module1.o out/main.o
    wasmtime main.wasm

    Expected Output

    The result is 3