chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "d4e317f22c3bace76cb3205003bcc34b4929037d"
},
"path_in_vcs": "crates/wit-component"
}

1823
vendor/wit-component/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

217
vendor/wit-component/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,217 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.81.0"
name = "wit-component"
version = "0.244.0"
authors = ["Peter Huene <peter@huene.dev>"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = """
Tooling for working with `*.wit` and component files together.
"""
homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component"
documentation = "https://docs.rs/wit-component"
readme = "README.md"
license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
repository = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wit-component"
[package.metadata.docs.rs]
all-features = true
[features]
dummy-module = ["dep:wat"]
semver-check = ["dummy-module"]
wat = [
"dep:wast",
"dep:wat",
]
[lib]
name = "wit_component"
path = "src/lib.rs"
[[test]]
name = "components"
path = "tests/components.rs"
harness = false
[[test]]
name = "interfaces"
path = "tests/interfaces.rs"
harness = false
[[test]]
name = "linking"
path = "tests/linking.rs"
[[test]]
name = "merge"
path = "tests/merge.rs"
[[test]]
name = "targets"
path = "tests/targets.rs"
[[test]]
name = "wit"
path = "tests/wit.rs"
[dependencies.anyhow]
version = "1.0.58"
[dependencies.bitflags]
version = "2.3.3"
[dependencies.indexmap]
version = "2.7.0"
default-features = false
[dependencies.log]
version = "0.4.17"
[dependencies.serde]
version = "1.0.166"
features = ["alloc"]
default-features = false
[dependencies.serde_derive]
version = "1.0.166"
[dependencies.serde_json]
version = "1"
[dependencies.wasm-encoder]
version = "0.244.0"
features = [
"std",
"wasmparser",
]
default-features = false
[dependencies.wasm-metadata]
version = "0.244.0"
default-features = false
[dependencies.wasmparser]
version = "0.244.0"
features = [
"simd",
"std",
"component-model",
"simd",
]
default-features = false
[dependencies.wast]
version = "244.0.0"
optional = true
default-features = false
[dependencies.wat]
version = "1.244.0"
optional = true
default-features = false
[dependencies.wit-parser]
version = "0.244.0"
features = [
"decoding",
"serde",
]
[dev-dependencies.env_logger]
version = "0.11"
[dev-dependencies.glob]
version = "0.3.0"
[dev-dependencies.libtest-mimic]
version = "0.8.1"
[dev-dependencies.pretty_assertions]
version = "1.3.0"
[dev-dependencies.wasm-metadata]
version = "0.244.0"
features = ["oci"]
default-features = false
[dev-dependencies.wasmparser]
version = "0.244.0"
features = [
"simd",
"std",
"component-model",
"features",
]
default-features = false
[dev-dependencies.wasmprinter]
version = "0.244.0"
features = ["component-model"]
default-features = false
[dev-dependencies.wat]
version = "1.244.0"
features = ["component-model"]
default-features = false
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies.wasmtime]
version = "34.0.1"
features = [
"cranelift",
"component-model",
"runtime",
"gc-drc",
]
default-features = false
[lints.clippy]
clone_on_copy = "warn"
manual_strip = "warn"
map_clone = "warn"
uninlined_format_args = "warn"
unnecessary_cast = "warn"
unnecessary_fallible_conversions = "warn"
unnecessary_mut_passed = "warn"
unnecessary_to_owned = "warn"
[lints.clippy.all]
level = "allow"
priority = -1
[lints.rust]
deprecated-safe-2024 = "warn"
keyword_idents_2024 = "warn"
missing-unsafe-on-extern = "warn"
rust-2024-guarded-string-incompatible-syntax = "warn"
rust-2024-incompatible-pat = "warn"
rust-2024-prelude-collisions = "warn"
unsafe-attr-outside-unsafe = "warn"
unsafe-op-in-unsafe-fn = "warn"
unsafe_code = "deny"
unstable_features = "warn"
unused-lifetimes = "warn"
unused-macro-rules = "warn"
unused_extern_crates = "warn"
unused_import_braces = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(fuzzing)"]

251
vendor/wit-component/README.md vendored Normal file
View File

@@ -0,0 +1,251 @@
# `wit-component`
`wit-component` is a crate for creating and interacting with WebAssembly
components based on the [component model proposal][model].
## CLI usage
The `wit-component` crate is available through the `wasm-tools` CLI suite under
two subcommands:
```
# Create a component from the input core wasm module
$ wasm-tools component new core.wasm -o component.wasm
# Extract a `*.wit` interface from a component
$ wasm-tools component wit component.wasm
```
## Features
* Creates WebAssembly [component binaries][model] from input core WebAssembly
modules. Input modules communicate with the [canonical ABI] to imported and
exported interfaces described with [`*.wit` files][wit]. The wit interface is
required to be embedded directly in the core wasm binary.
* Supports "adapters" which can be used to bridge legacy core WebAssembly
imported functions into [component model][model] functions. Adapters are
themselves core wasm binaries which will be embedded into the final component.
An adapter's exports can be imported by the main core wasm binary and the
adapter can then call component model imports.
* A [`*.wit`][wit] interface can be extracted from an existing component to
see the interface that it exports and intends to import.
[model]: https://github.com/webassembly/component-model
[canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md
[wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
## Usage
Note that this crate is intended to be a low-level detail of tooling for
components. Developers will not necessarily interact with this tooling
day-to-day, instead using wrappers such as
[`cargo-component`](https://github.com/bytecodealliance/cargo-component) which
will automatically execute `wit-component` to produce component binaries.
First `wit-component` supports the wasm-based encoding of a WIT package:
```sh
$ cat demo.wit
package my:demo;
interface host {
hello: func();
}
world demo {
import host;
}
$ wasm-tools component wit demo.wit -o demo.wasm --wasm
# The output `demo.wasm` is a valid component binary
$ wasm-tools validate --features component-model demo.wasm
$ wasm-tools print demo.wasm
# The `*.wit` file can be recovered from the `demo.wasm` as well
$ wasm-tools component wit demo.wasm
```
Toolchain authors can use `wit-component` to embed this component types section
into a core wasm binary. For a small demo here a raw `*.wat` wasm text file will
be used where the `demo.wit` argument is specified manually, however.
```sh
$ cat demo.core.wat
(module
(import "my:demo/host" "hello" (func))
)
$ wasm-tools component embed demo.wit --world demo demo.core.wat -o demo.wasm
# See that there's a new `component-type` custom section
$ wasm-tools objdump demo.wasm
# Convert the core wasm into a component now
$ wasm-tools component new demo.wasm -o demo.component.wasm
# Like before the output `demo.wasm` is a valid component binary
$ wasm-tools validate --features component-model demo.component.wasm
$ wasm-tools print demo.component.wasm
# Additionally like before the `*.wit` interface can still be extracted
$ wasm-tools component wit demo.component.wasm
```
Here the `demo.component.wasm` can now be shipped to a component runtime or
embedded into hosts.
## Custom Section Format
Toolchains producing components typically have a workflow that looks something
like this:
1. A bindings generator, often based on [`wit-bindgen`], is used to generate
source code.
2. A user writes source code against these bindings.
3. The language's compiler runs and produces a core WebAssembly module.
4. The core WebAssembly module is passed to this crate, producing a component.
Steps (1) and (4) both take WIT as input and the goal is to make it such that
you don't have to specify the same WIT on both ends. Instead the WIT from (1)
should be seamlessly passed through to (4) without developers having to
explicitly opt-in to it otherwise. To do this, the `wit-component` crate and
componentization process uses a custom section.
Part of the generated bindings from (1) above is source code (or similar) to
instruct the language to embed a custom section in the final binary. The custom
section's name must start with `component-type` but can have any number of
characters following it (e.g. it must match the regex `^component-type`). A
module is allowed to have any number of custom sections, including zero.
Each custom section describes a WIT `world`. The unit of bindings generation is
a `world`, and each `world` used during step (1), which may possibly be in
multiple separate libraries, will be encoded into custom sections. Custom
sections typically have a name that includes the bindings generator,
the bindings generator's version, the world, and an optional use-provided
string. The contents of the custom section is a wasm-encoded WIT world.
An example of this is that for this document:
```wit
package a:b;
world foo {
import host: func();
export guest: func();
}
```
this WIT world is encoded to wasm as:
```wasm
(component
(type (;0;)
(component
(type (;0;)
(component
(type (;0;) (func))
(import "host" (func (;0;) (type 0)))
(export (;1;) "guest" (func (type 0)))
)
)
(export (;0;) "a:b/foo" (component (type 0)))
)
)
(export (;1;) "foo" (type 0))
(@custom "package-docs" "\00{}")
(@producers
(processed-by "wit-component" "0.218.0")
)
)
```
More details about the wasm encoding of WIT can [be found
upstream](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md).
Note here that the WIT world is just the single `component`-type export needed
to decode a single world, no other items are encoded into this wasm.
When determining the `world` for a wasm component the componentization process
will read the input module, remove all sections that match `^component-type`,
extract the `world` that the type represents, and then "merge" all of the worlds
together. This merging process can fail if there are conflicts, but otherwise
duplicate imports are unified together.
The final componentization process then understands the WIT `world` that's being
used and assumes that the component follows the naming scheme in
[`BuildTargets.md`](https://github.com/WebAssembly/component-model/pull/378)
upstream. (note that legacy names are also accepted at this time)
## Merging Worlds
Part of what `wit-component` does when creating a component is that it will
merge WIT `world`s from a number of sources. For example each custom section
above may have a world. For tools like `wasm-component-ld` worlds may also be
supplied on the command line. The final component, however, can only have one
set of imports and exports so a single `world` needs to be created from all
these input worlds.
There are two workhorse methods for performing this merging process and both
live within the `wit-parser` crate:
* [`wit_parser::Resolve::merge_worlds`]
* [`wit_parser::Resolve::merge_world_imports_based_on_semver`]
The first is unconditionally used to merge all worlds together. Each custom
section, input argument, etc, are all merged with the
[`wit_parser::Resolve::merge_worlds`] method. This method may fail in certain
situations, primarily if there are duplicate exports specified in different
worlds (even if they're the same name they can't unify to one export as it's not
clear which export to pick). There are some other niche situations as well in
which the merging process can fail.
Once all worlds are merged together with [`wit_parser::Resolve::merge_worlds`]
there is then an optional, but enabled by default, step to "trim" the world
down based on semver versions. This method is
[`wit_parser::Resolve::merge_world_imports_based_on_semver`] and is used to
change a world such as this:
```wit
world {
import wasi:cli/environment@0.2.0;
import wasi:cli/environment@0.2.1;
}
```
into this
```wit
world {
import wasi:cli/environment@0.2.1;
}
```
Notably imports are deduplicated based on their semver versions of interfaces.
The maximum version of the interface is always selected in the end. Only
interfaces which are semver compatible are deduplicated, for example this world
cannot be deduplicated further:
```wit
world {
import wasi:cli/environment@0.2.0;
import wasi:cli/environment@0.3.0;
}
```
The main purpose for this deduplication is to enable internally within a
component various libraries and runtimes to evolve at a different pace with
respect to interface versions. By definition in WIT it should always be possible
to use a larger versions, so older imports are automatically "upgraded" to
newer imports in these situations. This ensures that, for example, only one
`wasi:io/poll/pollable` resource will be imported into the final component. If
two were imported there would be no way to actually functionally use the
resulting component.
Note that this semver-deduplication process is enabled by default but can be
disabled through various options and flags in tooling.
[`wit_parser::Resolve::merge_worlds`]: https://docs.rs/wit-parser/latest/wit_parser/struct.Resolve.html#method.merge_worlds
[`wit_parser::Resolve::merge_world_imports_based_on_semver`]: https://docs.rs/wit-parser/latest/wit_parser/struct.Resolve.html#method.merge_world_imports_based_on_semver

BIN
vendor/wit-component/libdl.so vendored Executable file

Binary file not shown.

395
vendor/wit-component/src/dummy.rs vendored Normal file
View File

@@ -0,0 +1,395 @@
use wit_parser::abi::WasmType;
use wit_parser::{
Function, LiftLowerAbi, ManglingAndAbi, Resolve, ResourceIntrinsic, TypeDefKind, TypeId,
WasmExport, WasmExportKind, WasmImport, WorldId, WorldItem, WorldKey,
};
/// Generate a dummy implementation core Wasm module for a given WIT document
pub fn dummy_module(resolve: &Resolve, world: WorldId, mangling: ManglingAndAbi) -> Vec<u8> {
let world = &resolve.worlds[world];
let mut wat = String::new();
wat.push_str("(module\n");
for (name, import) in world.imports.iter() {
match import {
WorldItem::Function(func) => {
push_imported_func(&mut wat, resolve, None, func, mangling);
}
WorldItem::Interface { id: import, .. } => {
for (_, func) in resolve.interfaces[*import].functions.iter() {
push_imported_func(&mut wat, resolve, Some(name), func, mangling);
}
for (_, ty) in resolve.interfaces[*import].types.iter() {
push_imported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling);
}
}
WorldItem::Type(id) => {
push_imported_type_intrinsics(&mut wat, resolve, None, *id, mangling);
}
}
}
if mangling.is_async() {
push_root_async_intrinsics(&mut wat);
}
// Append any intrinsics which are imported but used in exported items
// (e.g. resources)
for (name, export) in world.exports.iter() {
match export {
WorldItem::Function(func) => {
push_exported_func_intrinsics(&mut wat, resolve, None, func, mangling);
}
WorldItem::Interface { id: export, .. } => {
for (_, func) in resolve.interfaces[*export].functions.iter() {
push_exported_func_intrinsics(&mut wat, resolve, Some(name), func, mangling);
}
for (_, ty) in resolve.interfaces[*export].types.iter() {
push_exported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling);
}
}
WorldItem::Type(_) => {}
}
}
for (name, export) in world.exports.iter() {
match export {
WorldItem::Function(func) => {
push_func_export(&mut wat, resolve, None, func, mangling);
}
WorldItem::Interface { id: export, .. } => {
for (_, func) in resolve.interfaces[*export].functions.iter() {
push_func_export(&mut wat, resolve, Some(name), func, mangling);
}
for (_, ty) in resolve.interfaces[*export].types.iter() {
push_exported_resource_functions(&mut wat, resolve, name, *ty, mangling);
}
}
WorldItem::Type(_) => {}
}
}
let memory = resolve.wasm_export_name(mangling, WasmExport::Memory);
wat.push_str(&format!("(memory (export {memory:?}) 0)\n"));
let realloc = resolve.wasm_export_name(mangling, WasmExport::Realloc);
wat.push_str(&format!(
"(func (export {realloc:?}) (param i32 i32 i32 i32) (result i32) unreachable)\n"
));
let initialize = resolve.wasm_export_name(mangling, WasmExport::Initialize);
wat.push_str(&format!("(func (export {initialize:?}))"));
wat.push_str(")\n");
return wat::parse_str(&wat).unwrap();
}
fn push_imported_func(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
func: &Function,
mangling: ManglingAndAbi,
) {
let sig = resolve.wasm_signature(mangling.import_variant(), func);
let (module, name) = resolve.wasm_import_name(mangling, WasmImport::Func { interface, func });
wat.push_str(&format!("(import {module:?} {name:?} (func"));
push_tys(wat, "param", &sig.params);
push_tys(wat, "result", &sig.results);
wat.push_str("))\n");
if mangling.is_async() {
push_imported_future_and_stream_intrinsics(wat, resolve, "", interface, func);
}
}
fn push_imported_type_intrinsics(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
resource: TypeId,
mangling: ManglingAndAbi,
) {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {
let (module, name) = resolve.wasm_import_name(
// Force using a sync ABI here at this time as support for async
// resource drop isn't implemented yet.
mangling.sync(),
WasmImport::ResourceIntrinsic {
interface,
resource,
intrinsic: ResourceIntrinsic::ImportedDrop,
},
);
wat.push_str(&format!("(import {module:?} {name:?} (func (param i32)))"));
if mangling.is_async() {
// TODO: when wit-component supports async resource drop,
// implement it here too.
// let name = format!("[async-lower]{name}");
// wat.push_str(&format!("(import {module:?} {name:?} (func (param i32)))"));
}
}
// No other types with intrinsics at this time (futures/streams are
// relative to where they show up in function types.
_ => {}
}
}
fn push_exported_func_intrinsics(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
func: &Function,
mangling: ManglingAndAbi,
) {
if !mangling.is_async() {
return;
}
// For exported async functions, generate a `task.return` intrinsic.
let (module, name, sig) = func.task_return_import(resolve, interface, mangling.mangling());
wat.push_str(&format!("(import {module:?} {name:?} (func"));
push_tys(wat, "param", &sig.params);
push_tys(wat, "result", &sig.results);
wat.push_str("))\n");
push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func);
}
fn push_imported_future_and_stream_intrinsics(
wat: &mut String,
resolve: &Resolve,
module_prefix: &str,
interface: Option<&WorldKey>,
func: &Function,
) {
let module = match interface {
Some(key) => format!("{module_prefix}{}", resolve.name_world_key(key)),
None => format!("{module_prefix}$root"),
};
let name = &func.name;
for (i, id) in func
.find_futures_and_streams(resolve)
.into_iter()
.enumerate()
{
match &resolve.types[id].kind {
TypeDefKind::Future(_) => {
wat.push_str(&format!(
r#"
(import {module:?} "[future-new-{i}]{name}" (func (result i64)))
(import {module:?} "[future-read-{i}]{name}" (func (param i32 i32) (result i32)))
(import {module:?} "[future-write-{i}]{name}" (func (param i32 i32) (result i32)))
(import {module:?} "[future-cancel-read-{i}]{name}" (func (param i32) (result i32)))
(import {module:?} "[future-cancel-write-{i}]{name}" (func (param i32) (result i32)))
(import {module:?} "[future-drop-readable-{i}]{name}" (func (param i32)))
(import {module:?} "[future-drop-writable-{i}]{name}" (func (param i32)))
(import {module:?} "[async-lower][future-read-{i}]{name}" (func (param i32 i32) (result i32)))
(import {module:?} "[async-lower][future-write-{i}]{name}" (func (param i32 i32) (result i32)))
;; deferred behind 🚝
;;(import {module:?} "[async-lower][future-cancel-read-{i}]{name}" (func (param i32) (result i32)))
;;(import {module:?} "[async-lower][future-cancel-write-{i}]{name}" (func (param i32) (result i32)))
"#
));
}
TypeDefKind::Stream(_) => {
wat.push_str(&format!(
r#"
(import {module:?} "[stream-new-{i}]{name}" (func (result i64)))
(import {module:?} "[stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32)))
(import {module:?} "[stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32)))
(import {module:?} "[stream-cancel-read-{i}]{name}" (func (param i32) (result i32)))
(import {module:?} "[stream-cancel-write-{i}]{name}" (func (param i32) (result i32)))
(import {module:?} "[stream-drop-readable-{i}]{name}" (func (param i32)))
(import {module:?} "[stream-drop-writable-{i}]{name}" (func (param i32)))
(import {module:?} "[async-lower][stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32)))
(import {module:?} "[async-lower][stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32)))
;; deferred behind 🚝
;;(import {module:?} "[async-lower][stream-cancel-read-{i}]{name}" (func (param i32) (result i32)))
;;(import {module:?} "[async-lower][stream-cancel-write-{i}]{name}" (func (param i32) (result i32)))
"#
));
}
_ => unreachable!(),
}
}
}
fn push_exported_type_intrinsics(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
resource: TypeId,
mangling: ManglingAndAbi,
) {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {
let intrinsics = [
(ResourceIntrinsic::ExportedDrop, "(func (param i32))"),
(
ResourceIntrinsic::ExportedNew,
"(func (param i32) (result i32))",
),
(
ResourceIntrinsic::ExportedRep,
"(func (param i32) (result i32))",
),
];
for (intrinsic, sig) in intrinsics {
let (module, name) = resolve.wasm_import_name(
mangling.sync(),
WasmImport::ResourceIntrinsic {
interface,
resource,
intrinsic,
},
);
wat.push_str(&format!("(import {module:?} {name:?} {sig})\n"));
}
}
// No other types with intrinsics at this time (futures/streams
// relative to where they are in a function).
_ => {}
}
}
fn push_exported_resource_functions(
wat: &mut String,
resolve: &Resolve,
interface: &WorldKey,
resource: TypeId,
mangling: ManglingAndAbi,
) {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {}
_ => return,
}
// Feign destructors for any resource that this interface
// exports
let name = resolve.wasm_export_name(
mangling,
WasmExport::ResourceDtor {
interface,
resource,
},
);
wat.push_str(&format!("(func (export {name:?}) (param i32))"));
}
fn push_func_export(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
func: &Function,
mangling: ManglingAndAbi,
) {
let sig = resolve.wasm_signature(mangling.export_variant(), func);
let name = resolve.wasm_export_name(
mangling,
WasmExport::Func {
interface,
func,
kind: WasmExportKind::Normal,
},
);
wat.push_str(&format!("(func (export \"{name}\")"));
push_tys(wat, "param", &sig.params);
push_tys(wat, "result", &sig.results);
wat.push_str(" unreachable)\n");
match mangling {
ManglingAndAbi::Standard32 | ManglingAndAbi::Legacy(LiftLowerAbi::Sync) => {
let name = resolve.wasm_export_name(
mangling,
WasmExport::Func {
interface,
func,
kind: WasmExportKind::PostReturn,
},
);
wat.push_str(&format!("(func (export \"{name}\")"));
push_tys(wat, "param", &sig.results);
wat.push_str(")\n");
}
ManglingAndAbi::Legacy(LiftLowerAbi::AsyncCallback) => {
let name = resolve.wasm_export_name(
mangling,
WasmExport::Func {
interface,
func,
kind: WasmExportKind::Callback,
},
);
wat.push_str(&format!(
"(func (export \"{name}\") (param i32 i32 i32) (result i32) unreachable)\n"
));
}
ManglingAndAbi::Legacy(LiftLowerAbi::AsyncStackful) => {}
}
}
fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) {
if params.is_empty() {
return;
}
dst.push_str(" (");
dst.push_str(desc);
for ty in params {
dst.push(' ');
match ty {
WasmType::I32 => dst.push_str("i32"),
WasmType::I64 => dst.push_str("i64"),
WasmType::F32 => dst.push_str("f32"),
WasmType::F64 => dst.push_str("f64"),
WasmType::Pointer => dst.push_str("i32"),
WasmType::PointerOrI64 => dst.push_str("i64"),
WasmType::Length => dst.push_str("i32"),
}
}
dst.push(')');
}
fn push_root_async_intrinsics(dst: &mut String) {
dst.push_str(
r#"
(import "[export]$root" "[task-cancel]" (func))
(import "$root" "[backpressure-inc]" (func))
(import "$root" "[backpressure-dec]" (func))
(import "$root" "[waitable-set-new]" (func (result i32)))
(import "$root" "[waitable-set-wait]" (func (param i32 i32) (result i32)))
(import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32)))
(import "$root" "[waitable-set-drop]" (func (param i32)))
(import "$root" "[waitable-join]" (func (param i32 i32)))
(import "$root" "[thread-yield]" (func (result i32)))
(import "$root" "[subtask-drop]" (func (param i32)))
(import "$root" "[subtask-cancel]" (func (param i32) (result i32)))
(import "$root" "[context-get-0]" (func (result i32)))
(import "$root" "[context-set-0]" (func (param i32)))
;; deferred behind 🧵 upstream
;;(import "$root" "[cancellable][waitable-set-wait]" (func (param i32 i32) (result i32)))
;;(import "$root" "[cancellable][waitable-set-poll]" (func (param i32 i32) (result i32)))
;;(import "$root" "[cancellable][thread-yield]" (func (result i32)))
;;(import "$root" "[context-get-1]" (func (result i32)))
;;(import "$root" "[context-set-1]" (func (param i32)))
;; deferred behind 📝 upstream
;;(import "$root" "[error-context-new-utf8]" (func (param i32 i32) (result i32)))
;;(import "$root" "[error-context-new-utf16]" (func (param i32 i32) (result i32)))
;;(import "$root" "[error-context-new-latin1+utf16]" (func (param i32 i32) (result i32)))
;;(import "$root" "[error-context-debug-message-utf8]" (func (param i32 i32)))
;;(import "$root" "[error-context-debug-message-utf16]" (func (param i32 i32)))
;;(import "$root" "[error-context-debug-message-latin1+utf16]" (func (param i32 i32)))
;;(import "$root" "[error-context-drop]" (func (param i32)))
"#,
);
}

3231
vendor/wit-component/src/encoding.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,164 @@
//! Support for deduplicating module imports when turning them into a component.
//!
//! Core wasm allows for duplicate imports of the same name/field in a core wasm
//! module. This is not allowed in the component model, however, meaning that
//! such a core wasm module cannot be inserted into a component as-is. This
//! module is tasked with tackling this problem.
//!
//! The general idea of this module is to rewrite imports in-place to a
//! different and unique name. The original name is then recorded in
//! [`ModuleImportMap`] which is then plumbed through to the location that
//! classifies all imports. The classification then uses the original names for
//! what an import should be resolved to but records it under the unique names
//! generated here.
use anyhow::Result;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use wasm_encoder::reencode::{Reencode, RoundtripReencoder};
use wasm_encoder::{ImportSection, RawSection};
use wasmparser::{Parser, Payload::*};
/// A map of current names (possibly new) to original names, if any.
#[derive(Default)]
pub struct ModuleImportMap {
/// A two-level map for what names map to what.
///
/// * The first level of the map is the "module" field, or the first string,
/// in a wasm import.
/// * The second level of the map is the "name" field, or the second string,
/// in a wasm import.
/// * The value of the second level is what this name was originally found
/// under in the original module. `None` means "same as the hash map key"
/// while `Some` means "it's this new name".
///
/// This map is built during `ModuleImportMap::new` and serves double-duty
/// to actually track duplicate imports.
renamed_to_original: HashMap<String, HashMap<String, Option<String>>>,
}
impl ModuleImportMap {
/// Creates a new [`ModuleImportMap`] tracking duplicate imports, if any,
/// from the `wasm` provided.
///
/// Upon success a new wasm binary (possibly the same as the original) is
/// returned which is the new source of truth for this module. If duplicate
/// imports were found then a [`ModuleImportMap`] is returned, otherwise
/// `None` is returned.
///
/// # Errors
///
/// Returns an error if `wasm` could not be parsed as a wasm module.
pub fn new<'a>(wasm: Cow<'a, [u8]>) -> Result<(Cow<'a, [u8]>, Option<ModuleImportMap>)> {
let mut module = wasm_encoder::Module::new();
let mut ret = ModuleImportMap::default();
let mut found_duplicate_imports = false;
for payload in Parser::new(0).parse_all(&wasm) {
let payload = payload?;
match &payload {
Version { encoding, .. } if *encoding == wasmparser::Encoding::Component => {
// if this is a component let someone else deal with the
// error, we'll punt that up the stack.
assert!(!found_duplicate_imports);
break;
}
// This is the section we're interested in. Go over each import
// and delegate to `ModuleImportMap::push_import` for figuring
// out what to do with this import. At the end put the new
// import section in the `module` we're building.
ImportSection(i) => {
let mut new_import_section = ImportSection::new();
for import in i.clone().into_imports() {
found_duplicate_imports = ret
.push_import(&mut new_import_section, import?)?
|| found_duplicate_imports;
}
module.section(&new_import_section);
}
// All other sections get plumbed through as-is. This ensures we
// don't tamper with binary offsets anywhere in the module
// except the import section, for example.
_ => {
if let Some((id, range)) = payload.as_section() {
module.section(&RawSection {
id,
data: &wasm[range],
});
}
}
}
}
if found_duplicate_imports {
Ok((module.finish().into(), Some(ret)))
} else {
Ok((wasm, None))
}
}
/// Returns `Ok(true)` if this is a duplicate import, or `Ok(false)` if it's
/// a unique import for the first time.
fn push_import(
&mut self,
new_import_section: &mut ImportSection,
import: wasmparser::Import<'_>,
) -> Result<bool> {
let module_map = self
.renamed_to_original
.entry(import.module.to_string())
.or_insert(HashMap::new());
// If this import hasn't yet been seen, then great! Record that it is
// using its original name and encode it with its original name.
if !module_map.contains_key(import.name) {
let prev = module_map.insert(import.name.to_string(), None);
assert!(prev.is_none());
RoundtripReencoder.parse_import(new_import_section, import)?;
return Ok(false);
}
// FIXME: this is technically O(n^2), but it's also only applicable when
// a module has lots of imports, and surely that won't happen often...
// right? ... right?
//
// If this isn't fixed by the time someone reads this and is angry about
// O(n^2), sorry.
//
// Otherwise the purpose of this loop is to determine a unique name for
// the new import, something that hasn't previously been seen before.
let mut new_name = import.name.to_string();
for i in 2.. {
new_name.truncate(import.name.len());
write!(new_name, " [v{i}]").unwrap();
if !module_map.contains_key(&new_name) {
break;
}
}
// Now that `new_name` is unique, record the import in the new import
// section.
let mut new_import = import;
new_import.name = &new_name;
RoundtripReencoder.parse_import(new_import_section, new_import)?;
// Also record that `new_name` was originally known as `import.name`
// for later lookup in `original_names` below.
let prev = module_map.insert(new_name, Some(import.name.to_string()));
assert!(prev.is_none());
Ok(true)
}
/// Returns the original `name` that `import` should use, if any.
///
/// If `None` is returned then `import.name` should be used.
pub fn original_name(&self, import: &wasmparser::Import<'_>) -> Option<&str> {
self.renamed_to_original
.get(import.module)?
.get(import.name)?
.as_deref()
}
}

View File

@@ -0,0 +1,510 @@
use super::EncodingState;
use anyhow::Result;
use std::collections::HashMap;
use wasm_encoder::*;
use wit_parser::{
Enum, Flags, Function, Handle, InterfaceId, Record, Resolve, Result_, Tuple, Type, TypeDefKind,
TypeId, TypeOwner, Variant,
};
/// Represents a key type for interface function definitions.
#[derive(Hash, PartialEq, Eq, Clone)]
pub struct FunctionKey<'a> {
async_: bool,
params: &'a [(String, Type)],
result: &'a Option<Type>,
}
#[derive(Default, Clone)]
pub struct TypeEncodingMaps<'a> {
pub id_to_index: HashMap<TypeId, u32>,
pub def_to_index: HashMap<&'a TypeDefKind, u32>,
pub func_type_map: HashMap<FunctionKey<'a>, u32>,
pub unit_future: Option<u32>,
pub unit_stream: Option<u32>,
}
impl<'a> TypeEncodingMaps<'a> {
/// Returns the index used to encode `id`, if it has been previously
/// encoded.
fn lookup(&self, resolve: &'a Resolve, id: TypeId) -> Option<u32> {
// If this exact id has been encoded before, use that.
if let Some(index) = self.id_to_index.get(&id) {
return Some(*index);
}
// If this is an anonymous type, or one where the name of the type is
// `none`, then consult the `def_to_index` map as well.
let ty = &resolve.types[id];
if ty.name.is_none() {
if let Some(index) = self.def_to_index.get(&ty.kind) {
return Some(*index);
}
}
// Failing all that this hasn't previously been encoded.
None
}
/// Inserts that `id` was encoded as `index`.
fn insert(&mut self, resolve: &'a Resolve, id: TypeId, index: u32) {
// Always record the id=>index mapping.
self.id_to_index.insert(id, index);
// Conditionally record the kind=>index mapping if the type is anonymous
// and thus a candidate to use.
let ty = &resolve.types[id];
if ty.name.is_none() {
self.def_to_index.insert(&ty.kind, index);
}
}
}
/// Support for encoding a wit-parser type into a component.
///
/// This is a `trait` to enable different implementations which define types
/// slightly differently in different contexts. For example types might be
/// defined within an instance type's index space or might be defined in the
/// component's root index space in a type section. The default trait methods
/// here are intended to assist in multiplexing over this difference.
pub trait ValtypeEncoder<'a> {
/// Returns a new type encoder used to define a new type in this type
/// section.
///
/// The `u32` returned is the index of the type being defined in this type
/// index space and the encoder returned must be used to define a type.
fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>);
/// Returns the index of a new function type and the encoder of where to
/// place its results.
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>);
/// Creates an export item for the specified type index.
fn export_type(&mut self, index: u32, name: &'a str) -> Option<u32>;
/// Creates a new `(type (sub resource))` export with the given name,
/// returning the type index that refers to the fresh type created.
fn export_resource(&mut self, name: &'a str) -> u32;
/// Returns the encoding maps used to encoding types such as id-to-index
/// maps.
fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a>;
/// Imports `id` from a different interface, returning the index of the
/// imported type into this index space.
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32;
/// Returns the identifier of the interface that generation is for.
fn interface(&self) -> Option<InterfaceId>;
/// Encodes a new function type which is defined within the provided
/// document.
fn encode_func_type(&mut self, resolve: &'a Resolve, func: &'a Function) -> Result<u32> {
let key = FunctionKey {
async_: func.kind.is_async(),
params: &func.params,
result: &func.result,
};
if let Some(index) = self.type_encoding_maps().func_type_map.get(&key) {
return Ok(*index);
}
// Encode all referenced parameter types from this function.
let params: Vec<_> = self.encode_params(resolve, &func.params)?;
let result = func
.result
.map(|ty| self.encode_valtype(resolve, &ty))
.transpose()?;
// Encode the function type
let (index, mut f) = self.define_function_type();
f.async_(func.kind.is_async()).params(params).result(result);
let prev = self.type_encoding_maps().func_type_map.insert(key, index);
assert!(prev.is_none());
Ok(index)
}
fn encode_params(
&mut self,
resolve: &'a Resolve,
params: &'a [(String, Type)],
) -> Result<Vec<(&'a str, ComponentValType)>> {
params
.iter()
.map(|(name, ty)| Ok((name.as_str(), self.encode_valtype(resolve, ty)?)))
.collect::<Result<_>>()
}
/// Encodes the `ty`, defined within `resolve`, into this encoder and returns
/// the corresponding `ComponentValType` that it represents.
///
/// This will recursively define the entire structure of `ty` within `self`
/// if necessary.
fn encode_valtype(&mut self, resolve: &'a Resolve, ty: &Type) -> Result<ComponentValType> {
Ok(match *ty {
Type::Bool => ComponentValType::Primitive(PrimitiveValType::Bool),
Type::U8 => ComponentValType::Primitive(PrimitiveValType::U8),
Type::U16 => ComponentValType::Primitive(PrimitiveValType::U16),
Type::U32 => ComponentValType::Primitive(PrimitiveValType::U32),
Type::U64 => ComponentValType::Primitive(PrimitiveValType::U64),
Type::S8 => ComponentValType::Primitive(PrimitiveValType::S8),
Type::S16 => ComponentValType::Primitive(PrimitiveValType::S16),
Type::S32 => ComponentValType::Primitive(PrimitiveValType::S32),
Type::S64 => ComponentValType::Primitive(PrimitiveValType::S64),
Type::F32 => ComponentValType::Primitive(PrimitiveValType::F32),
Type::F64 => ComponentValType::Primitive(PrimitiveValType::F64),
Type::Char => ComponentValType::Primitive(PrimitiveValType::Char),
Type::String => ComponentValType::Primitive(PrimitiveValType::String),
Type::ErrorContext => ComponentValType::Primitive(PrimitiveValType::ErrorContext),
Type::Id(id) => {
// If this id has already been prior defined into this section
// refer to that definition.
if let Some(index) = self.type_encoding_maps().lookup(resolve, id) {
return Ok(ComponentValType::Type(index));
}
let ty = &resolve.types[id];
// If this type is imported from another interface then return
// it as it was bound here with an alias.
log::trace!("encode type name={:?} {:?}", ty.name, &ty.kind);
if let Some(index) = self.maybe_import_type(resolve, id) {
self.type_encoding_maps().insert(resolve, id, index);
return Ok(ComponentValType::Type(index));
}
// ... and failing all that insert the type export.
let mut encoded = match &ty.kind {
TypeDefKind::Record(r) => self.encode_record(resolve, r)?,
TypeDefKind::Tuple(t) => self.encode_tuple(resolve, t)?,
TypeDefKind::Flags(r) => self.encode_flags(r)?,
TypeDefKind::Variant(v) => self.encode_variant(resolve, v)?,
TypeDefKind::Option(t) => self.encode_option(resolve, t)?,
TypeDefKind::Result(r) => self.encode_result(resolve, r)?,
TypeDefKind::Enum(e) => self.encode_enum(e)?,
TypeDefKind::List(ty) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.list(ty);
ComponentValType::Type(index)
}
TypeDefKind::Map(key_ty, value_ty) => {
let key = self.encode_valtype(resolve, key_ty)?;
let value = self.encode_valtype(resolve, value_ty)?;
let (index, encoder) = self.defined_type();
encoder.map(key, value);
ComponentValType::Type(index)
}
TypeDefKind::FixedSizeList(ty, elements) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.fixed_size_list(ty, *elements);
ComponentValType::Type(index)
}
TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
TypeDefKind::Unknown => unreachable!(),
TypeDefKind::Resource => {
let name = ty.name.as_ref().expect("resources must be named");
let index = self.export_resource(name);
self.type_encoding_maps().id_to_index.insert(id, index);
return Ok(ComponentValType::Type(index));
}
TypeDefKind::Handle(Handle::Own(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.own(ty);
ComponentValType::Type(index)
}
TypeDefKind::Handle(Handle::Borrow(id)) => {
let ty = match self.encode_valtype(resolve, &Type::Id(*id))? {
ComponentValType::Type(index) => index,
_ => panic!("must be an indexed type"),
};
let (index, encoder) = self.defined_type();
encoder.borrow(ty);
ComponentValType::Type(index)
}
};
if let Some(name) = &ty.name {
let index = match encoded {
ComponentValType::Type(index) => index,
ComponentValType::Primitive(ty) => {
// Named primitive types need entries in the type
// section, so convert this to a type reference
let (index, encoder) = self.defined_type();
encoder.primitive(ty);
index
}
};
let index = self.export_type(index, name).unwrap_or(index);
encoded = ComponentValType::Type(index);
}
if let ComponentValType::Type(index) = encoded {
self.type_encoding_maps().insert(resolve, id, index);
}
encoded
}
})
}
/// Optionally imports `id` from a different interface, returning the index
/// of the imported type into this index space.
///
/// Returns `None` if `id` can't be imported.
fn maybe_import_type(&mut self, resolve: &Resolve, id: TypeId) -> Option<u32> {
let ty = &resolve.types[id];
let owner = match ty.owner {
TypeOwner::Interface(i) => i,
_ => return None,
};
if Some(owner) == self.interface() {
return None;
}
Some(self.import_type(owner, id))
}
fn encode_optional_valtype(
&mut self,
resolve: &'a Resolve,
ty: Option<&Type>,
) -> Result<Option<ComponentValType>> {
match ty {
Some(ty) => self.encode_valtype(resolve, ty).map(Some),
None => Ok(None),
}
}
fn encode_record(&mut self, resolve: &'a Resolve, record: &Record) -> Result<ComponentValType> {
let fields = record
.fields
.iter()
.map(|f| Ok((f.name.as_str(), self.encode_valtype(resolve, &f.ty)?)))
.collect::<Result<Vec<_>>>()?;
let (index, encoder) = self.defined_type();
encoder.record(fields);
Ok(ComponentValType::Type(index))
}
fn encode_tuple(&mut self, resolve: &'a Resolve, tuple: &Tuple) -> Result<ComponentValType> {
let tys = tuple
.types
.iter()
.map(|ty| self.encode_valtype(resolve, ty))
.collect::<Result<Vec<_>>>()?;
let (index, encoder) = self.defined_type();
encoder.tuple(tys);
Ok(ComponentValType::Type(index))
}
fn encode_flags(&mut self, flags: &Flags) -> Result<ComponentValType> {
let (index, encoder) = self.defined_type();
encoder.flags(flags.flags.iter().map(|f| f.name.as_str()));
Ok(ComponentValType::Type(index))
}
fn encode_variant(
&mut self,
resolve: &'a Resolve,
variant: &Variant,
) -> Result<ComponentValType> {
let cases = variant
.cases
.iter()
.map(|c| {
Ok((
c.name.as_str(),
self.encode_optional_valtype(resolve, c.ty.as_ref())?,
None, // TODO: support defaulting case values in the future
))
})
.collect::<Result<Vec<_>>>()?;
let (index, encoder) = self.defined_type();
encoder.variant(cases);
Ok(ComponentValType::Type(index))
}
fn encode_option(&mut self, resolve: &'a Resolve, payload: &Type) -> Result<ComponentValType> {
let ty = self.encode_valtype(resolve, payload)?;
let (index, encoder) = self.defined_type();
encoder.option(ty);
Ok(ComponentValType::Type(index))
}
fn encode_result(
&mut self,
resolve: &'a Resolve,
result: &Result_,
) -> Result<ComponentValType> {
let ok = self.encode_optional_valtype(resolve, result.ok.as_ref())?;
let error = self.encode_optional_valtype(resolve, result.err.as_ref())?;
let (index, encoder) = self.defined_type();
encoder.result(ok, error);
Ok(ComponentValType::Type(index))
}
fn encode_enum(&mut self, enum_: &Enum) -> Result<ComponentValType> {
let (index, encoder) = self.defined_type();
encoder.enum_type(enum_.cases.iter().map(|c| c.name.as_str()));
Ok(ComponentValType::Type(index))
}
fn encode_future(
&mut self,
resolve: &'a Resolve,
payload: &Option<Type>,
) -> Result<ComponentValType> {
let ty = self.encode_optional_valtype(resolve, payload.as_ref())?;
let (index, encoder) = self.defined_type();
encoder.future(ty);
Ok(ComponentValType::Type(index))
}
fn encode_stream(
&mut self,
resolve: &'a Resolve,
payload: &Option<Type>,
) -> Result<ComponentValType> {
let ty = self.encode_optional_valtype(resolve, payload.as_ref())?;
let (index, encoder) = self.defined_type();
encoder.stream(ty);
Ok(ComponentValType::Type(index))
}
fn encode_unit_future(&mut self) -> u32 {
if let Some(index) = self.type_encoding_maps().unit_future {
return index;
}
let (index, encoder) = self.defined_type();
encoder.future(None);
self.type_encoding_maps().unit_future = Some(index);
index
}
fn encode_unit_stream(&mut self) -> u32 {
if let Some(index) = self.type_encoding_maps().unit_stream {
return index;
}
let (index, encoder) = self.defined_type();
encoder.stream(None);
self.type_encoding_maps().unit_stream = Some(index);
index
}
}
pub struct RootTypeEncoder<'state, 'a> {
pub state: &'state mut EncodingState<'a>,
pub interface: Option<InterfaceId>,
pub import_types: bool,
}
impl<'a> ValtypeEncoder<'a> for RootTypeEncoder<'_, 'a> {
fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>) {
self.state.component.type_defined(None)
}
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) {
self.state.component.type_function(None)
}
fn interface(&self) -> Option<InterfaceId> {
self.interface
}
fn export_type(&mut self, idx: u32, name: &'a str) -> Option<u32> {
// When encoding types for the root the root component will export
// this type, but when encoding types for a targeted interface then we
// can't export types just yet. Interfaces will be created as an
// instance with a bag-of-exports construction which can't refer to its
// own types.
if self.interface.is_none() {
Some(if self.import_types {
self.state
.component
.import(name, ComponentTypeRef::Type(TypeBounds::Eq(idx)))
} else {
self.state
.component
.export(name, ComponentExportKind::Type, idx, None)
})
} else {
assert!(!self.import_types);
None
}
}
fn export_resource(&mut self, name: &'a str) -> u32 {
assert!(self.interface.is_none());
assert!(self.import_types);
self.state
.component
.import(name, ComponentTypeRef::Type(TypeBounds::SubResource))
}
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
if !self.import_types {
if let Some(cur) = self.interface {
let set = &self.state.info.exports_used[&cur];
if set.contains(&interface) {
return self.state.alias_exported_type(interface, id);
}
}
}
self.state.alias_imported_type(interface, id)
}
fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a> {
if self.import_types {
&mut self.state.import_type_encoding_maps
} else {
&mut self.state.export_type_encoding_maps
}
}
}
pub struct InstanceTypeEncoder<'state, 'a> {
pub state: &'state mut EncodingState<'a>,
pub interface: InterfaceId,
pub type_encoding_maps: TypeEncodingMaps<'a>,
pub ty: InstanceType,
}
impl<'a> ValtypeEncoder<'a> for InstanceTypeEncoder<'_, 'a> {
fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>) {
(self.ty.type_count(), self.ty.ty().defined_type())
}
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) {
(self.ty.type_count(), self.ty.ty().function())
}
fn export_type(&mut self, idx: u32, name: &str) -> Option<u32> {
let ret = self.ty.type_count();
self.ty
.export(name, ComponentTypeRef::Type(TypeBounds::Eq(idx)));
Some(ret)
}
fn export_resource(&mut self, name: &str) -> u32 {
let ret = self.ty.type_count();
self.ty
.export(name, ComponentTypeRef::Type(TypeBounds::SubResource));
ret
}
fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a> {
&mut self.type_encoding_maps
}
fn interface(&self) -> Option<InterfaceId> {
Some(self.interface)
}
fn import_type(&mut self, interface: InterfaceId, id: TypeId) -> u32 {
self.ty.alias(Alias::Outer {
count: 1,
index: self.state.alias_imported_type(interface, id),
kind: ComponentOuterAliasKind::Type,
});
self.ty.type_count() - 1
}
}

393
vendor/wit-component/src/encoding/wit.rs vendored Normal file
View File

@@ -0,0 +1,393 @@
use crate::encoding::types::{TypeEncodingMaps, ValtypeEncoder};
use anyhow::Result;
use indexmap::IndexSet;
use std::collections::HashMap;
use std::mem;
use wasm_encoder::*;
use wit_parser::*;
/// Encodes the given `package` within `resolve` to a binary WebAssembly
/// representation.
///
/// This function is the root of the implementation of serializing a WIT package
/// into a WebAssembly representation. The wasm representation serves two
/// purposes:
///
/// * One is to be a binary encoding of a WIT document which is ideally more
/// stable than the WIT textual format itself.
/// * Another is to provide a clear mapping of all WIT features into the
/// component model through use of its binary representation.
///
/// The `resolve` provided is a set of packages and types and such and the
/// `package` argument is an ID within the world provided. The documents within
/// `package` will all be encoded into the binary returned.
///
/// The binary returned can be [`decode`d](crate::decode) to recover the WIT
/// package provided.
pub fn encode(resolve: &Resolve, package: PackageId) -> Result<Vec<u8>> {
let mut component = encode_component(resolve, package)?;
component.raw_custom_section(&crate::base_producers().raw_custom_section());
Ok(component.finish())
}
/// Encodes the given `package` within `resolve` to a binary WebAssembly
/// representation.
///
/// This function is the root of the implementation of serializing a WIT package
/// into a WebAssembly representation. The wasm representation serves two
/// purposes:
///
/// * One is to be a binary encoding of a WIT document which is ideally more
/// stable than the WIT textual format itself.
/// * Another is to provide a clear mapping of all WIT features into the
/// component model through use of its binary representation.
///
/// The `resolve` provided is a set of packages and types and such and the
/// `package` argument is an ID within the world provided. The documents within
/// `package` will all be encoded into the binary returned.
///
/// The binary returned can be [`decode`d](crate::decode) to recover the WIT
/// package provided.
pub fn encode_component(resolve: &Resolve, package: PackageId) -> Result<ComponentBuilder> {
let mut encoder = Encoder {
component: ComponentBuilder::default(),
resolve,
package,
};
encoder.run()?;
let package_metadata = PackageMetadata::extract(resolve, package);
encoder.component.custom_section(&CustomSection {
name: PackageMetadata::SECTION_NAME.into(),
data: package_metadata.encode()?.into(),
});
Ok(encoder.component)
}
/// Encodes a `world` as a component type.
pub fn encode_world(resolve: &Resolve, world_id: WorldId) -> Result<ComponentType> {
let mut component = InterfaceEncoder::new(resolve);
let world = &resolve.worlds[world_id];
log::trace!("encoding world {}", world.name);
// Encode the imports
for (name, import) in world.imports.iter() {
let name = resolve.name_world_key(name);
log::trace!("encoding import {name}");
let ty = match import {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(t) => {
component.interface = None;
component.import_types = true;
component.encode_valtype(resolve, &Type::Id(*t))?;
component.import_types = false;
continue;
}
};
component.outer.import(&name, ty);
}
// Encode the exports
for (name, export) in world.exports.iter() {
let name = resolve.name_world_key(name);
log::trace!("encoding export {name}");
let ty = match export {
WorldItem::Interface { id, .. } => {
component.interface = Some(*id);
let idx = component.encode_instance(*id)?;
ComponentTypeRef::Instance(idx)
}
WorldItem::Function(f) => {
component.interface = None;
let idx = component.encode_func_type(resolve, f)?;
ComponentTypeRef::Func(idx)
}
WorldItem::Type(_) => unreachable!(),
};
component.outer.export(&name, ty);
}
Ok(component.outer)
}
struct Encoder<'a> {
component: ComponentBuilder,
resolve: &'a Resolve,
package: PackageId,
}
impl Encoder<'_> {
fn run(&mut self) -> Result<()> {
// Encode all interfaces as component types and then export them.
for (name, &id) in self.resolve.packages[self.package].interfaces.iter() {
let component_ty = self.encode_interface(id)?;
let ty = self.component.type_component(Some(name), &component_ty);
self.component
.export(name.as_ref(), ComponentExportKind::Type, ty, None);
}
// For each `world` encode it directly as a component and then create a
// wrapper component that exports that component.
for (name, &world) in self.resolve.packages[self.package].worlds.iter() {
let component_ty = encode_world(self.resolve, world)?;
let world = &self.resolve.worlds[world];
let mut wrapper = ComponentType::new();
wrapper.ty().component(&component_ty);
let pkg = &self.resolve.packages[world.package.unwrap()];
wrapper.export(&pkg.name.interface_id(name), ComponentTypeRef::Component(0));
let ty = self.component.type_component(Some(name), &wrapper);
self.component
.export(name.as_ref(), ComponentExportKind::Type, ty, None);
}
Ok(())
}
fn encode_interface(&mut self, id: InterfaceId) -> Result<ComponentType> {
// Build a set of interfaces reachable from this document, including the
// interfaces in the document itself. This is used to import instances
// into the component type we're encoding. Note that entire interfaces
// are imported with all their types as opposed to just the needed types
// in an interface for this document. That's done to assist with the
// decoding process where everyone's view of a foreign document agrees
// notably on the order that types are defined in to assist with
// roundtripping.
let mut interfaces = IndexSet::new();
self.add_live_interfaces(&mut interfaces, id);
// Seed the set of used names with all exported interfaces to ensure
// that imported interfaces choose different names as the import names
// aren't used during decoding.
let mut used_names = IndexSet::new();
for id in interfaces.iter() {
let iface = &self.resolve.interfaces[*id];
if iface.package == Some(self.package) {
let first = used_names.insert(iface.name.as_ref().unwrap().clone());
assert!(first);
}
}
let mut encoder = InterfaceEncoder::new(self.resolve);
for interface in interfaces {
encoder.interface = Some(interface);
let iface = &self.resolve.interfaces[interface];
let name = self.resolve.id_of(interface).unwrap();
if interface == id {
let idx = encoder.encode_instance(interface)?;
log::trace!("exporting self as {idx}");
encoder.outer.export(&name, ComponentTypeRef::Instance(idx));
} else {
encoder.push_instance();
for (_, id) in iface.types.iter() {
encoder.encode_valtype(self.resolve, &Type::Id(*id))?;
}
let instance = encoder.pop_instance();
let idx = encoder.outer.type_count();
encoder.outer.ty().instance(&instance);
encoder.import_map.insert(interface, encoder.instances);
encoder.instances += 1;
encoder.outer.import(&name, ComponentTypeRef::Instance(idx));
}
}
encoder.interface = None;
Ok(encoder.outer)
}
/// Recursively add all live interfaces reachable from `id` into the
/// `interfaces` set, and then add `id` to the set.
fn add_live_interfaces(&self, interfaces: &mut IndexSet<InterfaceId>, id: InterfaceId) {
if interfaces.contains(&id) {
return;
}
for id in self.resolve.interface_direct_deps(id) {
self.add_live_interfaces(interfaces, id);
}
assert!(interfaces.insert(id));
}
}
struct InterfaceEncoder<'a> {
resolve: &'a Resolve,
outer: ComponentType,
ty: Option<InstanceType>,
type_encoding_maps: TypeEncodingMaps<'a>,
saved_maps: Option<TypeEncodingMaps<'a>>,
import_map: HashMap<InterfaceId, u32>,
outer_type_map: HashMap<TypeId, u32>,
instances: u32,
import_types: bool,
interface: Option<InterfaceId>,
}
impl InterfaceEncoder<'_> {
fn new(resolve: &Resolve) -> InterfaceEncoder<'_> {
InterfaceEncoder {
resolve,
outer: ComponentType::new(),
ty: None,
type_encoding_maps: Default::default(),
import_map: Default::default(),
outer_type_map: Default::default(),
instances: 0,
saved_maps: None,
import_types: false,
interface: None,
}
}
fn encode_instance(&mut self, interface: InterfaceId) -> Result<u32> {
self.push_instance();
let iface = &self.resolve.interfaces[interface];
let mut type_order = IndexSet::new();
for (_, id) in iface.types.iter() {
self.encode_valtype(self.resolve, &Type::Id(*id))?;
type_order.insert(*id);
}
// Sort functions based on whether or not they're associated with
// resources.
//
// This is done here to ensure that when a WIT package is printed as WIT
// then decoded, or if it's printed as Wasm then decoded, the final
// result is the same. When printing via WIT resource methods are
// attached to the resource types themselves meaning that they'll appear
// intermingled with the rest of the types, namely first before all
// other functions. The purpose of this sort is to perform a stable sort
// over all functions by shuffling the resource-related functions first,
// in order of when their associated resource was encoded, and putting
// freestanding functions last.
//
// Note that this is not actually required for correctness, it's
// basically here to make fuzzing happy.
let mut funcs = iface.functions.iter().collect::<Vec<_>>();
funcs.sort_by_key(|(_name, func)| match func.kind.resource() {
Some(id) => type_order.get_index_of(&id).unwrap(),
None => type_order.len(),
});
for (name, func) in funcs {
let ty = self.encode_func_type(self.resolve, func)?;
self.ty
.as_mut()
.unwrap()
.export(name, ComponentTypeRef::Func(ty));
}
let instance = self.pop_instance();
let idx = self.outer.type_count();
self.outer.ty().instance(&instance);
self.import_map.insert(interface, self.instances);
self.instances += 1;
Ok(idx)
}
fn push_instance(&mut self) {
assert!(self.ty.is_none());
assert!(self.saved_maps.is_none());
self.saved_maps = Some(mem::take(&mut self.type_encoding_maps));
self.ty = Some(InstanceType::default());
}
fn pop_instance(&mut self) -> InstanceType {
let maps = self.saved_maps.take().unwrap();
self.type_encoding_maps = maps;
mem::take(&mut self.ty).unwrap()
}
}
impl<'a> ValtypeEncoder<'a> for InterfaceEncoder<'a> {
fn defined_type(&mut self) -> (u32, ComponentDefinedTypeEncoder<'_>) {
match &mut self.ty {
Some(ty) => (ty.type_count(), ty.ty().defined_type()),
None => (self.outer.type_count(), self.outer.ty().defined_type()),
}
}
fn define_function_type(&mut self) -> (u32, ComponentFuncTypeEncoder<'_>) {
match &mut self.ty {
Some(ty) => (ty.type_count(), ty.ty().function()),
None => (self.outer.type_count(), self.outer.ty().function()),
}
}
fn export_type(&mut self, index: u32, name: &'a str) -> Option<u32> {
match &mut self.ty {
Some(ty) => {
assert!(!self.import_types);
let ret = ty.type_count();
ty.export(name, ComponentTypeRef::Type(TypeBounds::Eq(index)));
Some(ret)
}
None => {
let ret = self.outer.type_count();
if self.import_types {
self.outer
.import(name, ComponentTypeRef::Type(TypeBounds::Eq(index)));
} else {
self.outer
.export(name, ComponentTypeRef::Type(TypeBounds::Eq(index)));
}
Some(ret)
}
}
}
fn export_resource(&mut self, name: &'a str) -> u32 {
let type_ref = ComponentTypeRef::Type(TypeBounds::SubResource);
match &mut self.ty {
Some(ty) => {
assert!(!self.import_types);
ty.export(name, type_ref);
ty.type_count() - 1
}
None => {
if self.import_types {
self.outer.import(name, type_ref);
} else {
self.outer.export(name, type_ref);
}
self.outer.type_count() - 1
}
}
}
fn type_encoding_maps(&mut self) -> &mut TypeEncodingMaps<'a> {
&mut self.type_encoding_maps
}
fn interface(&self) -> Option<InterfaceId> {
self.interface
}
fn import_type(&mut self, owner: InterfaceId, id: TypeId) -> u32 {
let ty = &self.resolve.types[id];
let instance = self.import_map[&owner];
let outer_idx = *self.outer_type_map.entry(id).or_insert_with(|| {
let ret = self.outer.type_count();
self.outer.alias(Alias::InstanceExport {
instance,
name: ty.name.as_ref().unwrap(),
kind: ComponentExportKind::Type,
});
ret
});
match &mut self.ty {
Some(ty) => {
let ret = ty.type_count();
ty.alias(Alias::Outer {
count: 1,
index: outer_idx,
kind: ComponentOuterAliasKind::Type,
});
ret
}
None => outer_idx,
}
}
}

View File

@@ -0,0 +1,525 @@
use super::{Adapter, ComponentEncoder, LibraryInfo, RequiredOptions};
use crate::validation::{
Import, ImportMap, PayloadType, ValidatedModule, validate_adapter_module, validate_module,
};
use anyhow::{Context, Result};
use indexmap::{IndexMap, IndexSet};
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use wit_parser::{
Function, InterfaceId, LiveTypes, Resolve, TypeDefKind, TypeId, TypeOwner, WorldId, WorldItem,
WorldKey,
abi::{AbiVariant, WasmSignature},
};
pub struct WorldAdapter<'a> {
pub wasm: Cow<'a, [u8]>,
pub info: ValidatedModule,
pub library_info: Option<&'a LibraryInfo>,
}
/// Metadata discovered from the state configured in a `ComponentEncoder`.
///
/// This is stored separately from `EncodingState` to be stored as a borrow in
/// `EncodingState` as this information doesn't change throughout the encoding
/// process.
pub struct ComponentWorld<'a> {
/// Encoder configuration with modules, the document ,etc.
pub encoder: &'a ComponentEncoder,
/// Validation information of the input module, or `None` in `--types-only`
/// mode.
pub info: ValidatedModule,
/// Validation information about adapters populated only for required
/// adapters. Additionally stores the gc'd wasm for each adapter.
pub adapters: IndexMap<&'a str, WorldAdapter<'a>>,
/// Map of all imports and descriptions of what they're importing.
pub import_map: IndexMap<Option<String>, ImportedInterface>,
/// Set of all live types which must be exported either because they're
/// directly used or because they're transitively used.
pub live_type_imports: IndexMap<InterfaceId, IndexSet<TypeId>>,
/// For each exported interface in the desired world this map lists
/// the set of interfaces that it depends on which are also exported.
///
/// This set is used to determine when types are imported/used whether they
/// come from imports or exports.
pub exports_used: HashMap<InterfaceId, HashSet<InterfaceId>>,
}
#[derive(Debug)]
pub struct ImportedInterface {
pub lowerings: IndexMap<(String, AbiVariant), Lowering>,
pub interface: Option<InterfaceId>,
}
#[derive(Debug)]
pub enum Lowering {
Direct,
Indirect {
sig: WasmSignature,
options: RequiredOptions,
},
ResourceDrop(TypeId),
}
impl<'a> ComponentWorld<'a> {
pub fn new(encoder: &'a ComponentEncoder) -> Result<Self> {
let info = validate_module(encoder, &encoder.module, encoder.module_import_map.as_ref())
.context("module was not valid")?;
let mut ret = ComponentWorld {
encoder,
info,
adapters: IndexMap::new(),
import_map: IndexMap::new(),
live_type_imports: Default::default(),
exports_used: HashMap::new(),
};
ret.process_adapters()?;
ret.process_imports()?;
ret.process_exports_used();
ret.process_live_type_imports();
Ok(ret)
}
/// Process adapters which are required here. Iterate over all
/// adapters and figure out what functions are required from the
/// adapter itself, either because the functions are imported by the
/// main module or they're part of the adapter's exports.
fn process_adapters(&mut self) -> Result<()> {
let resolve = &self.encoder.metadata.resolve;
let world = self.encoder.metadata.world;
for (
name,
Adapter {
wasm,
metadata: _,
required_exports,
library_info,
},
) in self.encoder.adapters.iter()
{
let required_by_import = self.info.imports.required_from_adapter(name.as_str());
let no_required_by_import = || required_by_import.is_empty();
let no_required_exports = || {
required_exports
.iter()
.all(|name| match &resolve.worlds[world].exports[name] {
WorldItem::Function(_) => false,
WorldItem::Interface { id, .. } => {
resolve.interfaces[*id].functions.is_empty()
}
WorldItem::Type(_) => true,
})
};
if no_required_by_import() && no_required_exports() && library_info.is_none() {
continue;
}
let wasm = if library_info.is_some() {
Cow::Borrowed(wasm as &[u8])
} else {
// Without `library_info` this means that this is an adapter.
// The goal of the adapter is to provide a suite of symbols that
// can be imported, but not all symbols may be imported. Here
// the module is trimmed down to only what's needed by the
// original main module.
//
// The main module requires `required_by_import` above, but
// adapters may themselves also export WIT items. To handle this
// the sequence of operations here are:
//
// 1. First the adapter is validated as-is. This ensures that
// everything looks good before GC.
// 2. The metadata from step (1) is used to determine the set of
// WIT-level exports that are needed. This includes things
// like realloc functions and such.
// 3. The set of WIT-level functions from (2) is unioned with
// `required_by_import` to create the set of required exports
// of the adapter.
// 4. This set of exports is used to delete some exports of the
// adapter and then perform a GC pass.
//
// Finally at the end of all of this the
// `validate_adapter_module` method is called for a second time
// on the minimized adapter. This is done because deleting
// imports may have deleted some imports which means that the
// final component may not need to import as many interfaces.
let info = validate_adapter_module(
self.encoder,
&wasm,
&required_by_import,
required_exports,
library_info.as_ref(),
)
.with_context(|| {
format!("failed to validate the imports of the adapter module `{name}`")
})?;
let mut required = IndexSet::new();
for (name, _ty) in required_by_import.iter() {
required.insert(name.to_string());
}
for (name, _export) in info.exports.iter() {
required.insert(name.to_string());
}
Cow::Owned(
crate::gc::run(
wasm,
&required,
if self.encoder.realloc_via_memory_grow {
None
} else {
self.info.exports.realloc_to_import_into_adapter()
},
)
.context("failed to reduce input adapter module to its minimal size")?,
)
};
let info = validate_adapter_module(
self.encoder,
&wasm,
&required_by_import,
required_exports,
library_info.as_ref(),
)
.with_context(|| {
format!("failed to validate the imports of the minimized adapter module `{name}`")
})?;
self.adapters.insert(
name,
WorldAdapter {
info,
wasm,
library_info: library_info.as_ref(),
},
);
}
Ok(())
}
/// Fills out the `import_map` field of `self` by determining the live
/// functions from all imports. This additionally classifies imported
/// functions into direct or indirect lowerings for managing shims.
fn process_imports(&mut self) -> Result<()> {
let resolve = &self.encoder.metadata.resolve;
let world = self.encoder.metadata.world;
// Inspect all imports of the main module and adapters to find all
// WIT-looking things and register those as required. This is used to
// prune out unneeded things in the `add_item` function below.
let mut required = Required::default();
for (_, _, import) in self
.adapters
.values()
.flat_map(|a| a.info.imports.imports())
.chain(self.info.imports.imports())
{
match import {
Import::WorldFunc(_, name, abi) => {
required
.interface_funcs
.entry(None)
.or_default()
.insert((name, *abi));
}
Import::InterfaceFunc(_, id, name, abi) => {
required
.interface_funcs
.entry(Some(*id))
.or_default()
.insert((name, *abi));
}
Import::ImportedResourceDrop(_, _, id) => {
required.resource_drops.insert(*id);
}
_ => {}
}
}
for (name, item) in resolve.worlds[world].imports.iter() {
add_item(&mut self.import_map, resolve, name, item, &required)?;
}
return Ok(());
fn add_item(
import_map: &mut IndexMap<Option<String>, ImportedInterface>,
resolve: &Resolve,
name: &WorldKey,
item: &WorldItem,
required: &Required<'_>,
) -> Result<()> {
let name = resolve.name_world_key(name);
log::trace!("register import `{name}`");
let import_map_key = match item {
WorldItem::Function(_) | WorldItem::Type(_) => None,
WorldItem::Interface { .. } => Some(name),
};
let interface_id = match item {
WorldItem::Function(_) | WorldItem::Type(_) => None,
WorldItem::Interface { id, .. } => Some(*id),
};
let interface = import_map
.entry(import_map_key)
.or_insert_with(|| ImportedInterface {
interface: interface_id,
lowerings: Default::default(),
});
assert_eq!(interface.interface, interface_id);
match item {
WorldItem::Function(func) => {
interface.add_func(required, resolve, func);
}
WorldItem::Type(ty) => {
interface.add_type(required, resolve, *ty);
}
WorldItem::Interface { id, .. } => {
for (_name, ty) in resolve.interfaces[*id].types.iter() {
interface.add_type(required, resolve, *ty);
}
for (_name, func) in resolve.interfaces[*id].functions.iter() {
interface.add_func(required, resolve, func);
}
}
}
Ok(())
}
}
/// Determines the set of live imported types which are required to satisfy
/// the imports and exports of the lifted core module.
fn process_live_type_imports(&mut self) {
let mut live = LiveTypes::default();
let resolve = &self.encoder.metadata.resolve;
let world = self.encoder.metadata.world;
// First use the previously calculated metadata about live imports to
// determine the set of live types in those imports.
self.add_live_imports(world, &self.info.imports, &mut live);
for (adapter_name, adapter) in self.adapters.iter() {
log::trace!("processing adapter `{adapter_name}`");
self.add_live_imports(world, &adapter.info.imports, &mut live);
}
// Next any imported types used by an export must also be considered
// live. This is a little tricky though because interfaces can be both
// imported and exported, so it's not as simple as registering the
// entire export's set of types and their transitive references
// (otherwise if you only export an interface it would consider those
// types imports live too).
//
// Here if the export is an interface the set of live types for that
// interface is calculated separately. The `exports_used` field
// previously calculated is then consulted to add any types owned by
// interfaces not in the `exports_used` set to the live imported types
// set. This means that only types not defined by referenced exports
// will get added here.
for (name, item) in resolve.worlds[world].exports.iter() {
log::trace!("add live world export `{}`", resolve.name_world_key(name));
let id = match item {
WorldItem::Interface { id, .. } => id,
WorldItem::Function(_) | WorldItem::Type(_) => {
live.add_world_item(resolve, item);
continue;
}
};
let exports_used = &self.exports_used[id];
let mut live_from_export = LiveTypes::default();
live_from_export.add_world_item(resolve, item);
for ty in live_from_export.iter() {
let owner = match resolve.types[ty].owner {
TypeOwner::Interface(id) => id,
_ => continue,
};
if owner != *id && !exports_used.contains(&owner) {
live.add_type_id(resolve, ty);
}
}
}
for live in live.iter() {
let owner = match resolve.types[live].owner {
TypeOwner::Interface(id) => id,
_ => continue,
};
self.live_type_imports
.entry(owner)
.or_insert(Default::default())
.insert(live);
}
}
fn add_live_imports(&self, world: WorldId, imports: &ImportMap, live: &mut LiveTypes) {
let resolve = &self.encoder.metadata.resolve;
let world = &resolve.worlds[world];
// FIXME: ideally liveness information here would be plumbed through to
// encoding but that's not done at this time. Only liveness for each
// interface is plumbed so top-level world types are unconditionally
// encoded and therefore unconditionally live here. Once encoding is
// based on conditionally-live things then this should be removed.
for (_, item) in world.imports.iter() {
if let WorldItem::Type(id) = item {
live.add_type_id(resolve, *id);
}
}
for (_, _, import) in imports.imports() {
match import {
// WIT-level function imports need the associated WIT definition.
Import::WorldFunc(key, _, _) => {
live.add_world_item(resolve, &world.imports[key]);
}
Import::InterfaceFunc(_, id, name, _) => {
live.add_func(resolve, &resolve.interfaces[*id].functions[name]);
}
// Resource-related intrinsics will need the resource.
Import::ImportedResourceDrop(.., ty)
| Import::ExportedResourceDrop(_, ty)
| Import::ExportedResourceNew(_, ty)
| Import::ExportedResourceRep(_, ty) => live.add_type_id(resolve, *ty),
// Future/Stream related intrinsics need to refer to the type
// that the intrinsic is operating on.
Import::StreamNew(info)
| Import::StreamRead { info, async_: _ }
| Import::StreamWrite { info, async_: _ }
| Import::StreamCancelRead { info, async_: _ }
| Import::StreamCancelWrite { info, async_: _ }
| Import::StreamDropReadable(info)
| Import::StreamDropWritable(info)
| Import::FutureNew(info)
| Import::FutureRead { info, async_: _ }
| Import::FutureWrite { info, async_: _ }
| Import::FutureCancelRead { info, async_: _ }
| Import::FutureCancelWrite { info, async_: _ }
| Import::FutureDropReadable(info)
| Import::FutureDropWritable(info) => {
if let PayloadType::Type { id, .. } = info.ty {
live.add_type_id(resolve, id);
}
}
// The `task.return` intrinsic needs to be able to refer to the
// type that is being returned.
Import::ExportedTaskReturn(.., ty) => {
if let Some(ty) = ty {
live.add_type(resolve, ty);
}
}
// Intrinsics that don't need to refer to WIT types can be
// skipped here.
Import::AdapterExport { .. }
| Import::MainModuleMemory
| Import::MainModuleExport { .. }
| Import::Item(_)
| Import::ContextGet(_)
| Import::ContextSet(_)
| Import::BackpressureInc
| Import::BackpressureDec
| Import::WaitableSetNew
| Import::WaitableSetWait { .. }
| Import::WaitableSetPoll { .. }
| Import::WaitableSetDrop
| Import::WaitableJoin
| Import::ThreadYield { .. }
| Import::SubtaskDrop
| Import::SubtaskCancel { .. }
| Import::ErrorContextNew { .. }
| Import::ErrorContextDebugMessage { .. }
| Import::ErrorContextDrop
| Import::ExportedTaskCancel
| Import::ThreadIndex
| Import::ThreadNewIndirect { .. }
| Import::ThreadSwitchTo { .. }
| Import::ThreadSuspend { .. }
| Import::ThreadResumeLater
| Import::ThreadYieldTo { .. } => {}
}
}
}
fn process_exports_used(&mut self) {
let resolve = &self.encoder.metadata.resolve;
let world = self.encoder.metadata.world;
let exports = &resolve.worlds[world].exports;
for (_name, item) in exports.iter() {
let id = match item {
WorldItem::Function(_) => continue,
WorldItem::Interface { id, .. } => *id,
WorldItem::Type(_) => unreachable!(),
};
let mut set = HashSet::new();
for other in resolve.interface_direct_deps(id) {
// If this dependency is not exported, then it'll show up
// through an import, so we're not interested in it.
if !exports.contains_key(&WorldKey::Interface(other)) {
continue;
}
// Otherwise this is a new exported dependency of ours, and
// additionally this interface inherits all the transitive
// dependencies too.
if set.insert(other) {
set.extend(self.exports_used[&other].iter().copied());
}
}
let prev = self.exports_used.insert(id, set);
assert!(prev.is_none());
}
}
}
#[derive(Default)]
struct Required<'a> {
interface_funcs: IndexMap<Option<InterfaceId>, IndexSet<(&'a str, AbiVariant)>>,
resource_drops: IndexSet<TypeId>,
}
impl ImportedInterface {
fn add_func(&mut self, required: &Required<'_>, resolve: &Resolve, func: &Function) {
let mut abis = Vec::with_capacity(2);
if let Some(set) = required.interface_funcs.get(&self.interface) {
if set.contains(&(func.name.as_str(), AbiVariant::GuestImport)) {
abis.push(AbiVariant::GuestImport);
}
if set.contains(&(func.name.as_str(), AbiVariant::GuestImportAsync)) {
abis.push(AbiVariant::GuestImportAsync);
}
}
for abi in abis {
log::trace!("add func {} {abi:?}", func.name);
let options = RequiredOptions::for_import(resolve, func, abi);
let lowering = if options.is_empty() {
Lowering::Direct
} else {
let sig = resolve.wasm_signature(abi, func);
Lowering::Indirect { sig, options }
};
let prev = self.lowerings.insert((func.name.clone(), abi), lowering);
assert!(prev.is_none());
}
}
fn add_type(&mut self, required: &Required<'_>, resolve: &Resolve, id: TypeId) {
let ty = &resolve.types[id];
match &ty.kind {
TypeDefKind::Resource => {}
_ => return,
}
let name = ty.name.as_deref().expect("resources must be named");
if required.resource_drops.contains(&id) {
let name = format!("{name}_drop");
let prev = self
.lowerings
.insert((name, AbiVariant::GuestImport), Lowering::ResourceDrop(id));
assert!(prev.is_none());
}
}
}

1134
vendor/wit-component/src/gc.rs vendored Normal file

File diff suppressed because it is too large Load Diff

178
vendor/wit-component/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,178 @@
//! The WebAssembly component tooling.
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::str::FromStr;
use std::{borrow::Cow, fmt::Display};
use anyhow::{Result, bail};
use wasm_encoder::{CanonicalOption, Encode, Section};
use wit_parser::{Resolve, WorldId};
mod encoding;
mod gc;
mod linking;
mod printing;
mod targets;
mod validation;
pub use encoding::{ComponentEncoder, LibraryInfo, encode};
pub use linking::Linker;
pub use printing::*;
pub use targets::*;
pub use validation::AdapterModuleDidNotExport;
pub use wit_parser::decoding::{DecodedWasm, decode, decode_reader};
pub mod metadata;
#[cfg(feature = "dummy-module")]
pub use dummy::dummy_module;
#[cfg(feature = "dummy-module")]
mod dummy;
#[cfg(feature = "semver-check")]
mod semver_check;
#[cfg(feature = "semver-check")]
pub use semver_check::*;
/// Supported string encoding formats.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum StringEncoding {
/// Strings are encoded with UTF-8.
#[default]
UTF8,
/// Strings are encoded with UTF-16.
UTF16,
/// Strings are encoded with compact UTF-16 (i.e. Latin1+UTF-16).
CompactUTF16,
}
impl Display for StringEncoding {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StringEncoding::UTF8 => write!(f, "utf8"),
StringEncoding::UTF16 => write!(f, "utf16"),
StringEncoding::CompactUTF16 => write!(f, "compact-utf16"),
}
}
}
impl FromStr for StringEncoding {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"utf8" => Ok(StringEncoding::UTF8),
"utf16" => Ok(StringEncoding::UTF16),
"compact-utf16" => Ok(StringEncoding::CompactUTF16),
_ => bail!("unknown string encoding `{}`", s),
}
}
}
impl From<StringEncoding> for wasm_encoder::CanonicalOption {
fn from(e: StringEncoding) -> wasm_encoder::CanonicalOption {
match e {
StringEncoding::UTF8 => CanonicalOption::UTF8,
StringEncoding::UTF16 => CanonicalOption::UTF16,
StringEncoding::CompactUTF16 => CanonicalOption::CompactUTF16,
}
}
}
/// A producer section to be added to all modules and components synthesized by
/// this crate
pub(crate) fn base_producers() -> wasm_metadata::Producers {
let mut producer = wasm_metadata::Producers::empty();
producer.add("processed-by", "wit-component", env!("CARGO_PKG_VERSION"));
producer
}
/// Embed component metadata in a buffer of bytes that contains a Wasm module
pub fn embed_component_metadata(
bytes: &mut Vec<u8>,
wit_resolver: &Resolve,
world: WorldId,
encoding: StringEncoding,
) -> Result<()> {
let encoded = metadata::encode(&wit_resolver, world, encoding, None)?;
let section = wasm_encoder::CustomSection {
name: "component-type".into(),
data: Cow::Borrowed(&encoded),
};
bytes.push(section.id());
section.encode(bytes);
Ok(())
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use wasmparser::Payload;
use wit_parser::Resolve;
use super::{StringEncoding, embed_component_metadata};
const MODULE_WAT: &str = r#"
(module
(type (;0;) (func))
(func (;0;) (type 0)
nop
)
)
"#;
const COMPONENT_WIT: &str = r#"
package test:foo;
world test-world {}
"#;
#[test]
fn component_metadata_embedding_works() -> Result<()> {
let mut bytes = wat::parse_str(MODULE_WAT)?;
// Get original len & custom section count
let original_len = bytes.len();
let payloads = wasmparser::Parser::new(0).parse_all(&bytes);
let original_custom_section_count = payloads.fold(0, |acc, payload| {
if let Ok(Payload::CustomSection { .. }) = payload {
acc + 1
} else {
acc
}
});
// Parse pre-canned WIT to build resolver
let mut resolver = Resolve::default();
let pkg = resolver.push_str("in-code.wit", COMPONENT_WIT)?;
let world = resolver.select_world(&[pkg], Some("test-world"))?;
// Embed component metadata
embed_component_metadata(&mut bytes, &resolver, world, StringEncoding::UTF8)?;
// Re-retrieve custom section count, and search for the component-type custom section along the way
let mut found_component_section = false;
let new_custom_section_count =
wasmparser::Parser::new(0)
.parse_all(&bytes)
.fold(0, |acc, payload| {
if let Ok(Payload::CustomSection(reader)) = payload {
if reader.name() == "component-type" {
found_component_section = true;
}
acc + 1
} else {
acc
}
});
assert!(original_len < bytes.len());
assert_eq!(original_custom_section_count + 1, new_custom_section_count);
assert!(found_component_section);
Ok(())
}
}

1727
vendor/wit-component/src/linking.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,515 @@
//! Support for parsing and analyzing [dynamic
//! library](https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md) modules.
use {
anyhow::{Context, Error, Result, bail},
std::{
collections::{BTreeSet, HashMap, HashSet},
fmt,
},
wasmparser::{
Dylink0Subsection, ExternalKind, FuncType, KnownCustom, MemInfo, Parser, Payload, RefType,
SymbolFlags, TableType, TagKind, TagType, TypeRef, ValType,
},
};
/// Represents a core Wasm value type (not including V128 or reference types, which are not yet supported)
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ValueType {
I32,
I64,
F32,
F64,
}
impl TryFrom<ValType> for ValueType {
type Error = Error;
fn try_from(value: ValType) -> Result<Self> {
Ok(match value {
ValType::I32 => Self::I32,
ValType::I64 => Self::I64,
ValType::F32 => Self::F32,
ValType::F64 => Self::F64,
_ => bail!("{value:?} not yet supported"),
})
}
}
impl From<ValueType> for wasm_encoder::ValType {
fn from(value: ValueType) -> Self {
match value {
ValueType::I32 => Self::I32,
ValueType::I64 => Self::I64,
ValueType::F32 => Self::F32,
ValueType::F64 => Self::F64,
}
}
}
/// Represents a core Wasm function type
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FunctionType {
pub parameters: Vec<ValueType>,
pub results: Vec<ValueType>,
}
impl fmt::Display for FunctionType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?} -> {:?}", self.parameters, self.results)
}
}
impl TryFrom<&FuncType> for FunctionType {
type Error = Error;
fn try_from(value: &FuncType) -> Result<Self> {
Ok(Self {
parameters: value
.params()
.iter()
.map(|&v| ValueType::try_from(v))
.collect::<Result<_>>()?,
results: value
.results()
.iter()
.map(|&v| ValueType::try_from(v))
.collect::<Result<_>>()?,
})
}
}
/// Represents a core Wasm global variable type
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GlobalType {
pub ty: ValueType,
pub mutable: bool,
pub shared: bool,
}
impl fmt::Display for GlobalType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.mutable {
write!(f, "mut ")?;
}
write!(f, "{:?}", self.ty)
}
}
/// Represents a core Wasm export or import type
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Type {
Function(FunctionType),
Global(GlobalType),
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Function(ty) => write!(f, "function {ty}"),
Self::Global(ty) => write!(f, "global {ty}"),
}
}
}
impl From<&Type> for wasm_encoder::ExportKind {
fn from(value: &Type) -> Self {
match value {
Type::Function(_) => wasm_encoder::ExportKind::Func,
Type::Global(_) => wasm_encoder::ExportKind::Global,
}
}
}
/// Represents a core Wasm import
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Import<'a> {
pub module: &'a str,
pub name: &'a str,
pub ty: Type,
pub flags: SymbolFlags,
}
/// Represents a core Wasm export
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ExportKey<'a> {
pub name: &'a str,
pub ty: Type,
}
impl<'a> fmt::Display for ExportKey<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({})", self.name, self.ty)
}
}
/// Represents a core Wasm export, including dylink.0 flags
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Export<'a> {
pub key: ExportKey<'a>,
pub flags: SymbolFlags,
}
/// Metadata extracted from a dynamic library module
#[derive(Debug)]
pub struct Metadata<'a> {
/// The name of the module
///
/// This is currently not part of the file itself and must be provided separately, but the plan is to add
/// something like a `WASM_DYLINK_SO_NAME` field to the dynamic linking tool convention so we can parse it
/// along with everything else.
pub name: &'a str,
/// Whether this module should be resolvable via `dlopen`
pub dl_openable: bool,
/// The `WASM_DYLINK_MEM_INFO` value (or all zeros if not found)
pub mem_info: MemInfo,
/// The `WASM_DYLINK_NEEDED` values, if any
pub needed_libs: Vec<&'a str>,
/// The `WASM_DYLINK_RUNTIME_PATH` values, if any
pub runtime_path: Vec<&'a str>,
/// Whether this module exports `__wasm_apply_data_relocs`
pub has_data_relocs: bool,
/// Whether this module exports `__wasm_call_ctors`
pub has_ctors: bool,
/// Whether this module exports `_initialize`
pub has_initialize: bool,
/// Whether this module exports `_start`
pub has_wasi_start: bool,
/// Whether this module exports `__wasm_set_libraries`
pub has_set_libraries: bool,
/// Whether this module includes any `component-type*` custom sections which include exports
pub has_component_exports: bool,
/// Whether this module imports `__asyncify_state` or `__asyncify_data`, indicating that it is
/// asyncified with `--pass-arg=asyncify-relocatable` option.
pub is_asyncified: bool,
/// The functions imported from the `env` module, if any
pub env_imports: BTreeSet<(&'a str, (FunctionType, SymbolFlags))>,
/// The memory addresses imported from `GOT.mem`, if any
pub memory_address_imports: BTreeSet<&'a str>,
/// The table addresses imported from `GOT.func`, if any
pub table_address_imports: BTreeSet<&'a str>,
/// Imported exception tags
pub tag_imports: BTreeSet<(&'a str, FunctionType)>,
/// The symbols exported by this module, if any
pub exports: BTreeSet<Export<'a>>,
/// The symbols imported by this module (and not accounted for in the above fields), if any
pub imports: BTreeSet<Import<'a>>,
}
impl<'a> Metadata<'a> {
/// Parse the specified module and extract its metadata.
pub fn try_new(
name: &'a str,
dl_openable: bool,
module: &'a [u8],
adapter_names: &HashSet<&str>,
) -> Result<Self> {
let bindgen = crate::metadata::decode(module)?.1;
let has_component_exports = !bindgen.resolve.worlds[bindgen.world].exports.is_empty();
let mut result = Self {
name,
dl_openable,
mem_info: MemInfo {
memory_size: 0,
memory_alignment: 1,
table_size: 0,
table_alignment: 1,
},
needed_libs: Vec::new(),
runtime_path: Vec::new(),
has_data_relocs: false,
has_ctors: false,
has_initialize: false,
has_wasi_start: false,
has_set_libraries: false,
has_component_exports,
is_asyncified: false,
env_imports: BTreeSet::new(),
memory_address_imports: BTreeSet::new(),
table_address_imports: BTreeSet::new(),
exports: BTreeSet::new(),
imports: BTreeSet::new(),
tag_imports: BTreeSet::new(),
};
let mut types = Vec::new();
let mut function_types = Vec::new();
let mut global_types = Vec::new();
let mut import_info = HashMap::new();
let mut export_info = HashMap::new();
for payload in Parser::new(0).parse_all(module) {
match payload? {
Payload::CustomSection(section) => {
if let KnownCustom::Dylink0(reader) = section.as_known() {
for subsection in reader {
match subsection.context("failed to parse `dylink.0` subsection")? {
Dylink0Subsection::MemInfo(info) => result.mem_info = info,
Dylink0Subsection::Needed(needed) => {
result.needed_libs = needed.clone()
}
Dylink0Subsection::ExportInfo(info) => {
export_info
.extend(info.iter().map(|info| (info.name, info.flags)));
}
Dylink0Subsection::ImportInfo(info) => {
import_info.extend(
info.iter()
.map(|info| ((info.module, info.field), info.flags)),
);
}
Dylink0Subsection::RuntimePath(runtime_path) => {
result.runtime_path.extend(runtime_path.iter());
}
Dylink0Subsection::Unknown { ty, .. } => {
bail!("unrecognized `dylink.0` subsection: {ty}")
}
}
}
}
}
Payload::TypeSection(reader) => {
types = reader
.into_iter_err_on_gc_types()
.collect::<Result<Vec<_>, _>>()?;
}
Payload::ImportSection(reader) => {
for import in reader.into_imports() {
let import = import?;
match import.ty {
TypeRef::Func(ty) => function_types.push(usize::try_from(ty).unwrap()),
TypeRef::Global(ty) => global_types.push(ty),
_ => (),
}
let type_error = || {
bail!(
"unexpected type for {}:{}: {:?}",
import.module,
import.name,
import.ty
)
};
match (import.module, import.name) {
("env", "memory") => {
if !matches!(import.ty, TypeRef::Memory(_)) {
return type_error();
}
}
("env", "__asyncify_data" | "__asyncify_state") => {
result.is_asyncified = true;
if !matches!(
import.ty,
TypeRef::Global(wasmparser::GlobalType {
content_type: ValType::I32,
..
})
) {
return type_error();
}
}
("env", "__memory_base" | "__table_base" | "__stack_pointer") => {
if !matches!(
import.ty,
TypeRef::Global(wasmparser::GlobalType {
content_type: ValType::I32,
..
})
) {
return type_error();
}
}
("env", "__indirect_function_table") => {
if let TypeRef::Table(TableType {
element_type,
maximum: None,
..
}) = import.ty
{
if element_type != RefType::FUNCREF {
return type_error();
}
} else {
return type_error();
}
}
("env", name) => match import.ty {
TypeRef::Func(ty) => {
result.env_imports.insert((
name,
(
FunctionType::try_from(
&types[usize::try_from(ty).unwrap()],
)?,
import_info
.get(&("env", name))
.copied()
.unwrap_or_default(),
),
));
}
TypeRef::Tag(TagType {
kind: TagKind::Exception,
func_type_idx,
}) => {
result.tag_imports.insert((
name,
FunctionType::try_from(
&types[usize::try_from(func_type_idx).unwrap()],
)?,
));
}
_ => return type_error(),
},
("GOT.mem", name) => {
if let TypeRef::Global(wasmparser::GlobalType {
content_type: ValType::I32,
..
}) = import.ty
{
match name {
"__heap_base" | "__heap_end" | "__stack_high"
| "__stack_low" => (),
_ => {
result.memory_address_imports.insert(name);
}
}
} else {
return type_error();
}
}
("GOT.func", name) => {
if let TypeRef::Global(wasmparser::GlobalType {
content_type: ValType::I32,
..
}) = import.ty
{
result.table_address_imports.insert(name);
} else {
return type_error();
}
}
(module, name) if adapter_names.contains(module) => {
let ty = match import.ty {
TypeRef::Global(wasmparser::GlobalType {
content_type,
mutable,
shared,
}) => Type::Global(GlobalType {
ty: content_type.try_into()?,
mutable,
shared,
}),
TypeRef::Func(ty) => Type::Function(FunctionType::try_from(
&types[usize::try_from(ty).unwrap()],
)?),
ty => {
bail!("unsupported import kind for {module}.{name}: {ty:?}",)
}
};
let flags = import_info
.get(&(module, name))
.copied()
.unwrap_or_default();
result.imports.insert(Import {
module,
name,
ty,
flags,
});
}
_ => {
if !matches!(import.ty, TypeRef::Func(_) | TypeRef::Global(_)) {
return type_error();
}
}
}
}
}
Payload::FunctionSection(reader) => {
for function in reader {
function_types.push(usize::try_from(function?).unwrap());
}
}
Payload::GlobalSection(reader) => {
for global in reader {
global_types.push(global?.ty);
}
}
Payload::ExportSection(reader) => {
for export in reader {
let export = export?;
match export.name {
"__wasm_apply_data_relocs" => result.has_data_relocs = true,
"__wasm_call_ctors" => result.has_ctors = true,
"_initialize" => result.has_initialize = true,
"_start" => result.has_wasi_start = true,
"__wasm_set_libraries" => result.has_set_libraries = true,
_ => {
let ty = match export.kind {
ExternalKind::Func => Type::Function(FunctionType::try_from(
&types[function_types
[usize::try_from(export.index).unwrap()]],
)?),
ExternalKind::Global => {
let ty =
global_types[usize::try_from(export.index).unwrap()];
Type::Global(GlobalType {
ty: ValueType::try_from(ty.content_type)?,
mutable: ty.mutable,
shared: ty.shared,
})
}
kind => {
bail!(
"unsupported export kind for {}: {kind:?}",
export.name
)
}
};
let flags =
export_info.get(&export.name).copied().unwrap_or_default();
result.exports.insert(Export {
key: ExportKey {
name: export.name,
ty,
},
flags,
});
}
}
}
}
_ => {}
}
}
Ok(result)
}
}

447
vendor/wit-component/src/metadata.rs vendored Normal file
View File

@@ -0,0 +1,447 @@
//! Definition for encoding of custom sections within core wasm modules of
//! component-model related data.
//!
//! When creating a component from a source language the high-level process for
//! doing this is that code will be generated into the source language by
//! `wit-bindgen` or a similar tool which will be compiled down to core wasm.
//! The core wasm file is then fed into `wit-component` and a component is
//! created. This means that the componentization process is decoupled from the
//! binding generation process and intentionally affords for linking together
//! libraries into the main core wasm module that import different interfaces.
//!
//! The purpose of this module is to define an intermediate format to reside in
//! a custom section in the core wasm output. This intermediate format is
//! carried through the wasm linker through a custom section whose name starts
//! with `component-type`. This custom section is created
//! per-language-binding-generation and consumed by slurping up all the
//! sections during the component creation process.
//!
//! Currently the encoding of this custom section is itself a component. The
//! component has a single export which is a component type which represents the
//! `world` that was bound during bindings generation. This single export is
//! used to decode back into a `Resolve` with a WIT representation.
//!
//! Currently the component additionally has a custom section named
//! `wit-component-encoding` (see `CUSTOM_SECTION_NAME`). This section is
//! currently defined as 2 bytes:
//!
//! * The first byte is `CURRENT_VERSION` to help protect against future and
//! past changes.
//! * The second byte indicates the string encoding used for imports/exports as
//! part of the bindings process. The mapping is defined by
//! `encode_string_encoding`.
//!
//! This means that the top-level `encode` function takes a `Resolve`, a
//! `WorldId`, and a `StringEncoding`. Note that the top-level `decode` function
//! is slightly different because it's taking all custom sections in a core
//! wasm binary, possibly from multiple invocations of bindgen, and unioning
//! them all together. This means that the output is a `Bindgen` which
//! represents the union of all previous bindings.
//!
//! The dual of `encode` is the `decode_custom_section` function which decodes
//! the three arguments originally passed to `encode`.
use crate::{DecodedWasm, StringEncoding};
use anyhow::{Context, Result, bail};
use indexmap::{IndexMap, IndexSet};
use std::borrow::Cow;
use wasm_encoder::{
ComponentBuilder, ComponentExportKind, ComponentType, ComponentTypeRef, CustomSection,
};
use wasm_metadata::Producers;
use wasmparser::{BinaryReader, Encoding, Parser, Payload};
use wit_parser::{CloneMaps, Package, PackageName, Resolve, World, WorldId, WorldItem, WorldKey};
const CURRENT_VERSION: u8 = 0x04;
const CUSTOM_SECTION_NAME: &str = "wit-component-encoding";
/// The result of decoding binding information from a WebAssembly binary.
///
/// This structure is returned by [`decode`] and represents the interface of a
/// WebAssembly binary.
pub struct Bindgen {
/// Interface and type information for this binary.
pub resolve: Resolve,
/// The world that was bound.
pub world: WorldId,
/// Metadata about this specific module that was bound.
pub metadata: ModuleMetadata,
/// Producer information about tools used to produce this specific module.
pub producers: Option<Producers>,
}
impl Default for Bindgen {
fn default() -> Bindgen {
let mut resolve = Resolve::default();
let package = resolve.packages.alloc(Package {
name: PackageName {
namespace: "root".to_string(),
name: "root".to_string(),
version: None,
},
docs: Default::default(),
interfaces: Default::default(),
worlds: Default::default(),
});
let world = resolve.worlds.alloc(World {
name: "root".to_string(),
docs: Default::default(),
imports: Default::default(),
exports: Default::default(),
includes: Default::default(),
include_names: Default::default(),
package: Some(package),
stability: Default::default(),
});
resolve.packages[package]
.worlds
.insert("root".to_string(), world);
Bindgen {
resolve,
world,
metadata: ModuleMetadata::default(),
producers: None,
}
}
}
/// Module-level metadata that's specific to one core WebAssembly module. This
/// is extracted with a [`Bindgen`].
#[derive(Default)]
pub struct ModuleMetadata {
/// Per-function options imported into the core wasm module, currently only
/// related to string encoding.
pub import_encodings: EncodingMap,
/// Per-function options exported from the core wasm module, currently only
/// related to string encoding.
pub export_encodings: EncodingMap,
}
/// Internal map that keeps track of encodings for various world imports and
/// exports.
///
/// Stored in [`ModuleMetadata`].
#[derive(Default)]
pub struct EncodingMap {
/// A map of an "identifying string" for world items to what string
/// encoding the import or export is using.
///
/// The keys of this map are created by `EncodingMap::key` and are
/// specifically chosen to be able to be looked up during both insertion and
/// fetching. Note that in particular this map does not use `*Id` types such
/// as `InterfaceId` from `wit_parser`. This is due to the fact that during
/// world merging new interfaces are created for named imports (e.g. `import
/// x: interface { ... }`) as inline interfaces are copied from one world to
/// another. Additionally during world merging different interfaces at the
/// same version may be deduplicated.
///
/// For these reasons a string-based key is chosen to avoid juggling IDs
/// through the world merging process. Additionally versions are chopped off
/// for now to help with a problem such as:
///
/// * The main module imports a:b/c@0.1.0
/// * An adapter imports a:b/c@0.1.1
/// * The final world uses a:b/c@0.1.1, but the main module has no
/// encoding listed for that exact item.
///
/// By chopping off versions this is able to get everything registered
/// correctly even in the fact of merging interfaces and worlds.
encodings: IndexMap<String, StringEncoding>,
}
impl EncodingMap {
fn insert_all(
&mut self,
resolve: &Resolve,
set: &IndexMap<WorldKey, WorldItem>,
encoding: StringEncoding,
) {
for (name, item) in set {
match item {
WorldItem::Function(func) => {
let key = self.key(resolve, name, &func.name);
self.encodings.insert(key, encoding);
}
WorldItem::Interface { id, .. } => {
for (func, _) in resolve.interfaces[*id].functions.iter() {
let key = self.key(resolve, name, func);
self.encodings.insert(key, encoding);
}
}
WorldItem::Type(_) => {}
}
}
}
/// Looks up the encoding of the function `func` which is scoped under `key`
/// in the world in question.
pub fn get(&self, resolve: &Resolve, key: &WorldKey, func: &str) -> Option<StringEncoding> {
let key = self.key(resolve, key, func);
self.encodings.get(&key).copied()
}
fn key(&self, resolve: &Resolve, key: &WorldKey, func: &str) -> String {
format!(
"{}/{func}",
match key {
WorldKey::Name(name) => name.to_string(),
WorldKey::Interface(id) => {
let iface = &resolve.interfaces[*id];
let pkg = &resolve.packages[iface.package.unwrap()];
format!(
"{}:{}/{}",
pkg.name.namespace,
pkg.name.name,
iface.name.as_ref().unwrap()
)
}
}
)
}
fn merge(&mut self, other: EncodingMap) -> Result<()> {
for (key, encoding) in other.encodings {
if let Some(prev) = self.encodings.insert(key.clone(), encoding) {
if prev != encoding {
bail!("conflicting string encodings specified for `{key}`");
}
}
}
Ok(())
}
}
/// This function will parse the core `wasm` binary given as input and return a
/// [`Bindgen`] which extracts the custom sections describing component-level
/// types from within the binary itself.
///
/// This is used to parse the output of `wit-bindgen`-generated modules and is
/// one of the earliest phases in transitioning such a module to a component.
/// The extraction here provides the metadata necessary to continue the process
/// later on.
///
/// This will return an error if `wasm` is not a valid WebAssembly module.
///
/// If a `component-type` custom section was found then a new binary is
/// optionally returned with the custom sections stripped out. If no
/// `component-type` custom sections are found then `None` is returned.
pub fn decode(wasm: &[u8]) -> Result<(Option<Vec<u8>>, Bindgen)> {
let mut ret = Bindgen::default();
let mut new_module = wasm_encoder::Module::new();
let mut found_custom = false;
for payload in wasmparser::Parser::new(0).parse_all(wasm) {
let payload = payload.context("decoding item in module")?;
match payload {
wasmparser::Payload::CustomSection(cs) if cs.name().starts_with("component-type") => {
let data = Bindgen::decode_custom_section(cs.data())
.with_context(|| format!("decoding custom section {}", cs.name()))?;
ret.merge(data)
.with_context(|| format!("updating metadata for section {}", cs.name()))?;
found_custom = true;
}
wasmparser::Payload::Version { encoding, .. } if encoding != Encoding::Module => {
bail!("decoding a component is not supported")
}
_ => {
if let Some((id, range)) = payload.as_section() {
new_module.section(&wasm_encoder::RawSection {
id,
data: &wasm[range],
});
}
}
}
}
if found_custom {
Ok((Some(new_module.finish()), ret))
} else {
Ok((None, ret))
}
}
/// Creates a `component-type*` custom section to be decoded by `decode` above.
///
/// This is primarily created by wit-bindgen-based guest generators to embed
/// into the final core wasm binary. The core wasm binary is later fed
/// through `wit-component` to produce the actual component where this returned
/// section will be decoded.
pub fn encode(
resolve: &Resolve,
world: WorldId,
string_encoding: StringEncoding,
extra_producers: Option<&Producers>,
) -> Result<Vec<u8>> {
let ty = crate::encoding::encode_world(resolve, world)?;
let world = &resolve.worlds[world];
let mut outer_ty = ComponentType::new();
outer_ty.ty().component(&ty);
outer_ty.export(
&resolve.id_of_name(world.package.unwrap(), &world.name),
ComponentTypeRef::Component(0),
);
let mut builder = ComponentBuilder::default();
let string_encoding = encode_string_encoding(string_encoding);
builder.custom_section(&CustomSection {
name: CUSTOM_SECTION_NAME.into(),
data: Cow::Borrowed(&[CURRENT_VERSION, string_encoding]),
});
let ty = builder.type_component(None, &outer_ty);
builder.export(&world.name, ComponentExportKind::Type, ty, None);
let mut producers = crate::base_producers();
if let Some(p) = extra_producers {
producers.merge(&p);
}
builder.raw_custom_section(&producers.raw_custom_section());
Ok(builder.finish())
}
fn decode_custom_section(wasm: &[u8]) -> Result<(Resolve, WorldId, StringEncoding)> {
let (resolve, world) = wit_parser::decoding::decode_world(wasm)?;
let mut custom_section = None;
for payload in Parser::new(0).parse_all(wasm) {
match payload? {
Payload::CustomSection(s) if s.name() == CUSTOM_SECTION_NAME => {
custom_section = Some(s.data());
}
_ => {}
}
}
let string_encoding = match custom_section {
None => bail!("missing custom section of name `{CUSTOM_SECTION_NAME}`"),
Some([CURRENT_VERSION, byte]) => decode_string_encoding(*byte)?,
Some([]) => bail!("custom section `{CUSTOM_SECTION_NAME}` in unknown format"),
Some([version, ..]) => bail!(
"custom section `{CUSTOM_SECTION_NAME}` uses format {version} but only {CURRENT_VERSION} is supported"
),
};
Ok((resolve, world, string_encoding))
}
fn encode_string_encoding(e: StringEncoding) -> u8 {
match e {
StringEncoding::UTF8 => 0x00,
StringEncoding::UTF16 => 0x01,
StringEncoding::CompactUTF16 => 0x02,
}
}
fn decode_string_encoding(byte: u8) -> Result<StringEncoding> {
match byte {
0x00 => Ok(StringEncoding::UTF8),
0x01 => Ok(StringEncoding::UTF16),
0x02 => Ok(StringEncoding::CompactUTF16),
byte => bail!("invalid string encoding {byte:#x}"),
}
}
impl Bindgen {
fn decode_custom_section(data: &[u8]) -> Result<Bindgen> {
let wasm;
let world;
let resolve;
let encoding;
let mut reader = BinaryReader::new(data, 0);
match reader.read_u8()? {
// Historical 0x03 format where the support here will be deleted in
// the future
0x03 => {
encoding = decode_string_encoding(reader.read_u8()?)?;
let world_name = reader.read_string()?;
wasm = &data[reader.original_position()..];
let (r, pkg) = match crate::decode(wasm)? {
DecodedWasm::WitPackage(resolve, pkgs) => (resolve, pkgs),
DecodedWasm::Component(..) => bail!("expected encoded wit package(s)"),
};
resolve = r;
world = resolve.select_world(&[pkg], Some(world_name.into()))?;
}
// Current format where `data` is a wasm component itself.
_ => {
wasm = data;
(resolve, world, encoding) = decode_custom_section(wasm)?;
}
}
Ok(Bindgen {
metadata: ModuleMetadata::new(&resolve, world, encoding),
producers: wasm_metadata::Producers::from_wasm(wasm)?,
resolve,
world,
})
}
/// Merges another `BindgenMetadata` into this one.
///
/// This operation is intended to be akin to "merging worlds" when the
/// abstraction level for that is what we're working at here. For now the
/// merge operation only succeeds if the two metadata descriptions are
/// entirely disjoint.
///
/// Note that at this time there's no support for changing string encodings
/// between metadata.
///
/// This function returns the set of exports that the main world of
/// `other` added to the world in `self`.
pub fn merge(&mut self, other: Bindgen) -> Result<IndexSet<WorldKey>> {
let Bindgen {
resolve,
world,
metadata:
ModuleMetadata {
import_encodings,
export_encodings,
},
producers,
} = other;
let remap = self
.resolve
.merge(resolve)
.context("failed to merge WIT package sets together")?;
let world = remap.map_world(world, None)?;
let exports = self.resolve.worlds[world].exports.keys().cloned().collect();
self.resolve
.merge_worlds(world, self.world, &mut CloneMaps::default())
.context("failed to merge worlds from two documents")?;
self.metadata.import_encodings.merge(import_encodings)?;
self.metadata.export_encodings.merge(export_encodings)?;
if let Some(producers) = producers {
if let Some(mine) = &mut self.producers {
mine.merge(&producers);
} else {
self.producers = Some(producers);
}
}
Ok(exports)
}
}
impl ModuleMetadata {
/// Creates a new `ModuleMetadata` instance holding the given set of
/// interfaces which are expected to all use the `encoding` specified.
pub fn new(resolve: &Resolve, world: WorldId, encoding: StringEncoding) -> ModuleMetadata {
let mut ret = ModuleMetadata::default();
let world = &resolve.worlds[world];
ret.export_encodings
.insert_all(resolve, &world.exports, encoding);
ret.import_encodings
.insert_all(resolve, &world.imports, encoding);
ret
}
}

1485
vendor/wit-component/src/printing.rs vendored Normal file

File diff suppressed because it is too large Load Diff

113
vendor/wit-component/src/semver_check.rs vendored Normal file
View File

@@ -0,0 +1,113 @@
use crate::{
ComponentEncoder, StringEncoding, dummy_module, embed_component_metadata,
encoding::encode_world,
};
use anyhow::{Context, Result, bail};
use wasm_encoder::{ComponentBuilder, ComponentExportKind, ComponentTypeRef};
use wasmparser::Validator;
use wit_parser::{ManglingAndAbi, Resolve, WorldId};
/// Tests whether `new` is a semver-compatible upgrade from the world `prev`.
///
/// This function is will test whether a WIT-level semver-compatible predicate
/// holds. Internally this will ignore all versions associated with packages and
/// will instead test for structural equality between types and such. For
/// example `new` is allowed to have more imports and fewer exports, but types
/// and such must have the exact same structure otherwise (e.g. function params,
/// record fields, etc).
//
// NB: the general implementation strategy here is similar to the `targets`
// function where components are synthesized and we effectively rely on
// wasmparser to figure out everything for us. Specifically what happens is:
//
// 1. A dummy component representing `prev` is created.
// 2. A component importing a component of shape `new` is created.
// 3. The component from (2) is instantiated with the component from (1).
//
// If that all type-checks and is valid then the semver compatible predicate
// holds. Otherwise something has gone wrong.
//
// Note that this does not produce great error messages, so this implementation
// likely wants to be improved in the future.
pub fn semver_check(mut resolve: Resolve, prev: WorldId, new: WorldId) -> Result<()> {
// First up clear out all version information. This is required to ensure
// that the strings line up for wasmparser's validation which does exact
// string matching.
//
// This can leave `resolve` in a weird state which is why this function
// takes ownership of `resolve`. Specifically the internal maps for package
// names won't be updated and additionally this could leave two packages
// with the same name (e.g. no version) which would be a bit odd.
//
// NB: this will probably cause confusing errors below if a world imports
// two versions of a package's interfaces. I think that'll result in weird
// wasmparser validation errors as there would be two imports of the same
// name for example.
for (_id, pkg) in resolve.packages.iter_mut() {
pkg.name.version = None;
}
let old_pkg_id = resolve.worlds[prev]
.package
.context("old world not in named package")?;
let old_pkg_name = &resolve.packages[old_pkg_id].name;
let new_pkg_id = resolve.worlds[new]
.package
.context("new world not in named package")?;
let new_pkg_name = &resolve.packages[new_pkg_id].name;
if old_pkg_name != new_pkg_name {
bail!(
"the old world is in package {old_pkg_name}, which is not the same as the new world, which is in package {new_pkg_name}",
)
}
// Component that will be validated at the end.
let mut root_component = ComponentBuilder::default();
// (1) above - create a dummy component which has the shape of `prev`.
let mut prev_as_module = dummy_module(&resolve, prev, ManglingAndAbi::Standard32);
embed_component_metadata(&mut prev_as_module, &resolve, prev, StringEncoding::UTF8)
.context("failed to embed component metadata")?;
let prev_as_component = ComponentEncoder::default()
.module(&prev_as_module)
.context("failed to register previous world encoded as a module")?
.encode()
.context("failed to encode previous world as a component")?;
let component_to_test_idx = root_component.component_raw(None, &prev_as_component);
// (2) above - create a component which imports a component of the shape of
// `new`.
let test_component_idx = {
let component_ty =
encode_world(&resolve, new).context("failed to encode the new world as a type")?;
let mut component = ComponentBuilder::default();
let component_ty_idx = component.type_component(None, &component_ty);
component.import(
&resolve.worlds[new].name,
ComponentTypeRef::Component(component_ty_idx),
);
root_component.component(None, component)
};
// (3) Instantiate the component from (2) with the component to test from (1).
root_component.instantiate(
None,
test_component_idx,
[(
resolve.worlds[new].name.clone(),
ComponentExportKind::Component,
component_to_test_idx,
)],
);
let bytes = root_component.finish();
// The final step is validating that this component is indeed valid. If any
// error message is produced here an attempt is made to make it more
// understandable but there's only but so good these errors can be with this
// strategy.
Validator::new()
.validate_all(&bytes)
.context("new world is not semver-compatible with the previous world")?;
Ok(())
}

44
vendor/wit-component/src/targets.rs vendored Normal file
View File

@@ -0,0 +1,44 @@
use crate::encoding::encode_world;
use anyhow::{Context, Result};
use wasm_encoder::{ComponentBuilder, ComponentExportKind, ComponentTypeRef};
use wasmparser::{Validator, WasmFeatures};
use wit_parser::{Resolve, WorldId};
/// This function checks whether `component_to_test` correctly conforms to the world specified.
/// It does so by instantiating a generated component that imports a component instance with
/// the component type as described by the "target" world.
pub fn targets(resolve: &Resolve, world: WorldId, component_to_test: &[u8]) -> Result<()> {
let mut root_component = ComponentBuilder::default();
// (1) Embed the component to test.
let component_to_test_idx = root_component.component_raw(None, component_to_test);
// (2) Encode the world to a component type and embed a new component which
// imports the encoded component type.
let test_component_idx = {
let component_ty = encode_world(resolve, world)?;
let mut component = ComponentBuilder::default();
let component_ty_idx = component.type_component(None, &component_ty);
component.import(
&resolve.worlds[world].name,
ComponentTypeRef::Component(component_ty_idx),
);
root_component.component(None, component)
};
// (3) Instantiate the component from (2) with the component to test from (1).
let args: Vec<(String, ComponentExportKind, u32)> = vec![(
resolve.worlds[world].name.clone(),
ComponentExportKind::Component,
component_to_test_idx,
)];
root_component.instantiate(None, test_component_idx, args);
let bytes = root_component.finish();
Validator::new_with_features(WasmFeatures::all())
.validate_all(&bytes)
.context("failed to validate encoded bytes")?;
Ok(())
}

2679
vendor/wit-component/src/validation.rs vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

2
vendor/wit-component/tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
!*.wasm
!*.wat

291
vendor/wit-component/tests/components.rs vendored Normal file
View File

@@ -0,0 +1,291 @@
use anyhow::{Context, Error, Result, bail};
use libtest_mimic::{Arguments, Trial};
use pretty_assertions::assert_eq;
use std::{borrow::Cow, fs, path::Path};
use wasm_encoder::{Encode, Section};
use wasm_metadata::{Metadata, Payload};
use wasmparser::{Parser, Validator, WasmFeatures};
use wit_component::{ComponentEncoder, DecodedWasm, Linker, StringEncoding, WitPrinter};
use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup};
/// Tests the encoding of components.
///
/// This test looks in the `components/` directory for test cases.
///
/// The expected input files for a test case are:
///
/// * [required] `module.wat` *or* some combination of `lib-$name.wat` and
/// `dlopen-lib-$name.wat` - contains the core module definition(s) to be
/// encoded as a component. If one or more `lib-$name.wat` and/or
/// `dlopen-lib-$name.wat` files exist, they will be linked using `Linker`
/// such that the `lib-` ones are not `dlopen`-able but the `dlopen-lib-` ones
/// are.
/// * [required] `module.wit` *or* `lib-$name.wat` and `dlopen-lib-$name.wat`
/// corresponding to the WAT files above - WIT package(s) describing the
/// interfaces of the `module.wat` or `lib-$name.wat` and
/// `dlopen-lib-$name.wat` files. Must have a `default world`
/// * [optional] `adapt-$name.wat` - optional adapter for the module name
/// `$name`, can be specified for multiple `$name`s. Alternatively, if $name
/// doesn't work as part of a filename (e.g. contains forward slashes), it may
/// be specified on the first line of the file with the prefix `;; module name:
/// `, e.g. `;; module name: wasi:cli/environment@0.2.0`.
/// * [optional] `adapt-$name.wit` - required for each `*.wat` adapter to
/// describe imports/exports of the adapter.
/// * [optional] `stub-missing-functions` - if linking libraries and this file
/// exists, `Linker::stub_missing_functions` will be set to `true`. The
/// contents of the file are ignored.
/// * [optional] `use-built-in-libdl` - if linking libraries and this file
/// exists, `Linker::use_built_in_libdl` will be set to `true`. The contents
/// of the file are ignored.
///
/// And the output files are one of the following:
///
/// * `component.wat` - the expected encoded component in text format if the
/// encoding is expected to succeed.
/// * `component.wit` - if `component.wat` exists this is the inferred interface
/// of the component.
/// * `error.txt` - the expected error message if the encoding is expected to
/// fail.
///
/// The test encodes a component based on the input files. If the encoding
/// succeeds, it expects the output to match `component.wat`. If the encoding
/// fails, it expects the output to match `error.txt`.
///
/// Run the test with the environment variable `BLESS` set to update
/// either `component.wat` or `error.txt` depending on the outcome of the encoding.
fn main() -> Result<()> {
drop(env_logger::try_init());
let mut trials = Vec::new();
for entry in fs::read_dir("tests/components")? {
let path = entry?.path();
if !path.is_dir() {
continue;
}
trials.push(Trial::test(path.to_str().unwrap().to_string(), move || {
run_test(&path).map_err(|e| format!("{e:?}").into())
}));
}
let mut args = Arguments::from_args();
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
args.test_threads = Some(1);
}
libtest_mimic::run(&args, trials).exit();
}
fn run_test(path: &Path) -> Result<()> {
let test_case = path.file_stem().unwrap().to_str().unwrap();
let mut resolve = Resolve::default();
let (pkg_id, _) = resolve.push_dir(&path)?;
// If this test case contained multiple packages, create separate sub-directories for
// each.
let path = path.to_path_buf();
let module_path = path.join("module.wat");
let mut adapters = glob::glob(path.join("adapt-*.wat").to_str().unwrap())?;
let result = if module_path.is_file() {
let module = read_core_module(&module_path, &resolve, pkg_id)
.with_context(|| format!("failed to read core module at {module_path:?}"))?;
adapters
.try_fold(
ComponentEncoder::default()
.debug_names(true)
.module(&module)?,
|encoder, path| {
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg_id)?;
Ok::<_, Error>(encoder.adapter(&name, &wasm)?)
},
)?
.encode()
} else {
let mut libs = glob::glob(path.join("lib-*.wat").to_str().unwrap())?
.map(|path| Ok(("lib-", path?, false)))
.chain(
glob::glob(path.join("dlopen-lib-*.wat").to_str().unwrap())?
.map(|path| Ok(("dlopen-lib-", path?, true))),
)
.collect::<Result<Vec<_>>>()?;
// Sort list to ensure deterministic order, which determines priority in cases of duplicate symbols:
libs.sort_by(|(_, a, _), (_, b, _)| a.cmp(b));
let mut linker = Linker::default().validate(false).debug_names(true);
if path.join("stub-missing-functions").is_file() {
linker = linker.stub_missing_functions(true);
}
if path.join("use-built-in-libdl").is_file() {
linker = linker.use_built_in_libdl(true);
}
let linker = libs
.into_iter()
.try_fold(linker, |linker, (prefix, path, dl_openable)| {
let (name, wasm) = read_name_and_module(prefix, &path, &resolve, pkg_id)?;
Ok::<_, Error>(linker.library(&name, &wasm, dl_openable)?)
})?;
adapters
.try_fold(linker, |linker, path| {
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg_id)?;
Ok::<_, Error>(linker.adapter(&name, &wasm)?)
})?
.encode()
};
let component_path = path.join("component.wat");
let component_wit_path = path.join("component.wit.print");
let error_path = path.join("error.txt");
let bytes = match result {
Ok(bytes) => {
if test_case.starts_with("error-") {
bail!("expected an error but got success");
}
bytes
}
Err(err) => {
if !test_case.starts_with("error-") {
return Err(err);
}
assert_output(&format!("{err:#}"), &error_path)?;
return Ok(());
}
};
Validator::new_with_features(WasmFeatures::all())
.validate_all(&bytes)
.context("failed to validate component output")?;
let wat = wasmprinter::print_bytes(&bytes).context("failed to print bytes")?;
assert_output(&wat, &component_path)?;
let mut parser = Parser::new(0);
parser.set_features(WasmFeatures::all());
let (pkg, resolve) = match wit_component::decode_reader(bytes.as_slice())
.context("failed to decode resolve")?
{
DecodedWasm::WitPackage(..) => unreachable!(),
DecodedWasm::Component(resolve, world) => (resolve.worlds[world].package.unwrap(), resolve),
};
let mut printer = WitPrinter::default();
printer
.print(&resolve, pkg, &[])
.context("failed to print WIT")?;
let wit = printer.output.to_string();
assert_output(&wit, &component_wit_path)?;
UnresolvedPackageGroup::parse(&component_wit_path, &wit)
.context("failed to parse printed WIT")?;
// Check that the producer data got piped through properly
match Payload::from_binary(&bytes).unwrap() {
// Depends on the ComponentEncoder always putting the first module as the 0th child:
Payload::Component { children, .. } => match &children[0] {
Payload::Module(Metadata { producers, .. }) => {
let producers = producers.as_ref().expect("child module has producers");
let processed_by = producers
.get("processed-by")
.expect("child has processed-by section");
assert_eq!(
processed_by
.get("wit-component")
.expect("wit-component producer present"),
env!("CARGO_PKG_VERSION")
);
if module_path.is_file() {
assert_eq!(
processed_by
.get("my-fake-bindgen")
.expect("added bindgen field present"),
"123.45"
);
} else {
// Otherwise, we used `Linker`, which synthesizes the
// "main" module and thus won't have `my-fake-bindgen`
}
}
_ => panic!("expected child to be a module"),
},
_ => panic!("expected top level metadata of component"),
}
Ok(())
}
fn read_name_and_module(
prefix: &str,
path: &Path,
resolve: &Resolve,
pkg: PackageId,
) -> Result<(String, Vec<u8>)> {
let wasm = read_core_module(path, resolve, pkg)
.with_context(|| format!("failed to read core module at {path:?}"))?;
let stem = path.file_stem().unwrap().to_str().unwrap();
let name = if let Some(name) = fs::read_to_string(path)?
.lines()
.next()
.and_then(|line| line.strip_prefix(";; module name: "))
{
name.to_owned()
} else {
stem.trim_start_matches(prefix).to_owned()
};
Ok((name, wasm))
}
/// Parses the core wasm module at `path`, expected as a `*.wat` file.
///
/// The `resolve` and `pkg` are the parsed WIT package from this test's
/// directory and the `path`'s filename is used to find a WIT document of the
/// corresponding name which should have a world that `path` ascribes to.
fn read_core_module(path: &Path, resolve: &Resolve, pkg: PackageId) -> Result<Vec<u8>> {
let mut wasm = wat::parse_file(path)?;
let name = path.file_stem().and_then(|s| s.to_str()).unwrap();
let world = resolve
.select_world(&[pkg], Some(name))
.context("failed to select a world")?;
// Add this producer data to the wit-component metadata so we can make sure it gets through the
// translation:
let mut producers = wasm_metadata::Producers::empty();
producers.add("processed-by", "my-fake-bindgen", "123.45");
let encoded =
wit_component::metadata::encode(resolve, world, StringEncoding::UTF8, Some(&producers))?;
let section = wasm_encoder::CustomSection {
name: "component-type".into(),
data: Cow::Borrowed(&encoded),
};
wasm.push(section.id());
section.encode(&mut wasm);
Ok(wasm)
}
fn assert_output(contents: &str, path: &Path) -> Result<()> {
let contents = contents.replace("\r\n", "\n").replace(
concat!("\"", env!("CARGO_PKG_VERSION"), "\""),
"\"$CARGO_PKG_VERSION\"",
);
if std::env::var_os("BLESS").is_some() {
fs::write(path, contents)?;
} else {
match fs::read_to_string(path) {
Ok(expected) => {
assert_eq!(
expected.replace("\r\n", "\n").trim(),
contents.trim(),
"failed baseline comparison ({})",
path.display(),
);
}
Err(_) => {
panic!("expected {path:?} to contain\n{contents}");
}
}
}
Ok(())
}

View File

@@ -0,0 +1,3 @@
(module
(func (export "thunk"))
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
thunk-that-is-not-called: func();
}
}

View File

@@ -0,0 +1,60 @@
(component
(core module $main (;0;)
(type (;0;) (func))
(import "old" "thunk" (func (;0;) (type 0)))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func))
(export "thunk" (func 0))
(func (;0;) (type 0))
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func))
(table (;0;) 1 1 funcref)
(export "0" (func $adapt-old-thunk))
(export "$imports" (table 0))
(func $adapt-old-thunk (;0;) (type 0)
i32.const 0
call_indirect (type 0)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func))
(import "" "0" (func (;0;) (type 0)))
(import "" "$imports" (table (;0;) 1 1 funcref))
(elem (;0;) (i32.const 0) func 0)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-thunk (;0;)))
(core instance $old (;1;)
(export "thunk" (func $adapt-old-thunk))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(core instance $"#core-instance3 old" (@name "old") (;3;) (instantiate $wit-component:adapter:old))
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance3 old" "thunk" (core func $thunk (;1;)))
(core instance $fixup-args (;4;)
(export "$imports" (table $"shim table"))
(export "0" (func $thunk))
)
(core instance $fixup (;5;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,4 @@
package root:component;
world root {
}

View File

@@ -0,0 +1,3 @@
(module
(import "old" "thunk" (func))
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,4 @@
(module
(import "__main_module__" "the_entrypoint" (func $entry))
(export "entrypoint" (func $entry))
)

View File

@@ -0,0 +1,3 @@
world adapt-old {
export entrypoint: func();
}

View File

@@ -0,0 +1,32 @@
(component
(core module $main (;0;)
(type (;0;) (func))
(export "the_entrypoint" (func 0))
(func (;0;) (type 0))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func))
(import "__main_module__" "the_entrypoint" (func $entry (;0;) (type 0)))
(export "entrypoint" (func $entry))
)
(core instance $main (;0;) (instantiate $main))
(alias core export $main "the_entrypoint" (core func $the_entrypoint (;0;)))
(core instance $__main_module__ (;1;)
(export "the_entrypoint" (func $the_entrypoint))
)
(core instance $old (;2;) (instantiate $wit-component:adapter:old
(with "__main_module__" (instance $__main_module__))
)
)
(type (;0;) (func))
(alias core export $old "entrypoint" (core func $entrypoint (;1;)))
(func $entrypoint (;0;) (type 0) (canon lift (core func $entrypoint)))
(export $"#func1 entrypoint" (@name "entrypoint") (;1;) "entrypoint" (func $entrypoint))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,5 @@
package root:component;
world root {
export entrypoint: func();
}

View File

@@ -0,0 +1,3 @@
(module
(func (export "the_entrypoint"))
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,4 @@
(module
(import "__main_module__" "the_entrypoint" (func $entry))
(export "foo:foo/new#entrypoint" (func $entry))
)

View File

@@ -0,0 +1,7 @@
interface new {
entrypoint: func();
}
world adapt-old {
export new;
}

View File

@@ -0,0 +1,42 @@
(component
(core module $main (;0;)
(type (;0;) (func))
(export "the_entrypoint" (func 0))
(func (;0;) (type 0))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func))
(import "__main_module__" "the_entrypoint" (func $entry (;0;) (type 0)))
(export "foo:foo/new#entrypoint" (func $entry))
)
(core instance $main (;0;) (instantiate $main))
(alias core export $main "the_entrypoint" (core func $the_entrypoint (;0;)))
(core instance $__main_module__ (;1;)
(export "the_entrypoint" (func $the_entrypoint))
)
(core instance $old (;2;) (instantiate $wit-component:adapter:old
(with "__main_module__" (instance $__main_module__))
)
)
(type (;0;) (func))
(alias core export $old "foo:foo/new#entrypoint" (core func $foo:foo/new#entrypoint (;1;)))
(func $entrypoint (;0;) (type 0) (canon lift (core func $foo:foo/new#entrypoint)))
(component $foo:foo/new-shim-component (;0;)
(type (;0;) (func))
(import "import-func-entrypoint" (func (;0;) (type 0)))
(type (;1;) (func))
(export (;1;) "entrypoint" (func 0) (func (type 1)))
)
(instance $foo:foo/new-shim-instance (;0;) (instantiate $foo:foo/new-shim-component
(with "import-func-entrypoint" (func $entrypoint))
)
)
(export $foo:foo/new (;1;) "foo:foo/new" (instance $foo:foo/new-shim-instance))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,5 @@
package root:component;
world root {
export foo:foo/new;
}

View File

@@ -0,0 +1,3 @@
(module
(func (export "the_entrypoint"))
)

View File

@@ -0,0 +1,3 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,47 @@
(module
(import "__main_module__" "main" (func $main (param i32 i32)))
(import "new" "read" (func $read (param i32 i32)))
(global $sp (mut i32) (i32.const 0))
(func $start
(global.set $sp
(i32.mul
(memory.grow (i32.const 1))
(i32.const 65536)))
)
(func (export "entrypoint") (param i32 i32)
unreachable
)
(func (export "cabi_export_realloc") (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (export "read") (param $ptr i32) (param $len i32)
(local $fp i32)
(global.set $sp
(local.tee $fp
(i32.sub
(global.get $sp)
(i32.const 8)
)
)
)
(call $read (local.get $len) (local.get $fp))
(global.set $sp
(i32.add
(local.get $fp)
(i32.const 8)
)
)
)
(func (export "cabi_import_realloc") (param i32 i32 i32 i32) (result i32)
unreachable
)
)

View File

@@ -0,0 +1,6 @@
world adapt-old {
import new: interface {
read: func(amt: u32) -> list<u8>;
}
export entrypoint: func(args: list<string>);
}

View File

@@ -0,0 +1,131 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (list u8))
(type (;1;) (func (param "amt" u32) (result 0)))
(export (;0;) "read" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (param i32 i32)))
(import "old" "read" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "main" (func 1))
(export "memory" (memory 0))
(func (;1;) (type 0) (param $args i32) (param $argv i32))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "new" "read" (func $read (;0;) (type 0)))
(global $sp (;0;) (mut i32) i32.const 0)
(export "entrypoint" (func 1))
(export "cabi_export_realloc" (func 2))
(export "read" (func 3))
(export "cabi_import_realloc" (func 4))
(func (;1;) (type 0) (param i32 i32)
unreachable
)
(func (;2;) (type 1) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (;3;) (type 0) (param i32 i32)
(local i32)
global.get $sp
i32.const 8
i32.sub
local.tee 2
global.set $sp
local.get 1
local.get 2
call $read
local.get 2
i32.const 8
i32.add
global.set $sp
)
(func (;4;) (type 1) (param i32 i32 i32 i32) (result i32)
unreachable
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-read))
(export "1" (func $indirect-new-read))
(export "$imports" (table 0))
(func $adapt-old-read (;0;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-read (;1;) (type 1) (param i32 i32)
local.get 0
local.get 1
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-read (;0;)))
(core instance $old (;1;)
(export "read" (func $adapt-old-read))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-read (;1;)))
(core instance $new (;3;)
(export "read" (func $indirect-new-read))
)
(core instance $"#core-instance4 old" (@name "old") (;4;) (instantiate $wit-component:adapter:old
(with "new" (instance $new))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance4 old" "read" (core func $read (;2;)))
(alias export $new "read" (func $read (;0;)))
(alias core export $"#core-instance4 old" "cabi_import_realloc" (core func $realloc (;3;)))
(core func $"#core-func4 indirect-new-read" (@name "indirect-new-read") (;4;) (canon lower (func $read) (memory $memory) (realloc $realloc)))
(core instance $fixup-args (;5;)
(export "$imports" (table $"shim table"))
(export "0" (func $read))
(export "1" (func $"#core-func4 indirect-new-read"))
)
(core instance $fixup (;6;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(type (;1;) (list string))
(type (;2;) (func (param "args" 1)))
(alias core export $"#core-instance4 old" "entrypoint" (core func $entrypoint (;5;)))
(alias core export $"#core-instance4 old" "cabi_export_realloc" (core func $cabi_export_realloc (;6;)))
(func $entrypoint (;1;) (type 2) (canon lift (core func $entrypoint) (memory $memory) (realloc $cabi_export_realloc) string-encoding=utf8))
(export $"#func2 entrypoint" (@name "entrypoint") (;2;) "entrypoint" (func $entrypoint))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,9 @@
package root:component;
world root {
import new: interface {
read: func(amt: u32) -> list<u8>;
}
export entrypoint: func(args: list<string>);
}

View File

@@ -0,0 +1,8 @@
(module
(import "old" "read" (func (param i32 i32)))
(func (export "main") (param $args i32) (param $argv i32)
;; ...
)
(memory (export "memory") 1)
)

View File

@@ -0,0 +1,3 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,13 @@
(module
(import "__main_module__" "the_entrypoint" (func $entry))
(global $nargs (mut i32) (i32.const 0))
(func (export "entrypoint") (param $nargs i32)
(global.set $nargs (local.get $nargs))
call $entry
)
(func (export "nargs") (result i32)
global.get $nargs)
)

View File

@@ -0,0 +1,3 @@
world adapt-old {
export entrypoint: func(nargs: u32);
}

View File

@@ -0,0 +1,86 @@
(component
(core module $main (;0;)
(type (;0;) (func (result i32)))
(type (;1;) (func))
(import "old" "nargs" (func (;0;) (type 0)))
(export "the_entrypoint" (func 1))
(func (;1;) (type 1))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func))
(type (;1;) (func (param i32)))
(type (;2;) (func (result i32)))
(import "__main_module__" "the_entrypoint" (func $entry (;0;) (type 0)))
(global $nargs (;0;) (mut i32) i32.const 0)
(export "entrypoint" (func 1))
(export "nargs" (func 2))
(func (;1;) (type 1) (param i32)
local.get 0
global.set $nargs
call $entry
)
(func (;2;) (type 2) (result i32)
global.get $nargs
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(table (;0;) 1 1 funcref)
(export "0" (func $adapt-old-nargs))
(export "$imports" (table 0))
(func $adapt-old-nargs (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "$imports" (table (;0;) 1 1 funcref))
(elem (;0;) (i32.const 0) func 0)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-nargs (;0;)))
(core instance $old (;1;)
(export "nargs" (func $adapt-old-nargs))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "the_entrypoint" (core func $the_entrypoint (;1;)))
(core instance $__main_module__ (;3;)
(export "the_entrypoint" (func $the_entrypoint))
)
(core instance $"#core-instance4 old" (@name "old") (;4;) (instantiate $wit-component:adapter:old
(with "__main_module__" (instance $__main_module__))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance4 old" "nargs" (core func $nargs (;2;)))
(core instance $fixup-args (;5;)
(export "$imports" (table $"shim table"))
(export "0" (func $nargs))
)
(core instance $fixup (;6;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(type (;0;) (func (param "nargs" u32)))
(alias core export $"#core-instance4 old" "entrypoint" (core func $entrypoint (;3;)))
(func $entrypoint (;0;) (type 0) (canon lift (core func $entrypoint)))
(export $"#func1 entrypoint" (@name "entrypoint") (;1;) "entrypoint" (func $entrypoint))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,5 @@
package root:component;
world root {
export entrypoint: func(nargs: u32);
}

View File

@@ -0,0 +1,4 @@
(module
(import "old" "nargs" (func (result i32)))
(func (export "the_entrypoint"))
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,33 @@
;; This example represents adapting modules which use an early version of the
;; canonical ABI, back when `cabi_realloc` was named `canonical_abi_realloc` and
;; had a friend named `canonical_abi_free`. Such modules are pretty easy to
;; adapt since the adapter can use the main module's allocator for both lowering
;; and post-return functions.
;;
;; See https://github.com/fermyon/spin-componentize for a real-world example.
(module
(import "__main_module__" "canonical_abi_realloc" (func $realloc (param i32 i32 i32 i32) (result i32)))
(import "__main_module__" "canonical_abi_free" (func $free (param i32 i32 i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $allocation_state (mut i32) i32.const 0)
(func (export "foo:foo/new#foo") (result i32)
;; This is a dummy, non-working implementation, just to make gc.rs do what
;; we want, which is to treat this adapter as if it uses the main module's
;; allocator to allocate and free memory.
global.get $__stack_pointer
global.get $allocation_state
(call $realloc (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0))
unreachable
)
(func (export "cabi_post_foo:foo/new#foo") (param i32)
;; another dummy implementation
(call $free (i32.const 0) (i32.const 0) (i32.const 0))
unreachable
)
)

View File

@@ -0,0 +1,7 @@
interface new {
foo: func() -> string;
}
world adapt-old {
export new;
}

View File

@@ -0,0 +1,101 @@
(component
(core module $main (;0;)
(type (;0;) (func (param i32 i32 i32 i32) (result i32)))
(type (;1;) (func (param i32 i32 i32)))
(memory (;0;) 1)
(export "canonical_abi_realloc" (func 0))
(export "canonical_abi_free" (func 1))
(export "memory" (memory 0))
(func (;0;) (type 0) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (;1;) (type 1) (param i32 i32 i32)
unreachable
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32 i32 i32 i32) (result i32)))
(type (;1;) (func (param i32 i32 i32)))
(type (;2;) (func (result i32)))
(type (;3;) (func (param i32)))
(type (;4;) (func))
(import "__main_module__" "canonical_abi_realloc" (func $realloc (;0;) (type 0)))
(import "__main_module__" "canonical_abi_free" (func $free (;1;) (type 1)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $allocation_state (;1;) (mut i32) i32.const 0)
(export "foo:foo/new#foo" (func 2))
(export "cabi_post_foo:foo/new#foo" (func 3))
(func (;2;) (type 2) (result i32)
call $allocate_stack
global.get $__stack_pointer
global.get $allocation_state
i32.const 0
i32.const 0
i32.const 0
i32.const 0
call $realloc
unreachable
)
(func (;3;) (type 3) (param i32)
call $allocate_stack
i32.const 0
i32.const 0
i32.const 0
call $free
unreachable
)
(func $allocate_stack (;4;) (type 4)
global.get $allocation_state
i32.const 0
i32.eq
if ;; label = @1
i32.const 1
global.set $allocation_state
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $realloc
i32.const 65536
i32.add
global.set $__stack_pointer
i32.const 2
global.set $allocation_state
end
)
)
(core instance $main (;0;) (instantiate $main))
(alias core export $main "memory" (core memory $memory (;0;)))
(alias core export $main "canonical_abi_realloc" (core func $canonical_abi_realloc (;0;)))
(alias core export $main "canonical_abi_free" (core func $canonical_abi_free (;1;)))
(core instance $__main_module__ (;1;)
(export "canonical_abi_realloc" (func $canonical_abi_realloc))
(export "canonical_abi_free" (func $canonical_abi_free))
)
(core instance $old (;2;) (instantiate $wit-component:adapter:old
(with "__main_module__" (instance $__main_module__))
)
)
(type (;0;) (func (result string)))
(alias core export $old "foo:foo/new#foo" (core func $foo:foo/new#foo (;2;)))
(alias core export $old "cabi_post_foo:foo/new#foo" (core func $cabi_post_foo:foo/new#foo (;3;)))
(func $foo (;0;) (type 0) (canon lift (core func $foo:foo/new#foo) (memory $memory) string-encoding=utf8 (post-return $cabi_post_foo:foo/new#foo)))
(component $foo:foo/new-shim-component (;0;)
(type (;0;) (func (result string)))
(import "import-func-foo" (func (;0;) (type 0)))
(type (;1;) (func (result string)))
(export (;1;) "foo" (func 0) (func (type 1)))
)
(instance $foo:foo/new-shim-instance (;0;) (instantiate $foo:foo/new-shim-component
(with "import-func-foo" (func $foo))
)
)
(export $foo:foo/new (;1;) "foo:foo/new" (instance $foo:foo/new-shim-instance))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,5 @@
package root:component;
world root {
export foo:foo/new;
}

View File

@@ -0,0 +1,9 @@
(module
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (export "canonical_abi_free") (param i32 i32 i32)
unreachable
)
(memory (export "memory") 1)
)

View File

@@ -0,0 +1,3 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,7 @@
(module
(import "foo:foo/adapter-imports" "foo" (func $foo (param i32 i32)))
(func (export "adapter-bar") (param i32 i32)
(call $foo (i32.const 0) (i32.const 0))
)
(func (export "cabi_export_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
)

View File

@@ -0,0 +1,9 @@
interface adapter-imports {
foo: func(x: string);
}
world adapt-unused {
import adapter-imports;
export adapter-bar: func(x: string);
}

View File

@@ -0,0 +1,115 @@
(component
(type $ty-foo:foo/adapter-imports (;0;)
(instance
(type (;0;) (func (param "x" string)))
(export (;0;) "foo" (func (type 0)))
)
)
(import "foo:foo/adapter-imports" (instance $foo:foo/adapter-imports (;0;) (type $ty-foo:foo/adapter-imports)))
(type (;1;) (func (param "x" string)))
(import "foo" (func $foo (;0;) (type 1)))
(core module $main (;0;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func))
(import "$root" "foo" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "bar" (func 1))
(export "memory" (memory 0))
(func (;1;) (type 1)
unreachable
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:unused (;1;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "foo:foo/adapter-imports" "foo" (func $foo (;0;) (type 0)))
(export "adapter-bar" (func 1))
(export "cabi_export_realloc" (func 2))
(func (;1;) (type 0) (param i32 i32)
i32.const 0
i32.const 0
call $foo
)
(func (;2;) (type 1) (param i32 i32 i32 i32) (result i32)
unreachable
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (param i32 i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $indirect-$root-foo))
(export "1" (func $indirect-foo:foo/adapter-imports-foo))
(export "$imports" (table 0))
(func $indirect-$root-foo (;0;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 0
call_indirect (type 0)
)
(func $indirect-foo:foo/adapter-imports-foo (;1;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 1
call_indirect (type 0)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (param i32 i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 0)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $indirect-$root-foo (;0;)))
(core instance $$root (;1;)
(export "foo" (func $indirect-$root-foo))
)
(core instance $main (;2;) (instantiate $main
(with "$root" (instance $$root))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(alias core export $wit-component-shim-instance "1" (core func $indirect-foo:foo/adapter-imports-foo (;1;)))
(core instance $foo:foo/adapter-imports (;3;)
(export "foo" (func $indirect-foo:foo/adapter-imports-foo))
)
(core instance $unused (;4;) (instantiate $wit-component:adapter:unused
(with "foo:foo/adapter-imports" (instance $foo:foo/adapter-imports))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(core func $"#core-func2 indirect-$root-foo" (@name "indirect-$root-foo") (;2;) (canon lower (func $foo) (memory $memory) string-encoding=utf8))
(alias export $foo:foo/adapter-imports "foo" (func $"#func1 foo" (@name "foo") (;1;)))
(core func $"#core-func3 indirect-foo:foo/adapter-imports-foo" (@name "indirect-foo:foo/adapter-imports-foo") (;3;) (canon lower (func $"#func1 foo") (memory $memory) string-encoding=utf8))
(core instance $fixup-args (;5;)
(export "$imports" (table $"shim table"))
(export "0" (func $"#core-func2 indirect-$root-foo"))
(export "1" (func $"#core-func3 indirect-foo:foo/adapter-imports-foo"))
)
(core instance $fixup (;6;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(type (;2;) (func))
(alias core export $main "bar" (core func $bar (;4;)))
(func $bar (;2;) (type 2) (canon lift (core func $bar)))
(export $"#func3 bar" (@name "bar") (;3;) "bar" (func $bar))
(alias core export $unused "adapter-bar" (core func $adapter-bar (;5;)))
(alias core export $unused "cabi_export_realloc" (core func $cabi_export_realloc (;6;)))
(func $adapter-bar (;4;) (type 1) (canon lift (core func $adapter-bar) (memory $memory) (realloc $cabi_export_realloc) string-encoding=utf8))
(export $"#func5 adapter-bar" (@name "adapter-bar") (;5;) "adapter-bar" (func $adapter-bar))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,9 @@
package root:component;
world root {
import foo:foo/adapter-imports;
import foo: func(x: string);
export bar: func();
export adapter-bar: func(x: string);
}

View File

@@ -0,0 +1,5 @@
(module
(import "$root" "foo" (func (param i32 i32)))
(func (export "bar") unreachable)
(memory (export "memory") 1)
)

View File

@@ -0,0 +1,6 @@
package foo:foo;
world module {
import foo: func(x: string);
export bar: func();
}

View File

@@ -0,0 +1,76 @@
(module
(import "new" "get-two" (func $get_two (param i32)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (param i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $some_other_mutable_global (mut i32) i32.const 0)
;; `wit-component` should use this to track the status of a lazy stack
;; allocation:
(global $allocation_state (mut i32) i32.const 0)
;; This is a sample adapter which is adapting between ABI. This exact function
;; signature is imported by `module.wat` and we're implementing it here with a
;; canonical-abi function that returns two integers. The canonical ABI for
;; returning two integers is different than the ABI of this function, hence
;; the adapter here.
;;
;; The purpose of this test case is to exercise the `$__stack_pointer` global.
;; The stack pointer here needs to be initialized to something valid for
;; this adapter module which is done with an injected `start` function into
;; this adapter module when it's bundled into a component.
(func (export "get_sum") (result i32)
(local i32 i32)
;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))
;; First, allocate a page using $cabi_realloc and write to it. This tests
;; that we can use the main module's allocator if present (or else a
;; substitute synthesized by `wit-component`).
(local.set 0
(call $cabi_realloc
(i32.const 0)
(i32.const 0)
(i32.const 8)
(i32.const 65536)))
(i32.store (local.get 0) (i32.const 42))
(i32.store offset=65532 (local.get 0) (i32.const 42))
;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
;; function is saved in local 1.
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
;; Call the imported function which will return two u32 values into the
;; return pointer specified here, our stack frame.
local.get 1
call $get_two
;; Compute the result of this function by adding together the two return
;; values.
(i32.add
(i32.load (local.get 1))
(i32.load offset=4 (local.get 1)))
;; Test that if there is another mutable global in this module that it
;; doesn't affect the detection of the stack pointer. This extra mutable
;; global should not be initialized or tampered with as part of the
;; initialize-the-stack-pointer injected function
(global.set $some_other_mutable_global (global.get $some_other_mutable_global))
;; Restore the stack pointer to the value it was at prior to entering this
;; function.
local.get 0
global.set $__stack_pointer
)
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,174 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (tuple u32 u32))
(type (;1;) (func (result 0)))
(export (;0;) "get-two" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "old" "get_sum" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(export "cabi_realloc" (func $cabi_realloc))
(export "cabi_realloc_adapter" (func $cabi_realloc_adapter))
(func $cabi_realloc (;1;) (type 1) (param i32 i32 i32 i32) (result i32)
i32.const 123456789
)
(func $cabi_realloc_adapter (;2;) (type 1) (param i32 i32 i32 i32) (result i32)
i32.const 987654321
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(type (;2;) (func (result i32)))
(type (;3;) (func))
(import "env" "memory" (memory (;0;) 0))
(import "new" "get-two" (func $get_two (;0;) (type 0)))
(import "__main_module__" "cabi_realloc_adapter" (func $cabi_realloc (;1;) (type 1)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $some_other_mutable_global (;1;) (mut i32) i32.const 0)
(global $allocation_state (;2;) (mut i32) i32.const 0)
(export "get_sum" (func 2))
(func (;2;) (type 2) (result i32)
(local i32 i32)
call $allocate_stack
global.get $allocation_state
i32.const 2
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
local.set 0
local.get 0
i32.const 42
i32.store
local.get 0
i32.const 42
i32.store offset=65532
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
local.get 1
call $get_two
local.get 1
i32.load
local.get 1
i32.load offset=4
i32.add
global.get $some_other_mutable_global
global.set $some_other_mutable_global
local.get 0
global.set $__stack_pointer
)
(func $allocate_stack (;3;) (type 3)
global.get $allocation_state
i32.const 0
i32.eq
if ;; label = @1
i32.const 1
global.set $allocation_state
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
i32.const 65536
i32.add
global.set $__stack_pointer
i32.const 2
global.set $allocation_state
end
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-get_sum))
(export "1" (func $indirect-new-get-two))
(export "$imports" (table 0))
(func $adapt-old-get_sum (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-get-two (;1;) (type 1) (param i32)
local.get 0
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-get_sum (;0;)))
(core instance $old (;1;)
(export "get_sum" (func $adapt-old-get_sum))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(core instance $env (;3;)
(export "memory" (memory $memory))
)
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-get-two (;1;)))
(core instance $new (;4;)
(export "get-two" (func $indirect-new-get-two))
)
(alias core export $main "cabi_realloc_adapter" (core func $cabi_realloc_adapter (;2;)))
(core instance $__main_module__ (;5;)
(export "cabi_realloc_adapter" (func $cabi_realloc_adapter))
)
(core instance $"#core-instance6 old" (@name "old") (;6;) (instantiate $wit-component:adapter:old
(with "env" (instance $env))
(with "new" (instance $new))
(with "__main_module__" (instance $__main_module__))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance6 old" "get_sum" (core func $get_sum (;3;)))
(alias export $new "get-two" (func $get-two (;0;)))
(core func $"#core-func4 indirect-new-get-two" (@name "indirect-new-get-two") (;4;) (canon lower (func $get-two) (memory $memory)))
(core instance $fixup-args (;7;)
(export "$imports" (table $"shim table"))
(export "0" (func $get_sum))
(export "1" (func $"#core-func4 indirect-new-get-two"))
)
(core instance $fixup (;8;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,7 @@
package root:component;
world root {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,12 @@
(module
(import "old" "get_sum" (func (result i32)))
(func $cabi_realloc (param i32 i32 i32 i32) (result i32)
(i32.const 123456789)
)
(func $cabi_realloc_adapter (param i32 i32 i32 i32) (result i32)
(i32.const 987654321)
)
(memory (export "memory") 1)
(export "cabi_realloc" (func $cabi_realloc))
(export "cabi_realloc_adapter" (func $cabi_realloc_adapter))
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,68 @@
(module
(import "new" "get-two" (func $get_two (param i32)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (param i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $some_other_mutable_global (mut i32) i32.const 0)
;; This is a sample adapter which is adapting between ABI. This exact function
;; signature is imported by `module.wat` and we're implementing it here with a
;; canonical-abi function that returns two integers. The canonical ABI for
;; returning two integers is different than the ABI of this function, hence
;; the adapter here.
;;
;; The purpose of this test case is to exercise the `$__stack_pointer` global.
;; The stack pointer here needs to be initialized to something valid for
;; this adapter module which is done with an injected `start` function into
;; this adapter module when it's bundled into a component.
(func (export "get_sum") (result i32)
(local i32 i32)
;; First, allocate a page using $cabi_realloc and write to it. This tests
;; that we can use the main module's allocator if present (or else a
;; substitute synthesized by `wit-component`).
(local.set 0
(call $cabi_realloc
(i32.const 0)
(i32.const 0)
(i32.const 8)
(i32.const 65536)))
(i32.store (local.get 0) (i32.const 42))
(i32.store offset=65532 (local.get 0) (i32.const 42))
;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
;; function is saved in local 1.
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
;; Call the imported function which will return two u32 values into the
;; return pointer specified here, our stack frame.
local.get 1
call $get_two
;; Compute the result of this function by adding together the two return
;; values.
(i32.add
(i32.load (local.get 1))
(i32.load offset=4 (local.get 1)))
;; Test that if there is another mutable global in this module that it
;; doesn't affect the detection of the stack pointer. This extra mutable
;; global should not be initialized or tampered with as part of the
;; initialize-the-stack-pointer injected function
(global.set $some_other_mutable_global (global.get $some_other_mutable_global))
;; Restore the stack pointer to the value it was at prior to entering this
;; function.
local.get 0
global.set $__stack_pointer
)
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,216 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (tuple u32 u32))
(type (;1;) (func (result 0)))
(export (;0;) "get-two" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "old" "get_sum" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(export "cabi_realloc" (func $cabi_realloc))
(func $cabi_realloc (;1;) (type 1) (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
local.get 1
i32.ne
if ;; label = @1
unreachable
end
i32.const 65536
local.get 3
i32.ne
if ;; label = @1
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if ;; label = @1
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(type (;2;) (func (result i32)))
(type (;3;) (func (param i32 i32 i32 i32) (result i32)))
(type (;4;) (func))
(import "env" "memory" (memory (;0;) 0))
(import "new" "get-two" (func $get_two (;0;) (type 0)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (;1;) (type 1)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $some_other_mutable_global (;1;) (mut i32) i32.const 0)
(export "get_sum" (func 2))
(start $allocate_stack)
(func (;2;) (type 2) (result i32)
(local i32 i32)
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
local.set 0
local.get 0
i32.const 42
i32.store
local.get 0
i32.const 42
i32.store offset=65532
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
local.get 1
call $get_two
local.get 1
i32.load
local.get 1
i32.load offset=4
i32.add
global.get $some_other_mutable_global
global.set $some_other_mutable_global
local.get 0
global.set $__stack_pointer
)
(func $realloc_via_memory_grow (;3;) (type 3) (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
local.get 1
i32.ne
if ;; label = @1
unreachable
end
i32.const 65536
local.get 3
i32.ne
if ;; label = @1
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if ;; label = @1
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(func $allocate_stack (;4;) (type 4)
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $realloc_via_memory_grow
i32.const 65536
i32.add
global.set $__stack_pointer
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-get_sum))
(export "1" (func $indirect-new-get-two))
(export "$imports" (table 0))
(func $adapt-old-get_sum (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-get-two (;1;) (type 1) (param i32)
local.get 0
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-get_sum (;0;)))
(core instance $old (;1;)
(export "get_sum" (func $adapt-old-get_sum))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(core instance $env (;3;)
(export "memory" (memory $memory))
)
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-get-two (;1;)))
(core instance $new (;4;)
(export "get-two" (func $indirect-new-get-two))
)
(alias core export $main "cabi_realloc" (core func $cabi_realloc (;2;)))
(core instance $__main_module__ (;5;)
(export "cabi_realloc" (func $cabi_realloc))
)
(core instance $"#core-instance6 old" (@name "old") (;6;) (instantiate $wit-component:adapter:old
(with "env" (instance $env))
(with "new" (instance $new))
(with "__main_module__" (instance $__main_module__))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance6 old" "get_sum" (core func $get_sum (;3;)))
(alias export $new "get-two" (func $get-two (;0;)))
(core func $"#core-func4 indirect-new-get-two" (@name "indirect-new-get-two") (;4;) (canon lower (func $get-two) (memory $memory)))
(core instance $fixup-args (;7;)
(export "$imports" (table $"shim table"))
(export "0" (func $get_sum))
(export "1" (func $"#core-func4 indirect-new-get-two"))
)
(core instance $fixup (;8;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,7 @@
package root:component;
world root {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,38 @@
(module
(import "old" "get_sum" (func (result i32)))
;; Minimal realloc which only accepts new, page-sized allocations:
(func $cabi_realloc (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if
unreachable
end
i32.const 0
local.get 1
i32.ne
if
unreachable
end
i32.const 65536
local.get 3
i32.ne
if
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(memory (export "memory") 1)
(export "cabi_realloc" (func $cabi_realloc))
)

View File

@@ -0,0 +1,3 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,76 @@
(module
(import "new" "get-two" (func $get_two (param i32)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (param i32 i32 i32 i32) (result i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $some_other_mutable_global (mut i32) i32.const 0)
;; `wit-component` should use this to track the status of a lazy stack
;; allocation:
(global $allocation_state (mut i32) i32.const 0)
;; This is a sample adapter which is adapting between ABI. This exact function
;; signature is imported by `module.wat` and we're implementing it here with a
;; canonical-abi function that returns two integers. The canonical ABI for
;; returning two integers is different than the ABI of this function, hence
;; the adapter here.
;;
;; The purpose of this test case is to exercise the `$__stack_pointer` global.
;; The stack pointer here needs to be initialized to something valid for
;; this adapter module which is done with an injected `start` function into
;; this adapter module when it's bundled into a component.
(func (export "get_sum") (result i32)
(local i32 i32)
;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))
;; First, allocate a page using $cabi_realloc and write to it. This tests
;; that we can use the main module's allocator if present (or else a
;; substitute synthesized by `wit-component`).
(local.set 0
(call $cabi_realloc
(i32.const 0)
(i32.const 0)
(i32.const 8)
(i32.const 65536)))
(i32.store (local.get 0) (i32.const 42))
(i32.store offset=65532 (local.get 0) (i32.const 42))
;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
;; function is saved in local 1.
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
;; Call the imported function which will return two u32 values into the
;; return pointer specified here, our stack frame.
local.get 1
call $get_two
;; Compute the result of this function by adding together the two return
;; values.
(i32.add
(i32.load (local.get 1))
(i32.load offset=4 (local.get 1)))
;; Test that if there is another mutable global in this module that it
;; doesn't affect the detection of the stack pointer. This extra mutable
;; global should not be initialized or tampered with as part of the
;; initialize-the-stack-pointer injected function
(global.set $some_other_mutable_global (global.get $some_other_mutable_global))
;; Restore the stack pointer to the value it was at prior to entering this
;; function.
local.get 0
global.set $__stack_pointer
)
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,199 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (tuple u32 u32))
(type (;1;) (func (result 0)))
(export (;0;) "get-two" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "old" "get_sum" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(export "cabi_realloc" (func $cabi_realloc))
(func $cabi_realloc (;1;) (type 1) (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
local.get 1
i32.ne
if ;; label = @1
unreachable
end
i32.const 65536
local.get 3
i32.ne
if ;; label = @1
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if ;; label = @1
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(type (;2;) (func (result i32)))
(type (;3;) (func))
(import "env" "memory" (memory (;0;) 0))
(import "new" "get-two" (func $get_two (;0;) (type 0)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (;1;) (type 1)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $some_other_mutable_global (;1;) (mut i32) i32.const 0)
(global $allocation_state (;2;) (mut i32) i32.const 0)
(export "get_sum" (func 2))
(func (;2;) (type 2) (result i32)
(local i32 i32)
call $allocate_stack
global.get $allocation_state
i32.const 2
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
local.set 0
local.get 0
i32.const 42
i32.store
local.get 0
i32.const 42
i32.store offset=65532
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
local.get 1
call $get_two
local.get 1
i32.load
local.get 1
i32.load offset=4
i32.add
global.get $some_other_mutable_global
global.set $some_other_mutable_global
local.get 0
global.set $__stack_pointer
)
(func $allocate_stack (;3;) (type 3)
global.get $allocation_state
i32.const 0
i32.eq
if ;; label = @1
i32.const 1
global.set $allocation_state
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
i32.const 65536
i32.add
global.set $__stack_pointer
i32.const 2
global.set $allocation_state
end
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-get_sum))
(export "1" (func $indirect-new-get-two))
(export "$imports" (table 0))
(func $adapt-old-get_sum (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-get-two (;1;) (type 1) (param i32)
local.get 0
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-get_sum (;0;)))
(core instance $old (;1;)
(export "get_sum" (func $adapt-old-get_sum))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(core instance $env (;3;)
(export "memory" (memory $memory))
)
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-get-two (;1;)))
(core instance $new (;4;)
(export "get-two" (func $indirect-new-get-two))
)
(alias core export $main "cabi_realloc" (core func $cabi_realloc (;2;)))
(core instance $__main_module__ (;5;)
(export "cabi_realloc" (func $cabi_realloc))
)
(core instance $"#core-instance6 old" (@name "old") (;6;) (instantiate $wit-component:adapter:old
(with "env" (instance $env))
(with "new" (instance $new))
(with "__main_module__" (instance $__main_module__))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance6 old" "get_sum" (core func $get_sum (;3;)))
(alias export $new "get-two" (func $get-two (;0;)))
(core func $"#core-func4 indirect-new-get-two" (@name "indirect-new-get-two") (;4;) (canon lower (func $get-two) (memory $memory)))
(core instance $fixup-args (;7;)
(export "$imports" (table $"shim table"))
(export "0" (func $get_sum))
(export "1" (func $"#core-func4 indirect-new-get-two"))
)
(core instance $fixup (;8;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,7 @@
package root:component;
world root {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,38 @@
(module
(import "old" "get_sum" (func (result i32)))
;; Minimal realloc which only accepts new, page-sized allocations:
(func $cabi_realloc (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if
unreachable
end
i32.const 0
local.get 1
i32.ne
if
unreachable
end
i32.const 65536
local.get 3
i32.ne
if
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(memory (export "memory") 1)
(export "cabi_realloc" (func $cabi_realloc))
)

View File

@@ -0,0 +1,3 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,62 @@
(module
(import "new" "get-two" (func $get_two (param i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $some_other_mutable_global (mut i32) i32.const 0)
;; `wit-component` should use this to track the status of a lazy stack
;; allocation:
(global $allocation_state (mut i32) i32.const 0)
;; This is a sample adapter which is adapting between ABI. This exact function
;; signature is imported by `module.wat` and we're implementing it here with a
;; canonical-abi function that returns two integers. The canonical ABI for
;; returning two integers is different than the ABI of this function, hence
;; the adapter here.
;;
;; The purpose of this test case is to exercise the `$__stack_pointer` global.
;; The stack pointer here needs to be initialized to something valid for
;; this adapter module which is done with an injected `start` function into
;; this adapter module when it's bundled into a component.
(func (export "get_sum") (result i32)
(local i32 i32)
;; `wit-component` should have injected a call to a function that allocates
;; the stack and sets $allocation_state to 2
(if (i32.ne (global.get $allocation_state) (i32.const 2)) (then (unreachable)))
;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
;; function is saved in local 1.
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
;; Call the imported function which will return two u32 values into the
;; return pointer specified here, our stack frame.
local.get 1
call $get_two
;; Compute the result of this function by adding together the two return
;; values.
(i32.add
(i32.load (local.get 1))
(i32.load offset=4 (local.get 1)))
;; Test that if there is another mutable global in this module that it
;; doesn't affect the detection of the stack pointer. This extra mutable
;; global should not be initialized or tampered with as part of the
;; initialize-the-stack-pointer injected function
(global.set $some_other_mutable_global (global.get $some_other_mutable_global))
;; Restore the stack pointer to the value it was at prior to entering this
;; function.
local.get 0
global.set $__stack_pointer
)
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,187 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (tuple u32 u32))
(type (;1;) (func (result 0)))
(export (;0;) "get-two" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(import "old" "get_sum" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(export "cabi_realloc" (func $cabi_realloc))
(func $cabi_realloc (;1;) (type 1) (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
local.get 1
i32.ne
if ;; label = @1
unreachable
end
i32.const 65536
local.get 3
i32.ne
if ;; label = @1
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if ;; label = @1
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32)))
(type (;1;) (func (result i32)))
(type (;2;) (func (param i32 i32 i32 i32) (result i32)))
(type (;3;) (func))
(import "env" "memory" (memory (;0;) 0))
(import "new" "get-two" (func $get_two (;0;) (type 0)))
(import "__main_module__" "cabi_realloc" (func $cabi_realloc (;1;) (type 2)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $some_other_mutable_global (;1;) (mut i32) i32.const 0)
(global $allocation_state (;2;) (mut i32) i32.const 0)
(export "get_sum" (func 2))
(func (;2;) (type 1) (result i32)
(local i32 i32)
call $allocate_stack
global.get $allocation_state
i32.const 2
i32.ne
if ;; label = @1
unreachable
end
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
local.get 1
call $get_two
local.get 1
i32.load
local.get 1
i32.load offset=4
i32.add
global.get $some_other_mutable_global
global.set $some_other_mutable_global
local.get 0
global.set $__stack_pointer
)
(func $allocate_stack (;3;) (type 3)
global.get $allocation_state
i32.const 0
i32.eq
if ;; label = @1
i32.const 1
global.set $allocation_state
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $cabi_realloc
i32.const 65536
i32.add
global.set $__stack_pointer
i32.const 2
global.set $allocation_state
end
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-get_sum))
(export "1" (func $indirect-new-get-two))
(export "$imports" (table 0))
(func $adapt-old-get_sum (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-get-two (;1;) (type 1) (param i32)
local.get 0
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-get_sum (;0;)))
(core instance $old (;1;)
(export "get_sum" (func $adapt-old-get_sum))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(core instance $env (;3;)
(export "memory" (memory $memory))
)
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-get-two (;1;)))
(core instance $new (;4;)
(export "get-two" (func $indirect-new-get-two))
)
(alias core export $main "cabi_realloc" (core func $cabi_realloc (;2;)))
(core instance $__main_module__ (;5;)
(export "cabi_realloc" (func $cabi_realloc))
)
(core instance $"#core-instance6 old" (@name "old") (;6;) (instantiate $wit-component:adapter:old
(with "env" (instance $env))
(with "new" (instance $new))
(with "__main_module__" (instance $__main_module__))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance6 old" "get_sum" (core func $get_sum (;3;)))
(alias export $new "get-two" (func $get-two (;0;)))
(core func $"#core-func4 indirect-new-get-two" (@name "indirect-new-get-two") (;4;) (canon lower (func $get-two) (memory $memory)))
(core instance $fixup-args (;7;)
(export "$imports" (table $"shim table"))
(export "0" (func $get_sum))
(export "1" (func $"#core-func4 indirect-new-get-two"))
)
(core instance $fixup (;8;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,7 @@
package root:component;
world root {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,38 @@
(module
(import "old" "get_sum" (func (result i32)))
;; Minimal realloc which only accepts new, page-sized allocations:
(func $cabi_realloc (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if
unreachable
end
i32.const 0
local.get 1
i32.ne
if
unreachable
end
i32.const 65536
local.get 3
i32.ne
if
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(memory (export "memory") 1)
(export "cabi_realloc" (func $cabi_realloc))
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,54 @@
(module
(import "new" "get-two" (func $get_two (param i32)))
(import "env" "memory" (memory 0))
(global $__stack_pointer (mut i32) i32.const 0)
(global $some_other_mutable_global (mut i32) i32.const 0)
;; This is a sample adapter which is adapting between ABI. This exact function
;; signature is imported by `module.wat` and we're implementing it here with a
;; canonical-abi function that returns two integers. The canonical ABI for
;; returning two integers is different than the ABI of this function, hence
;; the adapter here.
;;
;; The purpose of this test case is to exercise the `$__stack_pointer` global.
;; The stack pointer here needs to be initialized to something valid for
;; this adapter module which is done with an injected `start` function into
;; this adapter module when it's bundled into a component.
(func (export "get_sum") (result i32)
(local i32 i32)
;; Allocate 8 bytes of stack space for the two u32 return values. The
;; original stack pointer is saved in local 0 and the stack frame for this
;; function is saved in local 1.
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
;; Call the imported function which will return two u32 values into the
;; return pointer specified here, our stack frame.
local.get 1
call $get_two
;; Compute the result of this function by adding together the two return
;; values.
(i32.add
(i32.load (local.get 1))
(i32.load offset=4 (local.get 1)))
;; Test that if there is another mutable global in this module that it
;; doesn't affect the detection of the stack pointer. This extra mutable
;; global should not be initialized or tampered with as part of the
;; initialize-the-stack-pointer injected function
(global.set $some_other_mutable_global (global.get $some_other_mutable_global))
;; Restore the stack pointer to the value it was at prior to entering this
;; function.
local.get 0
global.set $__stack_pointer
)
)

View File

@@ -0,0 +1,5 @@
world adapt-old {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,163 @@
(component
(type $ty-new (;0;)
(instance
(type (;0;) (tuple u32 u32))
(type (;1;) (func (result 0)))
(export (;0;) "get-two" (func (type 1)))
)
)
(import "new" (instance $new (;0;) (type $ty-new)))
(core module $main (;0;)
(type (;0;) (func (result i32)))
(import "old" "get_sum" (func (;0;) (type 0)))
(memory (;0;) 1)
(export "memory" (memory 0))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module $wit-component:adapter:old (;1;)
(type (;0;) (func (param i32)))
(type (;1;) (func (result i32)))
(type (;2;) (func (param i32 i32 i32 i32) (result i32)))
(type (;3;) (func))
(import "env" "memory" (memory (;0;) 0))
(import "new" "get-two" (func $get_two (;0;) (type 0)))
(global $__stack_pointer (;0;) (mut i32) i32.const 0)
(global $some_other_mutable_global (;1;) (mut i32) i32.const 0)
(export "get_sum" (func 1))
(start $allocate_stack)
(func (;1;) (type 1) (result i32)
(local i32 i32)
global.get $__stack_pointer
local.tee 0
i32.const 8
i32.sub
local.tee 1
global.set $__stack_pointer
local.get 1
call $get_two
local.get 1
i32.load
local.get 1
i32.load offset=4
i32.add
global.get $some_other_mutable_global
global.set $some_other_mutable_global
local.get 0
global.set $__stack_pointer
)
(func $realloc_via_memory_grow (;2;) (type 2) (param i32 i32 i32 i32) (result i32)
(local i32)
i32.const 0
local.get 0
i32.ne
if ;; label = @1
unreachable
end
i32.const 0
local.get 1
i32.ne
if ;; label = @1
unreachable
end
i32.const 65536
local.get 3
i32.ne
if ;; label = @1
unreachable
end
i32.const 1
memory.grow
local.tee 4
i32.const -1
i32.eq
if ;; label = @1
unreachable
end
local.get 4
i32.const 16
i32.shl
)
(func $allocate_stack (;3;) (type 3)
i32.const 0
i32.const 0
i32.const 8
i32.const 65536
call $realloc_via_memory_grow
i32.const 65536
i32.add
global.set $__stack_pointer
)
)
(core module $wit-component-shim-module (;2;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(table (;0;) 2 2 funcref)
(export "0" (func $adapt-old-get_sum))
(export "1" (func $indirect-new-get-two))
(export "$imports" (table 0))
(func $adapt-old-get_sum (;0;) (type 0) (result i32)
i32.const 0
call_indirect (type 0)
)
(func $indirect-new-get-two (;1;) (type 1) (param i32)
local.get 0
i32.const 1
call_indirect (type 1)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module $wit-component-fixup (;3;)
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance $wit-component-shim-instance (;0;) (instantiate $wit-component-shim-module))
(alias core export $wit-component-shim-instance "0" (core func $adapt-old-get_sum (;0;)))
(core instance $old (;1;)
(export "get_sum" (func $adapt-old-get_sum))
)
(core instance $main (;2;) (instantiate $main
(with "old" (instance $old))
)
)
(alias core export $main "memory" (core memory $memory (;0;)))
(core instance $env (;3;)
(export "memory" (memory $memory))
)
(alias core export $wit-component-shim-instance "1" (core func $indirect-new-get-two (;1;)))
(core instance $new (;4;)
(export "get-two" (func $indirect-new-get-two))
)
(core instance $"#core-instance5 old" (@name "old") (;5;) (instantiate $wit-component:adapter:old
(with "env" (instance $env))
(with "new" (instance $new))
)
)
(alias core export $wit-component-shim-instance "$imports" (core table $"shim table" (;0;)))
(alias core export $"#core-instance5 old" "get_sum" (core func $get_sum (;2;)))
(alias export $new "get-two" (func $get-two (;0;)))
(core func $"#core-func3 indirect-new-get-two" (@name "indirect-new-get-two") (;3;) (canon lower (func $get-two) (memory $memory)))
(core instance $fixup-args (;6;)
(export "$imports" (table $"shim table"))
(export "0" (func $get_sum))
(export "1" (func $"#core-func3 indirect-new-get-two"))
)
(core instance $fixup (;7;) (instantiate $wit-component-fixup
(with "" (instance $fixup-args))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)

View File

@@ -0,0 +1,7 @@
package root:component;
world root {
import new: interface {
get-two: func() -> tuple<u32, u32>;
}
}

View File

@@ -0,0 +1,4 @@
(module
(import "old" "get_sum" (func (result i32)))
(memory (export "memory") 1)
)

View File

@@ -0,0 +1,2 @@
package foo:foo;
world module {}

View File

@@ -0,0 +1,14 @@
(module
(import "new" "read" (func $read (param i32)))
(import "env" "memory" (memory 0))
(func (export "read") (param i32 i32)
i32.const 8
call $read
unreachable
)
(func (export "cabi_import_realloc") (param i32 i32 i32 i32) (result i32)
unreachable
)
)

Some files were not shown because too many files have changed in this diff Show More