Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,28 @@ jobs:
run: just ${{ matrix.recipe }} --examples

# ---------------------------------------------------------------------------
# Componentization suites against the debug runtime (`just test-componentize`).
# That recipe builds the runtime for wasm32-wasip3, the target the componentizer
# links against, so this job installs beta and a wasi-sdk with a sysroot for it.
# Componentization suites against the debug runtime (`just test-componentize`),
# built for each wasm target (`RUNTIME_TARGET`). A wasm32-wasip3 build needs
# beta and a wasi-sdk with a sysroot for it, which the setup action installs.
# ---------------------------------------------------------------------------
componentize:
name: Componentize
name: Componentize (${{ matrix.target }})
strategy:
fail-fast: false
matrix:
target: [wasm32-wasip2, wasm32-wasip3]
runs-on: ubuntu-latest
env:
RUNTIME_TARGET: ${{ matrix.target }}
WASM_TARGET: ${{ matrix.target }}
WASMTIME_BACKTRACE_DETAILS: '1'
RUST_BACKTRACE: 'full'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: ./.github/actions/setup
with:
target: wasm32-wasip3
target: ${{ matrix.target }}

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
Expand All @@ -149,7 +155,7 @@ jobs:

# Snapshots with `wizer-initialize`, including CLI tools whose `run` export a snapshot calls.
- name: Test Wizer snapshots
run: WASM_TARGET=p3 just test-wizer
run: just test-wizer

# ---------------------------------------------------------------------------
# WPT suite: {native, wasm32-wasip2, wasm32-wasip3}
Expand Down Expand Up @@ -205,8 +211,9 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# `just check-gc` also checks the wasm32-wasip2 build, which needs the target's std and the
# WASI SDK, and runs crown's own tests.
# `just check-gc` also checks the wasm32-wasip2 and wasm32-wasip3 builds, which need the WASI
# SDK, and runs crown's own tests. Crown's toolchain, which rustup installs from
# `crown/rust-toolchain.toml`, brings both targets' std.
- uses: ./.github/actions/setup
with:
target: wasm32-wasip2
Expand Down
20 changes: 19 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exclude = ["crown"]
resolver = "2"

[workspace.dependencies]
async-task = "4.7.1"
bitflags = "2"
bytes = "1"
clap = { version = "4.5.60", features = ["derive"] }
Expand Down Expand Up @@ -48,10 +49,13 @@ smallvec = "1.16"
starling-macro = { path = "crates/starling-macro" }
tokio = { version = "1", features = ["rt", "time", "net", "io-util"] }
url = "2.5.8"
wasip2 = "2.0.1"
wasip3 = { version = "0.9.0", features = ["http-compat"] }
wasmparser = { version = "0.262", default-features = false, features = ["validate", "component-model"] }
# Must equal the `wit-bindgen` version `wasip3` depends on. See `starling/Cargo.toml`.
wit-bindgen = { version = "=0.62.0", features = ["macros"] }
# Must equal the `wit-bindgen` version `wasip2` and `wasip3` depend on. See `starling/Cargo.toml`.
# The async features are enabled only by the wasm32-wasip3 dependency tables, so a wasm32-wasip2
# build uses none of the component model's async ABI.
wit-bindgen = { version = "=0.62.0", default-features = false, features = ["macros", "realloc", "std", "bitflags", "macro-string"] }
wit-bridge = { path = "crates/wit-bridge" }
wit-name-mapping = { path = "crates/wit-name-mapping" }
wpt-support = { path = "crates/wpt-support" }
Expand Down
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ JavaScript application and a WIT world.
- [Components that serve and run](#components-that-serve-and-run)
- [Initialization](#initialization)
- [Refused worlds](#refused-worlds)
- [WASIp2 runtimes](#wasip2-runtimes)
- [Disabling WASI features](#disabling-wasi-features)
- [The interfaces the runtime imports](#the-interfaces-the-runtime-imports)
- [WIT values in JavaScript](#wit-values-in-javascript)
Expand Down Expand Up @@ -415,6 +416,24 @@ Componentizing these worlds fails with an error naming the function or export:
- a world that imports an interface at several compatible versions, from its own WIT or the
runtime's, where a version lacks a type or function that a lower one declares.

### WASIp2 support

StarlingMonkey has full WASIp2 support, enabling it to run on hosts that don't support WASIp3
interfaces or the Component Model's async functions, `future`s, and `stream`s.

Besides using WASIp2 APIs and `wasi:io` for streaming, a key implication is that such a build
doesn't support concurrent reuse, since incoming event handlers only return from the stack once
they have fully finished processing the event, including any post-return work, such as
body-streaming and promises passed to `waitUntil`.

The componentizer's CLI options, such as `--export-http`, automatically use the interface version
the runtime targets. That is, a runtime built for `wasm32-wasip2` will export
`wasi:http/incoming-handler@0.2.0` with `--export-http` set.

Trying to target a world with `async` functions, or using `stream` or `future`, will fail with a
`wasm32-wasip2` runtime. That limitation could be lifted, though it's advisable to use a P3 runtime
when targeting host environments that support WASIp3.

### Disabling WASI features

`--disable` takes a comma-separated list of `stdio`, `random`, `clocks`, `http` and `filesystem`,
Expand Down Expand Up @@ -467,9 +486,11 @@ highest otherwise. Its types then have their plain names. A version that lacks a
of a lower compatible version is [refused](#refused-worlds).

A module specifier names an interface by its canonical version, as in `wasi:cli/environment@0.3`,
and `starling-componentize imports` and the
[TypeScript declarations](#typescript-declarations-for-a-world) name it that way. A specifier with
any compatible full version, such as `wasi:cli/environment@0.3.0`, resolves to the same module.
and `starling-componentize imports` and the [TypeScript
declarations](#typescript-declarations-for-a-world) name it that way. A specifier with any
compatible full version, such as `wasi:cli/environment@0.3.0`, resolves to the same module. A
runtime built for WASIp2 imports its WASI interfaces at `0.2` versions, so the same import is
spelled `wasi:cli/environment@0.2`.

### WIT values in JavaScript

Expand Down Expand Up @@ -1189,14 +1210,18 @@ just check-all # more extensive tests, including GC checks
`--workspace` by default. It accepts all additional arguments to `cargo test`,
so to test specific packages, pass `-p [package name]`.

**For WebAssembly (WASIp2):**
**For WebAssembly:**

```bash
just build-wasm # debug build for wasm32-wasip2
just test-wasm # all Rust tests, on wasm32-wasip2
just check-wasm # fmt-check + clippy + wasm tests
```

A wasm32-wasip2 build imports and exports only WASI 0.2 interfaces and uses none of the Component
Model's async features. It runs on WASIp2 hosts, with the restrictions [WASIp2
runtimes](#wasip2-runtimes) lists. A wasm32-wasip3 build imports and exports WASI 0.3 interfaces.

`WASM_TARGET` selects the wasm target of the recipes above, the wasm WPT recipes and
`just test-wizer`, as `p2`, `p3` or a full triple. It defaults to `p2`:

Expand All @@ -1218,8 +1243,8 @@ component `target/wasm32-wasip2/debug/starling.wasm` from the crate's `cdylib` t
`target/wasm32-wasip3/release/starling.wasm`, with SpiderMonkey, libc and libc++ statically linked
and dead-code eliminated. `just build-runtime-debug` builds it with `debugmozjs`, at
`target/wasm32-wasip3/debug/starling.wasm`. `RUNTIME_TARGET` selects their target as `WASM_TARGET`
does, and defaults to `p3`. `RUNTIME_TARGET=p2` builds for wasm32-wasip2 from the stable toolchain
and an older SDK.
does, and defaults to `p3`. `RUNTIME_TARGET=p2` builds the runtime for WASIp2 hosts, from the
stable toolchain.

The component runs scripts under `wasmtime run`, serves under `wasmtime serve`, and is the runtime
`starling-componentize` links against. `just test-runtime` builds the debug component and runs two
Expand Down
2 changes: 1 addition & 1 deletion componentize/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ mimalloc = "0.1.52"
tokio = { version = "1", features = ["macros", "rt"] }
proptest = "1.11"
# `default-features = false` drops `default-send-request`, so no request reaches a real network.
wasmtime-wasi-http = { version = "50", features = ["p3"], default-features = false }
wasmtime-wasi-http = { version = "50", features = ["p2", "p3"], default-features = false }
http = "1"
http-body = "1"
http-body-util = "0.1"
Expand Down
9 changes: 7 additions & 2 deletions componentize/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ const CLI_WORLD_NAME: &str = "command";
/// The name of the world selected by `--export-http`, which exports
/// `wasi:http/handler`. The interface is implemented by the application, or by
/// the runtime, which dispatches each request to the `fetch` listeners that the
/// application added while it was initialized.
/// application added while it was initialized. A runtime built for WASIp2
/// exports `wasi:http/incoming-handler` instead.
const SERVE_WORLD_NAME: &str = "serve";

/// The WIT sources of the worlds selected by `--export-cli` and
Expand Down Expand Up @@ -81,7 +82,11 @@ impl BuiltinWorlds {
serve: builtin_world_wit(
"starling:serve",
SERVE_WORLD_NAME,
&interface(crate::HTTP_HANDLER_INTERFACE_PREFIX)?,
&interface(
runtime
.map_or(crate::WasiVersion::P3, crate::Runtime::wasi_version)
.http_handler_prefix(),
)?,
),
})
}
Expand Down
8 changes: 5 additions & 3 deletions componentize/src/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,12 +467,14 @@ enum StubBody {
}

/// The body of the stub of `func` in the interface named `interface`: zeroes
/// for a clock's `now`, the seed for `get-insecure-seed` if `insecure_seed` is
/// set, and a trap for everything else.
/// for a clock's `now`, the seed for `get-insecure-seed` (`insecure-seed` in
/// WASI 0.2) if `insecure_seed` is set, and a trap for everything else.
fn stub_body(interface: &str, func: &Function, insecure_seed: Option<u128>) -> StubBody {
match (func.name.as_str(), insecure_seed) {
("now", _) if interface.starts_with("wasi:clocks/") => StubBody::Zero,
("get-insecure-seed", Some(seed)) if interface.starts_with(INSECURE_SEED_INTERFACE) => {
("get-insecure-seed" | "insecure-seed", Some(seed))
if interface.starts_with(INSECURE_SEED_INTERFACE) =>
{
StubBody::Seed(seed)
}
_ => StubBody::Trap,
Expand Down
Loading
Loading