Skip to content

portal-co/pit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PIT - Portal Interface Types for WebAssembly

PIT (Portal Interface Types) is a WebAssembly interface definition language (IDL) and toolchain for defining and implementing cross-module interfaces. It provides a structured format for describing interfaces, methods, and resource types, and a set of tools to generate language bindings and transform WebAssembly modules to conform to the PIT ABI.

The core parsing library and IDL specification live in a separate crate: pit-core.

Current status: Alpha (version 0.5.0-alpha.1). The project is under active development. ABI v1 is functional. ABI v2 (pitx) is a work in progress with no stable implementation yet.

What it does

PIT lets you define an interface as a set of typed methods operating on resources. Given an interface definition, the toolchain can:

  • Generate Rust guest bindings (code a .wasm module compiles against)
  • Generate Rust host bindings (code to instantiate and call into a PIT module)
  • Generate TypeScript host adapters with JSPI when available
  • Generate C WASM glue headers (P<hex>_wasm.h) alongside more-pit canonical P<hex>.h
  • Generate Scala and Java TeaVM WASM glue (P<hex>TeaVm.*) — canonical P<hex> interfaces come from more-pit
  • Transform WebAssembly binary modules: lower externref to i32 table indices, convert TPIT intermediate format to PIT ABI v1, canonicalize and jigger unique resource IDs
  • Embed interface type metadata into a module's custom sections

Migration: R<hex> / B<hex>P<hex> (breaking)

Public generated traits R<hex> (guest) and B<hex> (host) are removed. Implement the canonical interface from more-pit instead:

Role User implements pit generates (internal)
Rust guest P<hex> from pit-rust-generic / pit-gen --backend rust pit_ffi_<hex> module
Rust host P<hex> wasm_<hex> import registration
C guest P<hex>_t from pit-gen --backend c P<hex>_wasm.h glue
TS host P<hex> or AP<hex> (async) from pit-gen --backend ts / ts-async Wasm adapter + @portal/pit-ts-host runtime
JVM guest P<hex> from pit-gen --backend java / scala P<hex>TeaVm.* TeaVM glue

Regenerate after upgrading:

scripts/autobuild-all.sh ../more-pit

Replace impl R<hex> with impl P<hex> (or P<hex>_t in C) and re-run codegen.

TypeScript host + JSPI

Async host mode mirrors more-pit’s existing split:

  • Sync: pit-gen --backend tsP<hex>
  • Async: pit-gen --backend ts-asyncAP<hex>

Generate Wasm host wiring:

pit generate ts-host [--async] interface.pit output_dir/P<hex>_host.ts
pit generate host-all [--more-pit DIR] [--async] [--out generated/host]

When WebAssembly.Suspending / promising are available (Chromium 137+, Firefox), async adapters use JSPI; otherwise they fall back to sync P<hex> imports.

Interface format

PIT interfaces are plain text files:

{
    method_name(param_types) -> (return_types);
    another_method(I32, I64) -> (F64);
}

Argument types

  • I32, I64 - 32/64-bit integers
  • F32, F64 - 32/64-bit floats
  • R<resource_id> - owned resource reference
  • R<resource_id>n - nullable resource reference
  • R<resource_id>& - borrowed resource reference

Resource IDs are SHA3-based hex hashes derived from the interface content.

Attributes

[version=1]{
    [async]get_data(I32) -> (R<buffer>);
}

For the full format specification, see SPEC.md in pit-core.

ABI versions

ABI v1

Resources are identified by a content-derived unique ID. The ABI uses externref for resource handles.

Module exports follow the pattern:

pit/<resource id>/~<unique id>/<method>
pit/<resource id>/~<unique id>.drop

Module imports follow the pattern:

pit/<resource id>.~<method>
pit.drop

ABI v2 (work in progress)

All ABI v2 import modules and exports start with pitx. No stable definition yet.

TPIT (Tablified PIT)

Many WebAssembly toolchains (including LLVM/Rust targeting wasm32-unknown-unknown) do not natively support externref. TPIT is an intermediate binary format that replaces externref handles with integer indices into a WebAssembly table. The pit untpit command converts a TPIT module into a proper PIT ABI v1 module.

Compilation flow for Rust guests

Rust source code
     │ compile with TPIT bindings (tpit-rt)
     ▼
TPIT module (.wasm)       ← uses tpit/* imports, i32 table handles
     │ pit untpit
     ▼
PIT ABI v1 module (.wasm) ← uses pit/* imports, externref handles
  1. Generate TPIT Rust bindings from a PIT interface:

    pit generate rust-guest interface.pit bindings.rs
  2. Compile the Rust crate (with tpit-rt as a dependency):

    cargo build --target wasm32-unknown-unknown --release
  3. Convert the TPIT output to PIT ABI v1:

    pit module untpit target/wasm32-unknown-unknown/release/my_module.wasm output.wasm

Interface definitions (more-pit)

Standard PIT interface definitions live in the sibling more-pit repository under more-pit/pit/:

File Resource ID Description
common/buffer.pit 867207405f... 32-bit addressable byte buffer (read8, write8, size)
common/buffer64.pit 68da167712... 64-bit addressable byte buffer
common/reader.pit Produces buffer or buffer64 resources
common/writer.pit Consumes buffer or buffer64 resources

Pure trait implementations (language-agnostic logic) are canonical in more-pit/impl/ and spliced into this repo via pit splice.

Generated output (generated/)

Checked-in generated artifacts:

  • generated/guest/ — per-interface WASM glue (Rust pit_ffi_*, C P*_wasm.h, TeaVM P*TeaVm.*) from pit generate all
  • generated/host/ts/ — TypeScript host adapters from pit generate host-all
  • generated/impl/ — spliced copies of more-pit/impl/ canonicals

Regenerate with:

scripts/autobuild-all.sh ../more-pit
# or guest-only:
scripts/autobuild-guest.sh ../more-pit

Crates

Core tooling

Crate Description
pit-cli Command-line interface for all PIT operations
pit-splice Rice-based splicing of canonical implementations from more-pit
pit-patch WebAssembly module transformation (TPIT unwrap, externref lowering, canonicalization, embedding interface metadata)

Rust code generation

Crate Description
pit-rust-guest Generates Rust guest WASM glue (P<hex> re-export + pit_ffi_<hex>)
pit-rust-host-core Generates Rust host WASM import wiring (wasm_<hex>)
pit-ts-host-core Generates TypeScript host Wasm adapters
pit-rust-host-lib Runtime support for hosting PIT modules (uses wasm_runtime_layer)
pit-rust-externref Configures the externref crate's processor for PIT's drop function

Runtime libraries

Crate Description
tpit-rt TPIT runtime: the Tpit<D> type wrapping an i32 table handle with RAII drop via tpit.drop import
pit-basic Implementations of the standard buffer interfaces for Vec<u8>, Box<[u8]>, slices

Other language targets

Crate Description
pit-c Generates internal P<hex>_wasm.h glue (includes canonical P<hex>.h from more-pit)
pit-teavm Generates TeaVM WASM glue (P<hex>TeaVm.*) — canonical interfaces from more-pit
pit-wit-bridge Bridge between PIT and WIT (WebAssembly Interface Types); currently nearly empty

CLI reference

pit module untpit <input.wasm> <output.wasm>      # convert TPIT to PIT ABI v1
pit module lower <input.wasm> <output.wasm>        # lower externref to i32 table indices
pit module jigger <input.wasm> <output.wasm>       # regenerate unique IDs based on content
pit module embed [-<interface.pit>...] <input.wasm> <output.wasm>  # embed interface metadata

pit generate rust-guest <input.pit> [options] [<output.rs>]
pit generate teavm [--pkg NAME] <input.pit> <output.scala>   # default pkg: pc.portal.pit.guest.scala
pit generate java [--pkg NAME] <input.pit> <output.java>     # default pkg: pc.portal.pit.guest
pit generate c <input.pit> <output.h>              # writes P<hex>_wasm.h glue
pit generate ts-host [--async] <input.pit> <output.ts>
pit generate host-all [--more-pit DIR] [--async] [--out DIR]
pit generate package <input.pit> [options] <output-dir>      # Rust + Java + Scala + C + build files
pit generate all [--more-pit DIR] [--out DIR]              # full guest autobuild

pit splice <file>...                                         # rice splice from more-pit impl/
pit splice --all [--more-pit DIR]                            # refresh all known splice targets

pit hash <input.pit> [<input2.pit> ...]                    # print resource ID of each interface

generate rust-guest options

pit generate rust-guest <input.pit> <output.rs>
pit generate rust-guest <input.pit> --extern-externref <output.rs>   # use externref directly
pit generate rust-guest <input.pit> --preserve-docs <output.rs>    # keep existing doc comments
pit generate rust-guest <input.pit> --salt <bytes> <output.rs>     # or set PIT_SALT env

With no output path, rust-guest writes to stdout.

Legacy flat invocations (pit rust-guest, pit untpit, etc.) still work with a deprecation warning.

Splice workflow

Canonical pure implementations live in more-pit/impl/. Refresh spliced copies in pit with:

pit splice --all --more-pit ../more-pit

In more-pit:

pit-gen splice --all

Dependencies

The workspace depends on:

  • portal-pc-waffle — fork of the waffle WebAssembly IR library, used for module analysis and transformation
  • pit-core — the PIT parser and core data structures (git dep ^0.5.0-alpha.1, locally patched via portal-hot/.cargo/config.toml)
  • more-pit — canonical .pit interfaces and pure trait implementations (pit-lang-generic, pit-rust-generic, pit-gen; path dep ../more-pit/crates/*)

Building

cargo build --release

Note: portal-pc-waffle is pulled from a git repository. The workspace Cargo.toml has a known typo in that URL (httpsd://) which must be corrected before the build will work.

Compile tests

Compile tests invoke real language toolchains and fail hard when a toolchain is missing. See docs/compile-tests.md for the full policy and required tools (javac, scalac, clang/cc, tsc, …).

Agents: see AGENTS.md.

License

CC0-1.0 (Public Domain)

About

extremely minimalistic object capabilities for wasm

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors