diff --git a/.gitmodules b/.gitmodules
index ddc976053..3083181c9 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
 [submodule "tests/codegen/wasi-clocks"]
 	path = tests/codegen/wasi-clocks
 	url = https://github.com/WebAssembly/wasi-clocks
+[submodule "crates/cpp/tests/wasm-micro-runtime"]
+	path = crates/cpp/tests/wasm-micro-runtime
+	url = https://github.com/bytecodealliance/wasm-micro-runtime.git
diff --git a/Cargo.lock b/Cargo.lock
index 82afbdf98..7195d0240 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1535,7 +1535,10 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 name = "rust-xcrate-test"
 version = "0.0.0"
 dependencies = [
+ "futures",
+ "once_cell",
  "wit-bindgen",
+ "wit-bindgen-rt",
 ]
 
 [[package]]
@@ -1809,7 +1812,7 @@ name = "test-helpers"
 version = "0.0.0"
 dependencies = [
  "codegen-macro",
- "wasm-encoder 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wit-bindgen-core",
  "wit-component",
  "wit-parser 0.227.1",
@@ -1819,8 +1822,11 @@ dependencies = [
 name = "test-rust-wasm"
 version = "0.0.0"
 dependencies = [
+ "futures",
+ "once_cell",
  "rust-xcrate-test",
  "wit-bindgen",
+ "wit-bindgen-rt",
 ]
 
 [[package]]
@@ -2089,8 +2095,7 @@ dependencies = [
 [[package]]
 name = "wasm-compose"
 version = "0.227.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "606f006290e2964ba8075a8dd079358ede1e588971cb761720db43b3824f26e5"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
 dependencies = [
  "anyhow",
  "heck 0.4.1",
@@ -2102,9 +2107,9 @@ dependencies = [
  "serde_derive",
  "serde_yaml",
  "smallvec",
- "wasm-encoder 0.227.1",
- "wasmparser 0.227.1",
- "wat",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wat 1.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
@@ -2123,14 +2128,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822"
 dependencies = [
  "leb128fmt",
- "wasmparser 0.227.1",
+ "wasmparser 0.227.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.227.1"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
+dependencies = [
+ "leb128fmt",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
 name = "wasm-metadata"
 version = "0.227.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce1ef0faabbbba6674e97a56bee857ccddf942785a336c8b47b42373c922a91d"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
 dependencies = [
  "anyhow",
  "auditable-serde",
@@ -2141,8 +2154,8 @@ dependencies = [
  "serde_json",
  "spdx",
  "url",
- "wasm-encoder 0.227.1",
- "wasmparser 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
@@ -2164,6 +2177,16 @@ name = "wasmparser"
 version = "0.227.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0f51cad774fb3c9461ab9bccc9c62dfb7388397b5deda31bf40e8108ccd678b2"
+dependencies = [
+ "bitflags",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.227.1"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
 dependencies = [
  "bitflags",
  "hashbrown 0.15.2",
@@ -2235,7 +2258,7 @@ dependencies = [
  "wasmtime-slab",
  "wasmtime-versioned-export-macros",
  "wasmtime-winch",
- "wat",
+ "wat 1.227.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "windows-sys 0.52.0",
 ]
 
@@ -2490,7 +2513,19 @@ dependencies = [
  "leb128fmt",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.227.1",
+ "wasm-encoder 0.227.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wast"
+version = "227.0.1"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
+dependencies = [
+ "bumpalo",
+ "leb128fmt",
+ "memchr",
+ "unicode-width",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
@@ -2499,7 +2534,15 @@ version = "1.227.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b3d394d5bef7006ff63338d481ca10f1af76601e65ebdf5ed33d29302994e9cc"
 dependencies = [
- "wast 227.0.1",
+ "wast 227.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wat"
+version = "1.227.1"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
+dependencies = [
+ "wast 227.0.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
@@ -2710,6 +2753,17 @@ dependencies = [
  "wit-bindgen-rust-macro",
 ]
 
+[[package]]
+name = "wit-bindgen-bridge"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "heck 0.5.0",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
 [[package]]
 name = "wit-bindgen-c"
 version = "0.41.0"
@@ -2717,7 +2771,7 @@ dependencies = [
  "anyhow",
  "clap",
  "heck 0.5.0",
- "wasm-encoder 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wasm-metadata",
  "wit-bindgen-core",
  "wit-component",
@@ -2731,12 +2785,14 @@ dependencies = [
  "clap",
  "heck 0.5.0",
  "test-artifacts",
- "wasm-encoder 0.227.1",
- "wasmparser 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wasmtime",
  "wasmtime-wasi",
+ "wit-bindgen-bridge",
  "wit-bindgen-c",
  "wit-bindgen-core",
+ "wit-bindgen-cpp",
  "wit-bindgen-csharp",
  "wit-bindgen-markdown",
  "wit-bindgen-moonbit",
@@ -2755,6 +2811,21 @@ dependencies = [
  "wit-parser 0.227.1",
 ]
 
+[[package]]
+name = "wit-bindgen-cpp"
+version = "0.3.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "heck 0.5.0",
+ "test-helpers",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wasm-metadata",
+ "wit-bindgen-c",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
 [[package]]
 name = "wit-bindgen-csharp"
 version = "0.41.0"
@@ -2764,9 +2835,9 @@ dependencies = [
  "heck 0.5.0",
  "indexmap",
  "test-helpers",
- "wasm-encoder 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wasm-metadata",
- "wasmparser 0.227.1",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wit-bindgen-core",
  "wit-component",
  "wit-parser 0.227.1",
@@ -2851,9 +2922,9 @@ dependencies = [
  "toml",
  "wasi-preview1-component-adapter-provider",
  "wasm-compose",
- "wasm-encoder 0.227.1",
- "wasmparser 0.227.1",
- "wat",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wat 1.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wit-component",
  "wit-parser 0.227.1",
 ]
@@ -2861,8 +2932,7 @@ dependencies = [
 [[package]]
 name = "wit-component"
 version = "0.227.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "635c3adc595422cbf2341a17fb73a319669cc8d33deed3a48368a841df86b676"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
 dependencies = [
  "anyhow",
  "bitflags",
@@ -2871,10 +2941,10 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.227.1",
+ "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wasm-metadata",
- "wasmparser 0.227.1",
- "wat",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
+ "wat 1.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
  "wit-parser 0.227.1",
 ]
 
@@ -2899,8 +2969,7 @@ dependencies = [
 [[package]]
 name = "wit-parser"
 version = "0.227.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddf445ed5157046e4baf56f9138c124a0824d4d1657e7204d71886ad8ce2fc11"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -2911,7 +2980,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.227.1",
+ "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 4b23406c4..2c05ddb65 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,7 @@ repository = "https://github.com/bytecodealliance/wit-bindgen"
 [workspace.dependencies]
 anyhow = "1.0.72"
 bitflags = "2.3.3"
-heck = { version = "0.5" }
+heck =  { version = "0.5" }
 pulldown-cmark = { version = "0.9", default-features = false }
 clap = { version = "4.3.19", features = ["derive"] }
 indexmap = "2.0.0"
@@ -33,13 +33,13 @@ prettyplease = "0.2.20"
 syn = { version = "2.0.89", features = ["printing"] }
 futures = "0.3.31"
 
-wat = "1.227.0"
-wasmparser = "0.227.0"
-wasm-encoder = "0.227.0"
-wasm-metadata = "0.227.0"
-wit-parser = "0.227.0"
-wit-component = "0.227.0"
-wasm-compose = "0.227.0"
+wat = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wasmparser = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wasm-encoder = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wasm-metadata = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wit-parser = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wit-component = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
+wasm-compose = { git = "https://github.com/cpetig/wasm-tools", branch = "symmetric" }
 
 wit-bindgen-core = { path = 'crates/core', version = '0.41.0' }
 wit-bindgen-c = { path = 'crates/c', version = '0.41.0' }
@@ -50,6 +50,9 @@ wit-bindgen-moonbit = { path = 'crates/moonbit', version = '0.41.0' }
 wit-bindgen = { path = 'crates/guest-rust', version = '0.41.0', default-features = false }
 wit-bindgen-test = { path = 'crates/test', version = '0.41.0' }
 
+wit-bindgen-cpp = { path = 'crates/cpp', version = '0.3.0' }
+wit-bindgen-bridge = { path = 'crates/bridge', version = '0.1.0' }
+
 [[bin]]
 name = "wit-bindgen"
 
@@ -65,6 +68,8 @@ wit-bindgen-csharp = { workspace = true, features = ['clap'], optional = true }
 wit-bindgen-test = { workspace = true }
 wit-component = { workspace = true }
 wasm-encoder = { workspace = true }
+wit-bindgen-cpp = { workspace = true, features = ['clap'], optional = true }
+wit-bindgen-bridge = { workspace = true, features = ['clap'], optional = true }
 
 [features]
 default = [
@@ -72,11 +77,15 @@ default = [
   'rust',
   'markdown',
   'go',
-  'csharp',
+  # 'csharp',
+  'cpp',
+  'bridge',
   'moonbit',
   'async',
 ]
+bridge = ['dep:wit-bindgen-bridge']
 c = ['dep:wit-bindgen-c']
+cpp = ['dep:wit-bindgen-cpp']
 rust = ['dep:wit-bindgen-rust']
 markdown = ['dep:wit-bindgen-markdown']
 go = []
@@ -93,3 +102,15 @@ test-artifacts = { path = 'crates/test-rust-wasm/artifacts' }
 wit-parser = { workspace = true }
 wasmparser = { workspace = true }
 wasm-encoder = { workspace = true }
+
+#[patch.crates-io]
+#wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" }
+#wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" }
+#wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools" }
+#wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" }
+#wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools" }
+#wasmprinter = { git = "https://github.com/bytecodealliance/wasm-tools" }
+
+#[patch."https://github.com/cpetig/wasm-tools"]
+#wit-parser = { path = "../wasm-tools/crates/wit-parser" }
+#wasmparser = { path = "../wasm-tools/crates/wasmparser" }
diff --git a/README.md b/README.md
index 5fb9d1f90..6101d5691 100644
--- a/README.md
+++ b/README.md
@@ -385,6 +385,17 @@ Then, you can generate the bindings for your project:
 wit-bindgen-go generate <path-to-wit-pkg>
 ```
 
+### Guest: C++-17+
+
+This fork contains code to generate C++ code which uses the std types 
+optional, string, string_view, vector, expected to represent generic
+WIT types.
+
+This relies on wasi-SDK for guest compilation.
+
+A separate subcommand (cpp-host) will generate C++ host code for 
+WebAssembly micro runtime.
+
 ### Guest: MoonBit
 
 MoonBit can be compiled to WebAssembly using [its toolchain](https://moonbitlang.com/download):
@@ -477,6 +488,8 @@ components:
   generate Python source code to interact with the component using an embedding
   of Wasmtime for its core WebAssembly support.
 
+- C++-17+: see above chapter for WAMR host code generation.
+
 - Ruby: the [`wasmtime-rb`](https://github.com/bytecodealliance/wasmtime-rb)
   project has initial support for components since
   [v27](https://github.com/bytecodealliance/wasmtime-rb/releases/tag/v27.0.0).
diff --git a/TODO.md b/TODO.md
index 5d08da9df..48667fc4d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -58,3 +58,8 @@
 
 * Imported handle types show up as `any` in TS, unsure how to plumb through
   actual types to get that actually typed.
+
+# Cpp
+
+* Nested lists
+* Host: Strings inside records
diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml
new file mode 100644
index 000000000..b41890a92
--- /dev/null
+++ b/crates/bridge/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "wit-bindgen-bridge"
+authors = ["Christof Petig <christof.petig@arcor.de>"]
+version = "0.1.0"
+edition.workspace = true
+repository = 'https://github.com/cpetig/wit-bindgen'
+license = "Apache-2.0 WITH LLVM-exception"
+description = """
+Bridge binding (forwarding) generator for WIT and the component model, targeting w2c2 and wamr.
+"""
+
+[lib]
+doctest = false
+test = false
+
+[dependencies]
+wit-bindgen-core = { workspace = true }
+wit-component = { workspace = true }
+anyhow = { workspace = true }
+heck = { workspace = true }
+clap = { workspace = true, optional = true }
+
+#[dev-dependencies]
+#test-helpers = { path = '../test-helpers' }
diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs
new file mode 100644
index 000000000..dd39c83db
--- /dev/null
+++ b/crates/bridge/src/lib.rs
@@ -0,0 +1,240 @@
+use std::fmt::Write;
+use wit_bindgen_core::{
+    abi::{AbiVariant, WasmType},
+    make_external_symbol, uwriteln,
+    wit_parser::{self, Function, Resolve, TypeOwner, WorldId, WorldKey},
+    Source, WorldGenerator,
+};
+
+#[derive(Default)]
+struct Bridge {
+    src: Source,
+    opts: Opts,
+}
+
+#[derive(Default, Debug, Clone)]
+#[cfg_attr(feature = "clap", derive(clap::Args))]
+pub struct Opts {
+    /// Output bridge code for webassembly micro runtime
+    #[cfg_attr(feature = "clap", arg(long))]
+    wamr: bool,
+    /// w2c2 Instance name (derived from wasm file)
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = String::default()))]
+    instance: String,
+    /// w2c2 Include name
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = String::default()))]
+    include: String,
+}
+
+impl Opts {
+    pub fn build(&self) -> Box<dyn WorldGenerator> {
+        let mut r = Bridge::default();
+        r.opts = self.clone();
+        Box::new(r)
+    }
+}
+
+impl WorldGenerator for Bridge {
+    fn preprocess(&mut self, resolve: &Resolve, world: WorldId) {
+        let world = &resolve.worlds[world];
+        let name = if self.opts.instance.is_empty() {
+            world.name.clone()
+        } else {
+            self.opts.instance.clone()
+        };
+        let include = if self.opts.include.is_empty() {
+            name.clone() + ".h"
+        } else {
+            self.opts.include.clone()
+        };
+        uwriteln!(
+            self.src,
+            r#"
+        #include <stdint.h>
+        #include <stdio.h>
+        #include "{include}"
+        
+        static {name}Instance* instance;
+        static {name}Instance app_instance;
+        
+        void trap(Trap trap) {{
+            abort();
+        }}
+        
+        {name}Instance* get_app() {{
+            if (!instance) {{
+                {name}Instantiate(&app_instance, NULL);
+                instance = &app_instance;
+            }}
+            return instance;
+        }}
+        "#
+        );
+    }
+
+    fn import_interface(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        name: &WorldKey,
+        iface: wit_parser::InterfaceId,
+        _files: &mut wit_bindgen_core::Files,
+    ) -> anyhow::Result<()> {
+        let world = match name {
+            WorldKey::Name(n) => n.clone(),
+            WorldKey::Interface(i) => resolve.interfaces[*i].name.clone().unwrap_or_default(),
+        };
+        uwriteln!(self.src, "// Import IF {world}");
+
+        let mut gen = self.interface(resolve);
+        for (_name, func) in resolve.interfaces[iface].functions.iter() {
+            gen.generate_function(func, &TypeOwner::Interface(iface), AbiVariant::GuestImport);
+        }
+        Ok(())
+    }
+
+    fn export_interface(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        name: &WorldKey,
+        iface: wit_parser::InterfaceId,
+        _files: &mut wit_bindgen_core::Files,
+    ) -> anyhow::Result<()> {
+        let world = match name {
+            WorldKey::Name(n) => n.clone(),
+            WorldKey::Interface(i) => resolve.interfaces[*i].name.clone().unwrap_or_default(),
+        };
+        uwriteln!(self.src, "// Export IF {world}");
+
+        let mut gen = self.interface(resolve);
+        for (_name, func) in resolve.interfaces[iface].functions.iter() {
+            gen.generate_function(func, &TypeOwner::Interface(iface), AbiVariant::GuestExport);
+        }
+        Ok(())
+    }
+
+    fn import_funcs(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        worldid: wit_parser::WorldId,
+        funcs: &[(&str, &wit_parser::Function)],
+        _files: &mut wit_bindgen_core::Files,
+    ) {
+        let world = &resolve.worlds[worldid];
+        uwriteln!(self.src, "// Import Funcs {}", world.name);
+        let mut gen = self.interface(resolve);
+        for (_name, func) in funcs.iter() {
+            gen.generate_function(func, &TypeOwner::World(worldid), AbiVariant::GuestImport);
+        }
+    }
+
+    fn export_funcs(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        worldid: wit_parser::WorldId,
+        funcs: &[(&str, &wit_parser::Function)],
+        _files: &mut wit_bindgen_core::Files,
+    ) -> anyhow::Result<()> {
+        let world = &resolve.worlds[worldid];
+        uwriteln!(self.src, "// Export Funcs {}", world.name);
+        let mut gen = self.interface(resolve);
+        for (_name, func) in funcs.iter() {
+            gen.generate_function(func, &TypeOwner::World(worldid), AbiVariant::GuestExport);
+        }
+        Ok(())
+    }
+
+    fn import_types(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        world: wit_parser::WorldId,
+        _types: &[(&str, wit_parser::TypeId)],
+        _files: &mut wit_bindgen_core::Files,
+    ) {
+        let world = &resolve.worlds[world];
+        uwriteln!(self.src, "// Import Types {}", world.name);
+    }
+
+    fn finish(
+        &mut self,
+        resolve: &wit_parser::Resolve,
+        world: wit_parser::WorldId,
+        files: &mut wit_bindgen_core::Files,
+    ) -> anyhow::Result<()> {
+        let world = &resolve.worlds[world];
+        files.push(&format!("{}_bridge.c", world.name), self.src.as_bytes());
+        Ok(())
+    }
+}
+
+impl Bridge {
+    fn interface<'a>(&'a mut self, resolve: &'a Resolve) -> BridgeInterfaceGenerator<'a> {
+        BridgeInterfaceGenerator { gen: self, resolve }
+    }
+
+    fn wasm_type(&self, ty: WasmType, _var: TypeVariant) -> String {
+        match ty {
+            WasmType::I32 => todo!(),
+            WasmType::I64 => todo!(),
+            WasmType::F32 => todo!(),
+            WasmType::F64 => todo!(),
+            WasmType::Pointer => todo!(),
+            WasmType::PointerOrI64 => todo!(),
+            WasmType::Length => todo!(),
+        }
+    }
+
+    fn func_name(
+        &self,
+        resolve: &Resolve,
+        func: &Function,
+        owner: &TypeOwner,
+        variant: AbiVariant,
+    ) -> String {
+        let module_name = match owner {
+            TypeOwner::World(_) => todo!(),
+            TypeOwner::Interface(i) => resolve.interfaces[*i].name.clone().unwrap_or_default(),
+            TypeOwner::None => todo!(),
+        };
+        make_external_symbol(&module_name, &func.name, variant)
+    }
+}
+
+struct BridgeInterfaceGenerator<'a> {
+    gen: &'a mut Bridge,
+    resolve: &'a Resolve,
+}
+
+enum TypeVariant {
+    W2C2,
+    Native,
+}
+
+impl<'a> BridgeInterfaceGenerator<'a> {
+    fn generate_function(&mut self, func: &Function, owner: &TypeOwner, variant: AbiVariant) {
+        uwriteln!(self.gen.src, "// Func {} {:?}", func.name, variant);
+        let result_var = match variant {
+            AbiVariant::GuestImport => TypeVariant::W2C2,
+            AbiVariant::GuestExport => TypeVariant::Native,
+            AbiVariant::GuestImportAsync => todo!(),
+            AbiVariant::GuestExportAsync => todo!(),
+            AbiVariant::GuestExportAsyncStackful => todo!(),
+        };
+        let signature = self.resolve.wasm_signature(variant, func);
+        let return_via_pointer = signature.retptr;
+        let is_export = matches!(variant, AbiVariant::GuestExport);
+        if is_export {
+            self.gen
+                .src
+                .push_str(r#"__attribute__ ((visibility ("default"))) "#);
+        }
+        let res = if signature.results.is_empty() || return_via_pointer {
+            "void".into()
+        } else {
+            self.gen.wasm_type(signature.results[0], result_var)
+        };
+        self.gen.src.push_str(&res);
+        self.gen.src.push_str(" ");
+        let fname = self.gen.func_name(self.resolve, func, owner, variant);
+        self.gen.src.push_str(&fname);
+    }
+}
diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs
index 57b57fe7a..ac9fdccf3 100644
--- a/crates/c/src/lib.rs
+++ b/crates/c/src/lib.rs
@@ -1,4 +1,4 @@
-mod component_type_object;
+pub mod component_type_object;
 
 use anyhow::Result;
 use heck::*;
@@ -3136,7 +3136,7 @@ impl Source {
     }
 }
 
-fn wasm_type(ty: WasmType) -> &'static str {
+pub fn wasm_type(ty: WasmType) -> &'static str {
     match ty {
         WasmType::I32 => "int32_t",
         WasmType::I64 => "int64_t",
@@ -3298,4 +3298,4 @@ pub fn to_c_ident(name: &str) -> String {
     }
 }
 
-const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)";
+pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)";
diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs
index ebb620b6c..5d596a79b 100644
--- a/crates/core/src/abi.rs
+++ b/crates/core/src/abi.rs
@@ -492,6 +492,7 @@ def_instruction! {
         CallWasm {
             name: &'a str,
             sig: &'a WasmSignature,
+            module_prefix: &'a str,
         } : [sig.params.len()] => [sig.results.len()],
 
         /// Same as `CallWasm`, except the dual where an interface is being
@@ -639,6 +640,8 @@ pub enum LiftLower {
     /// SourceLanguage --lower-args--> Wasm; call; Wasm --lift-results--> SourceLanguage
     /// ```
     LowerArgsLiftResults,
+    /// Symmetric calling convention
+    Symmetric,
 }
 
 /// Trait for language implementors to use to generate glue code between native
@@ -738,7 +741,13 @@ pub fn call(
     bindgen: &mut impl Bindgen,
     async_: bool,
 ) {
-    Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func);
+    if matches!(lift_lower, LiftLower::Symmetric) {
+        let sig = wasm_signature_symmetric(resolve, variant, func, true);
+        Generator::new(resolve, variant, lift_lower, bindgen, async_)
+            .call_with_signature(func, sig);
+    } else {
+        Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func);
+    }
 }
 
 pub fn lower_to_memory<B: Bindgen>(
@@ -880,12 +889,18 @@ impl<'a, B: Bindgen> Generator<'a, B> {
     }
 
     fn call(&mut self, func: &Function) {
-        const MAX_FLAT_PARAMS: usize = 16;
-
         let sig = self.resolve.wasm_signature(self.variant, func);
+        self.call_with_signature(func, sig);
+    }
+
+    fn call_with_signature(&mut self, func: &Function, sig: WasmSignature) {
+        const MAX_FLAT_PARAMS: usize = 16;
 
-        match self.lift_lower {
-            LiftLower::LowerArgsLiftResults => {
+        let language_to_abi = matches!(self.lift_lower, LiftLower::LowerArgsLiftResults)
+            || (matches!(self.lift_lower, LiftLower::Symmetric)
+                && matches!(self.variant, AbiVariant::GuestImport));
+        match language_to_abi {
+            true => {
                 if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) {
                     unimplemented!("host-side code generation for async lift/lower not supported");
                 }
@@ -950,10 +965,12 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                     }
                 }
 
+                let mut retptr_oprnd = None;
                 if self.async_ {
                     let ElementInfo { size, align } =
                         self.bindgen.sizes().record(func.result.iter());
                     let ptr = self.bindgen.return_pointer(size, align);
+                    retptr_oprnd = Some(ptr.clone());
                     self.return_pointer = Some(ptr.clone());
                     self.stack.push(ptr);
 
@@ -975,10 +992,19 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                     self.emit(&Instruction::CallWasm {
                         name: &func.name,
                         sig: &sig,
+                        module_prefix: Default::default(),
                     });
                 }
 
-                if !(sig.retptr || self.async_) {
+                if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr {
+                    if let Some(ptr) = retptr_oprnd {
+                        self.read_results_from_memory(
+                            &func.result,
+                            ptr.clone(),
+                            Default::default(),
+                        );
+                    }
+                } else if !(sig.retptr || self.async_) {
                     // With no return pointer in use we can simply lift the
                     // result(s) of the function from the result of the core
                     // wasm function.
@@ -1024,7 +1050,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                     amt: usize::from(func.result.is_some()),
                 });
             }
-            LiftLower::LiftArgsLowerResults => {
+            false => {
                 if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) {
                     todo!("implement host-side support for async lift/lower");
                 }
@@ -1101,14 +1127,16 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                         self.lower(ty);
                     }
                 } else {
-                    match self.variant {
+                    match self.variant == AbiVariant::GuestImport
+                        || self.lift_lower == LiftLower::Symmetric
+                    {
                         // When a function is imported to a guest this means
                         // it's a host providing the implementation of the
                         // import. The result is stored in the pointer
                         // specified in the last argument, so we get the
                         // pointer here and then write the return value into
                         // it.
-                        AbiVariant::GuestImport => {
+                        true => {
                             self.emit(&Instruction::GetArg {
                                 nth: sig.params.len() - 1,
                             });
@@ -1121,7 +1149,7 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                         // value was stored at. Allocate some space here
                         // (statically) and then write the result into that
                         // memory, returning the pointer at the end.
-                        AbiVariant::GuestExport => {
+                        false => {
                             let ElementInfo { size, align } =
                                 self.bindgen.sizes().params(&func.result);
                             let ptr = self.bindgen.return_pointer(size, align);
@@ -1130,14 +1158,21 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                                 ptr.clone(),
                                 Default::default(),
                             );
-                            self.stack.push(ptr);
-                        }
+                            if !matches!(self.lift_lower, LiftLower::Symmetric) {
+                                self.stack.push(ptr);
+                            }
+                        } // AbiVariant::GuestImportAsync
+                          // | AbiVariant::GuestExportAsync
+                          // | AbiVariant::GuestExportAsyncStackful => {
+                          //     unreachable!()
+                          // }
+                    }
 
-                        AbiVariant::GuestImportAsync
-                        | AbiVariant::GuestExportAsync
-                        | AbiVariant::GuestExportAsyncStackful => {
-                            unreachable!()
-                        }
+                    if matches!(
+                        self.variant,
+                        AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync
+                    ) {
+                        unreachable!()
                     }
                 }
 
@@ -1440,6 +1475,8 @@ impl<'a, B: Bindgen> Generator<'a, B> {
         use Instruction::*;
 
         match *ty {
+            // Builtin types need different flavors of storage instructions
+            // depending on the size of the value written.
             Type::Bool => self.emit(&BoolFromI32),
             Type::S8 => self.emit(&S8FromI32),
             Type::U8 => self.emit(&U8FromI32),
@@ -1609,8 +1646,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
         use Instruction::*;
 
         match *ty {
-            // Builtin types need different flavors of storage instructions
-            // depending on the size of the value written.
             Type::Bool | Type::U8 | Type::S8 => {
                 self.lower_and_emit(ty, addr, &I32Store8 { offset })
             }
@@ -1821,7 +1856,11 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                 TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset),
 
                 TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => {
-                    self.emit_and_lift(ty, addr, &I32Load { offset })
+                    if matches!(self.lift_lower, LiftLower::Symmetric) {
+                        self.emit_and_lift(ty, addr, &PointerLoad { offset })
+                    } else {
+                        self.emit_and_lift(ty, addr, &I32Load { offset })
+                    }
                 }
 
                 TypeDefKind::Resource => {
@@ -2005,7 +2044,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
                 });
                 self.emit(&Instruction::GuestDeallocateString);
             }
-
             Type::Bool
             | Type::U8
             | Type::S8
@@ -2019,7 +2057,6 @@ impl<'a, B: Bindgen> Generator<'a, B> {
             | Type::F32
             | Type::F64
             | Type::ErrorContext => {}
-
             Type::Id(id) => match &self.resolve.types[id].kind {
                 TypeDefKind::Type(t) => self.deallocate(t, addr, offset),
 
@@ -2174,3 +2211,68 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast {
         }
     }
 }
+
+fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec<WasmType>) {
+    if let Type::Id(id) = ty {
+        if matches!(
+            &resolve.types[*id].kind,
+            TypeDefKind::Handle(_) | TypeDefKind::Stream(_) | TypeDefKind::Future(_)
+        ) {
+            vec.push(WasmType::Pointer);
+        } else {
+            resolve.push_flat(ty, vec);
+        }
+    } else {
+        resolve.push_flat(ty, vec);
+    }
+}
+
+// another hack
+pub fn wasm_signature_symmetric(
+    resolve: &Resolve,
+    variant: AbiVariant,
+    func: &Function,
+    symmetric: bool,
+) -> WasmSignature {
+    if !symmetric {
+        return resolve.wasm_signature(variant, func);
+    }
+    const MAX_FLAT_PARAMS: usize = 16;
+    const MAX_FLAT_RESULTS: usize = 1;
+
+    let mut params = Vec::new();
+    let mut indirect_params = false;
+    for (_, param) in func.params.iter() {
+        push_flat_symmetric(resolve, param, &mut params);
+    }
+
+    if params.len() > MAX_FLAT_PARAMS {
+        params.truncate(0);
+        params.push(WasmType::Pointer);
+        indirect_params = true;
+    }
+
+    let mut results = Vec::new();
+    for ty in func.result.iter() {
+        push_flat_symmetric(resolve, ty, &mut results)
+    }
+
+    let mut retptr = false;
+
+    // Rust/C don't support multi-value well right now, so if a function
+    // would have multiple results then instead truncate it. Imports take a
+    // return pointer to write into and exports return a pointer they wrote
+    // into.
+    if results.len() > MAX_FLAT_RESULTS {
+        retptr = true;
+        results.truncate(0);
+        params.push(WasmType::Pointer);
+    }
+
+    WasmSignature {
+        params,
+        indirect_params,
+        results,
+        retptr,
+    }
+}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
index c52449982..6752aae34 100644
--- a/crates/core/src/lib.rs
+++ b/crates/core/src/lib.rs
@@ -12,6 +12,7 @@ mod types;
 pub use types::{TypeInfo, Types};
 mod path;
 pub use path::name_package_module;
+pub mod symmetric;
 
 #[derive(Default, Copy, Clone, PartialEq, Eq, Debug)]
 pub enum Direction {
@@ -130,6 +131,8 @@ pub trait WorldGenerator {
         files: &mut Files,
     );
     fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) -> Result<()>;
+    // modify resolve by command line options
+    fn apply_resolve_options(&mut self, _resolve: &mut Resolve, _world: &mut WorldId) {}
 }
 
 /// This is a possible replacement for the `Generator` trait above, currently
@@ -231,3 +234,52 @@ pub fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId {
         }
     }
 }
+
+fn hexdigit(v: u32) -> char {
+    if v < 10 {
+        char::from_u32(('0' as u32) + v).unwrap()
+    } else {
+        char::from_u32(('A' as u32) - 10 + v).unwrap()
+    }
+}
+
+/// encode symbol as alphanumeric by hex-encoding special characters
+pub fn make_external_component(input: &str) -> String {
+    input
+        .chars()
+        .map(|c| match c {
+            'A'..='Z' | 'a'..='z' | '0'..='9' | '_' => {
+                let mut s = String::new();
+                s.push(c);
+                s
+            }
+            '-' => {
+                let mut s = String::new();
+                s.push('_');
+                s
+            }
+            _ => {
+                let mut s = String::from("X");
+                s.push(hexdigit((c as u32 & 0xf0) >> 4));
+                s.push(hexdigit(c as u32 & 0xf));
+                s
+            }
+        })
+        .collect()
+}
+
+/// encode symbol as alphanumeric by hex-encoding special characters
+pub fn make_external_symbol(module_name: &str, name: &str, variant: abi::AbiVariant) -> String {
+    if module_name.is_empty() || module_name == "$root" {
+        make_external_component(name)
+    } else {
+        let mut res = make_external_component(module_name);
+        res.push_str(if matches!(variant, abi::AbiVariant::GuestExport) {
+            "X23" // Hash character
+        } else {
+            "X00" // NUL character (some tools use '.' for display)
+        });
+        res.push_str(&make_external_component(name));
+        res
+    }
+}
diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs
new file mode 100644
index 000000000..6d73fbca2
--- /dev/null
+++ b/crates/core/src/symmetric.rs
@@ -0,0 +1,179 @@
+// helper functions for symmetric ABI
+
+use wit_parser::{Resolve, Type, TypeDefKind};
+
+// figure out whether deallocation is needed in the caller
+fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool {
+    match tp {
+        Type::Bool
+        | Type::U8
+        | Type::U16
+        | Type::U32
+        | Type::U64
+        | Type::S8
+        | Type::S16
+        | Type::S32
+        | Type::S64
+        | Type::F32
+        | Type::F64
+        | Type::Char => false,
+        Type::String => true,
+        Type::Id(id) => match &resolve.types[*id].kind {
+            TypeDefKind::Enum(_) => false,
+            TypeDefKind::Record(r) => r.fields.iter().any(|f| needs_dealloc2(resolve, &f.ty)),
+            TypeDefKind::Resource => false,
+            TypeDefKind::Handle(_) => false,
+            TypeDefKind::Flags(_) => false,
+            TypeDefKind::Tuple(t) => t.types.iter().any(|f| needs_dealloc2(resolve, f)),
+            TypeDefKind::Variant(_) => todo!(),
+            TypeDefKind::Option(tp) => needs_dealloc2(resolve, tp),
+            TypeDefKind::Result(r) => {
+                r.ok.as_ref()
+                    .map_or(false, |tp| needs_dealloc2(resolve, tp))
+                    || r.err
+                        .as_ref()
+                        .map_or(false, |tp| needs_dealloc2(resolve, tp))
+            }
+            TypeDefKind::List(_l) => true,
+            TypeDefKind::Future(_) => todo!(),
+            TypeDefKind::Stream(_) => todo!(),
+            TypeDefKind::Type(tp) => needs_dealloc2(resolve, tp),
+            TypeDefKind::Unknown => false,
+        },
+        Type::ErrorContext => todo!(),
+    }
+}
+
+pub fn needs_dealloc(resolve: &Resolve, args: &[(String, Type)]) -> bool {
+    for (_n, t) in args {
+        if needs_dealloc2(resolve, t) {
+            return true;
+        }
+    }
+    return false;
+}
+
+fn has_non_canonical_list2(resolve: &Resolve, ty: &Type, maybe: bool) -> bool {
+    match ty {
+        Type::Bool
+        | Type::U8
+        | Type::U16
+        | Type::U32
+        | Type::U64
+        | Type::S8
+        | Type::S16
+        | Type::S32
+        | Type::S64
+        | Type::F32
+        | Type::F64
+        | Type::Char
+        | Type::String => false,
+        Type::Id(id) => match &resolve.types[*id].kind {
+            TypeDefKind::Record(r) => r
+                .fields
+                .iter()
+                .any(|field| has_non_canonical_list2(resolve, &field.ty, maybe)),
+            TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Flags(_) => false,
+            TypeDefKind::Tuple(t) => t
+                .types
+                .iter()
+                .any(|ty| has_non_canonical_list2(resolve, ty, maybe)),
+            TypeDefKind::Variant(var) => var.cases.iter().any(|case| {
+                case.ty
+                    .as_ref()
+                    .map_or(false, |ty| has_non_canonical_list2(resolve, ty, maybe))
+            }),
+            TypeDefKind::Enum(_) => false,
+            TypeDefKind::Option(ty) => has_non_canonical_list2(resolve, ty, maybe),
+            TypeDefKind::Result(res) => {
+                res.ok
+                    .as_ref()
+                    .map_or(false, |ty| has_non_canonical_list2(resolve, ty, maybe))
+                    || res
+                        .err
+                        .as_ref()
+                        .map_or(false, |ty| has_non_canonical_list2(resolve, ty, maybe))
+            }
+            TypeDefKind::List(ty) => {
+                if maybe {
+                    true
+                } else {
+                    has_non_canonical_list2(resolve, ty, true)
+                }
+            }
+            TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false,
+            TypeDefKind::Type(ty) => has_non_canonical_list2(resolve, ty, maybe),
+            TypeDefKind::Unknown => false,
+        },
+        Type::ErrorContext => todo!(),
+    }
+}
+
+// fn has_non_canonical_list(resolve: &Resolve, results: &Results) -> bool {
+//     match results {
+//         Results::Named(vec) => vec
+//             .iter()
+//             .any(|(_, ty)| has_non_canonical_list2(resolve, ty, false)),
+//         Results::Anon(one) => has_non_canonical_list2(resolve, &one, false),
+//     }
+// }
+
+pub fn has_non_canonical_list(resolve: &Resolve, args: &[(String, Type)]) -> bool {
+    args.iter()
+        .any(|(_, ty)| has_non_canonical_list2(resolve, ty, false))
+}
+
+fn has_non_canonical_list_rust2(resolve: &Resolve, ty: &Type) -> bool {
+    match ty {
+        Type::Bool
+        | Type::U8
+        | Type::U16
+        | Type::U32
+        | Type::U64
+        | Type::S8
+        | Type::S16
+        | Type::S32
+        | Type::S64
+        | Type::F32
+        | Type::F64
+        | Type::Char
+        | Type::String => false,
+        Type::Id(id) => match &resolve.types[*id].kind {
+            TypeDefKind::Record(r) => r
+                .fields
+                .iter()
+                .any(|field| has_non_canonical_list_rust2(resolve, &field.ty)),
+            TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Flags(_) => false,
+            TypeDefKind::Tuple(t) => t
+                .types
+                .iter()
+                .any(|ty| has_non_canonical_list_rust2(resolve, ty)),
+            TypeDefKind::Variant(var) => var.cases.iter().any(|case| {
+                case.ty
+                    .as_ref()
+                    .map_or(false, |ty| has_non_canonical_list_rust2(resolve, ty))
+            }),
+            TypeDefKind::Enum(_) => false,
+            TypeDefKind::Option(ty) => has_non_canonical_list_rust2(resolve, ty),
+            TypeDefKind::Result(res) => {
+                res.ok
+                    .as_ref()
+                    .map_or(false, |ty| has_non_canonical_list_rust2(resolve, ty))
+                    || res
+                        .err
+                        .as_ref()
+                        .map_or(false, |ty| has_non_canonical_list_rust2(resolve, ty))
+            }
+            TypeDefKind::List(_ty) => true,
+            TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false,
+            TypeDefKind::Type(ty) => has_non_canonical_list_rust2(resolve, ty),
+            TypeDefKind::Unknown => false,
+        },
+        Type::ErrorContext => todo!(),
+    }
+}
+
+pub fn has_non_canonical_list_rust(resolve: &Resolve, args: &[(String, Type)]) -> bool {
+    args.iter()
+        .any(|(_, ty)| has_non_canonical_list_rust2(resolve, ty))
+}
diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml
new file mode 100644
index 000000000..b9d9a5139
--- /dev/null
+++ b/crates/cpp/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "wit-bindgen-cpp"
+authors = ["Christof Petig <christof.petig@arcor.de>"]
+version = "0.3.0"
+edition.workspace = true
+repository = 'https://github.com/cpetig/wit-bindgen'
+license = "Apache-2.0 WITH LLVM-exception"
+description = """
+C++ guest and host binding generator for WIT and the component model.
+"""
+
+[lib]
+doctest = false
+test = false
+
+[dependencies]
+wit-bindgen-core = { workspace = true }
+wit-component = { workspace = true }
+wasm-encoder = { workspace = true }
+wasm-metadata = { workspace = true }
+wit-bindgen-c = { workspace = true }
+anyhow = { workspace = true }
+heck = { workspace = true }
+clap = { workspace = true, optional = true }
+# for now 
+#wit-bindgen-rust = { workspace = true }
+#wit-bindgen-cpp-host = { workspace = true }
+
+[dev-dependencies]
+test-helpers = { path = '../test-helpers' }
diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md
new file mode 100644
index 000000000..c750a906c
--- /dev/null
+++ b/crates/cpp/DESIGN.md
@@ -0,0 +1,98 @@
+# Type mapping
+
+| Code | Environment |
+| --- | --- |
+| G-- | guest side |
+| H-- | host side |
+| -I- | guest-import (guest calls) |
+| -E- | guest-export (host calls) |
+| --A | argument |
+| --R | result |
+| --S | in struct |
+
+| mode | |
+| --- | --- |
+| v | passed by value |
+| t | owernership transferred |
+| p | cabi_post_ cleans up |
+
+| API | | | ABI | |
+| --- | --- | --- | --- | --- |
+| 🕸 | old | | 📘 | canonical |
+| 💎 | new | | 🪞 | symmetric |
+
+| Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason |
+| --- | --- | --- | --- | --- | --- | --- |
+| GIA | v | string | &str[^1] | string_view (17) | addr, len | |
+| | | list | &[T] | wit::span [^5] | addr, len | |
+| | | tuple | (...) | std::tuple | 0, 1, ...| |
+| | | tuple<string, list> | (&str, &[T]) | std::tuple<...> | a,l,a,l |
+| | | record{string, list} | &T | T const& | a,l,a,l |
+| | | large-struct (>16 args) | &T | T const& | &t |
+| | | result<string,list> | Result<&str, &[]> | std::expected<string_view, span> | d,a,l |
+| | | option\<string> | Option\<&str> | optional<string_view> const& | d,a,l|
+| | | list\<resrc> | &[\&Resrc]? | vector<string_view> const& | a,l|
+| GIR | t | string | String | wit::string[^2] | &(addr, len) [^8] | |
+| | | list | Vec | wit::vector | &(a,l) |
+| | | result<string,list> | Result<String, Vec> | std::expected<wit::string, wit::vector> | &(d,a,l) |
+| GEA | t | string | String | 🕸 wit::string&& | addr, len |
+| | | | | 💎 string_view | |
+| | | result<string,list> | Result<String, Vec> | 🕸 std::expected<wit::string, wit::vector>&& | d,a,l |
+| | | | | 💎 std::expected<string_view, wit::span> | |
+| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] |
+| | | | | | 🪞 &(a,l) |
+| | | result<string,list> | Result<String, Vec> | std::expected<wit::string, wit::vector> | 📘 -> &(d,a,l) cabi_post |
+| --S | ? | string | String | wit::string | addr, len |
+| HIA | v | string | | string_view | a,l |
+| HIR | t | string | | wit::string[^3] | &(a,l) |
+| HEA | t | string | | 🕸 wit::string[^4] | a,l |
+| | | | | 💎 string_view [^6] | |
+| HER | p | string | | 🕸 wit::guest_owned<string_view> | 📘 -> &(a,l) |
+| | | | | 💎 wit::string [^6] | 🪞 &(a,l) |
+
+[^1]: The host never frees memory (is never passed ownership)!
+
+[^2]: A wit::string is identical to the canonical representation, so it can be part of structures. On the guest a wit::string owns the memory and frees it after use.
+On the host a wit::string can be constructed(=allocated) with an exec_env argument. Thus, without an exec_env a wit::string on the host is inaccessible.
+Complex (non-POD) struct elements on the host will need exec_env to decode or construct.
+
+[^3]: A wit::string requires exec_env inside the host implementation. ~~Perhaps a flexible type (either std::string or wit::string would be possible), or make this a generation option?~~ std::string requires a copy, wit::string requires passing exec_env to the method (which is necessary for methods anyway).
+
+[^4]: A host side wit::string doesn't own the data (not free in dtor), thus no move semantics.
+
+[^5]: std::span requires C++-20, this alias should give minimal functionality with older compiler targets.
+
+[^6]: Not implemented, for now symmetric is priority
+
+[^7]: Here the callee (guest) allocates the memory for the set on its side
+
+[^8]: Caller passes address of the return object as argument
+
+## [Symmetric ABI](https://github.com/WebAssembly/component-model/issues/386)
+
+The idea is to directly connect (link) components to each other.
+
+Thus imported and exported functions and resources need to be compatible
+at the ABI level.
+
+For now for functions the guest import convention is used in both directions:
+
+- The imported function ABI is used with the following properties
+
+  - (unchanged) List and string arguments are passed as Views, no free
+    required, lifetime is constrained until the end of the call
+
+  - (unchanged) Owned resources in arguments or results pass ownership
+    to the callee
+
+  - (unchanged) If there are too many (>1) flat results, a local
+    uninitialized ret_area is passed via the last argument
+
+  - (unchanged) Returned objects are owned.
+    For functional safety, i.e. avoiding all
+    allocations in the hot path, the hope is with [#385](https://github.com/WebAssembly/component-model/issues/385).
+
+- The imported resource ABI is used also for exporting
+  with one modification:
+
+   Resource IDs become usize, so you can optimize the resource table away.
diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h
new file mode 100644
index 000000000..68957569b
--- /dev/null
+++ b/crates/cpp/helper-types/wit-common.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <assert.h>
+#include <map>
+#include <optional>
+#include <stddef.h> // size_t
+#include <stdint.h>
+#if __cplusplus > 202001L
+#include <span>
+#else
+#include <vector>
+#endif
+
+namespace wit {
+#if __cplusplus > 202001L
+using std::span;
+#else
+/// Minimal span (vector view) implementation for older C++ environments
+template <class T> class span {
+  T const *address;
+  size_t length;
+
+public:
+  T const *data() const { return address; }
+  size_t size() const { return length; }
+
+  typedef T const *const_iterator;
+
+  const_iterator begin() const { return address; }
+  const_iterator end() const { return address + length; }
+  bool empty() const { return !length; }
+  T const &operator[](size_t index) const { return address[index]; }
+  span(T *a, size_t l) : address(a), length(l) {}
+  // create from any compatible vector (borrows data!)
+  template <class U>
+  span(std::vector<U> const &vec) : address(vec.data()), length(vec.size()) {}
+};
+#endif
+
+/// @brief Helper class to map between IDs and resources
+/// @tparam R Type of the Resource
+template <class R> class ResourceTable {
+  static std::map<int32_t, R> resources;
+
+public:
+  static R *lookup_resource(int32_t id) {
+    auto result = resources.find(id);
+    return result == resources.end() ? nullptr : &result->second;
+  }
+  static int32_t store_resource(R &&value) {
+    auto last = resources.rbegin();
+    int32_t id = last == resources.rend() ? 0 : last->first + 1;
+    resources.insert(std::pair<int32_t, R>(id, std::move(value)));
+    return id;
+  }
+  static std::optional<R> remove_resource(int32_t id) {
+    auto iter = resources.find(id);
+    std::optional<R> result;
+    if (iter != resources.end()) {
+      result = std::move(iter->second);
+      resources.erase(iter);
+    }
+    return std::move(result);
+  }
+};
+
+/// @brief Replaces void in the error position of a result
+struct Void {};
+} // namespace wit
diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h
new file mode 100644
index 000000000..e1d44f8fa
--- /dev/null
+++ b/crates/cpp/helper-types/wit-guest.h
@@ -0,0 +1,213 @@
+#pragma once
+#include "wit-common.h"
+#include <malloc.h>
+#include <memory> // unique_ptr
+#include <stdint.h>
+#include <string>
+#include <string_view>
+#include <string.h> // memcpy
+#include <stdlib.h> // free
+#include <new>
+
+namespace wit {
+/// A string in linear memory, freed unconditionally using free
+///
+/// A normal C++ string makes no guarantees about where the characters
+/// are stored and how this is freed.
+class string {
+  uint8_t const *data_;
+  size_t length;
+  // C++ is horrible!
+  //constexpr uint8_t const *const empty_ptr = (uint8_t const *)1;
+  static uint8_t const* empty_ptr() { return (uint8_t const *)1; }
+
+public:
+  // this constructor is helpful for creating vector<string>
+  string(string const &b) : string(string::from_view(b.get_view())) {}
+  string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; }
+  string &operator=(string const &) = delete;
+  string &operator=(string &&b) {
+    if (data_ && data_!=empty_ptr()) {
+      free(const_cast<uint8_t *>(data_));
+    }
+    data_ = b.data_;
+    length = b.length;
+    b.data_ = nullptr;
+    return *this;
+  }
+  string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {}
+  char const *data() const { return (char const *)data_; }
+  size_t size() const { return length; }
+  bool empty() const { return !length; }
+  ~string() {
+    if (data_ && data_!=empty_ptr()) {
+      free(const_cast<uint8_t *>(data_));
+    }
+  }
+  // leak the memory
+  void leak() { data_ = nullptr; }
+  // typically called by post
+  static void drop_raw(void *ptr) { free(ptr); }
+  std::string_view get_view() const {
+    return std::string_view((const char *)data_, length);
+  }
+  std::string to_string() const {
+    return std::string((const char *)data_, length);
+  }
+  static string from_view(std::string_view v) {
+    if (!v.size()) return string((char const*)empty_ptr(), 0);
+    char* addr = (char*)malloc(v.size());
+    memcpy(addr, v.data(), v.size());
+    return string(addr, v.size());
+  }
+};
+
+/// A vector in linear memory, freed unconditionally using free
+///
+/// You can't detach the data memory from a vector, nor create one
+/// in a portable way from a buffer and lenght without copying.
+template <class T> class vector {
+  T *data_;
+  size_t length;
+
+  static T* empty_ptr() { return (T*)alignof(T); }
+
+public:
+  vector(vector const &) = delete;
+  vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; }
+  vector &operator=(vector const &) = delete;
+  vector &operator=(vector &&b) {
+    if (data_ && length>0) {
+      free(const_cast<uint8_t *>(data_));
+    }
+    data_ = b.data_;
+    length = b.length;
+    b.data_ = nullptr;
+    return *this;
+  }
+  vector(T *d, size_t l) : data_(d), length(l) {}
+  // Rust needs a nonzero pointer here (alignment is typical)
+  vector() : data_(empty_ptr()), length() {}
+  T const *data() const { return data_; }
+  T *data() { return data_; }
+  T &operator[](size_t n) { return data_[n]; }
+  T const &operator[](size_t n) const { return data_[n]; }
+  size_t size() const { return length; }
+  bool empty() const { return !length; }
+  ~vector() {
+    if (data_ && length>0) {
+      for (unsigned i=0;i<length;++i) { data_[i].~T(); }
+      free((void*)data_);
+    }
+  }
+  // WARNING: vector contains uninitialized elements
+  static vector<T> allocate(size_t len) {
+    if (!len) return vector<T>(empty_ptr(), 0);
+    return vector<T>((T*)malloc(sizeof(T)*len), len);
+  }
+  void initialize(size_t n, T&& elem) {
+    new ((void*)(data_+n)) T(std::move(elem));
+  }
+  // leak the memory
+  T* leak() { T*result = data_; data_ = nullptr; return result; }
+  // typically called by post
+  static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); }
+  wit::span<T> get_view() const { return wit::span<T>(data_, length); }
+  wit::span<const T> get_const_view() const { return wit::span<const T>(data_, length); }
+  template <class U> static vector<T> from_view(wit::span<U> const& a) {
+    auto result = vector<T>::allocate(a.size());
+    for (uint32_t i=0;i<a.size();++i) {
+      new ((void*)(result.data_+i)) T(a[i]);
+    }
+    return result;
+  } 
+};
+
+/// @brief  A Resource defined within the guest (guest side)
+///
+/// It registers with the host and should remain in a static location.
+/// Typically referenced by the Owned type
+///
+/// Note that deregistering will cause the host to call Dtor which
+/// in turn frees the object.
+template <class R> class ResourceExportBase {
+public:
+  struct Deregister {
+    void operator()(R *ptr) const {
+      // probably always true because of unique_ptr wrapping, TODO: check
+#ifdef WIT_SYMMETRIC
+      if (ptr->handle != nullptr)
+#else
+      if (ptr->handle >= 0)
+#endif
+      {
+        // we can't deallocate because the host calls Dtor
+        R::ResourceDrop(ptr->handle);
+      }
+    }
+  };
+  typedef std::unique_ptr<R, Deregister> Owned;
+
+#ifdef WIT_SYMMETRIC
+  typedef uint8_t *handle_t;
+  static constexpr handle_t invalid = nullptr;
+#else
+  typedef int32_t handle_t;
+  static const handle_t invalid = -1;
+#endif
+
+  handle_t handle;
+
+  ResourceExportBase() : handle(R::ResourceNew((R *)this)) {}
+  // because this function is called by the host via Dtor we must not deregister
+  ~ResourceExportBase() {}
+  ResourceExportBase(ResourceExportBase const &) = delete;
+  ResourceExportBase(ResourceExportBase &&) = delete;
+  ResourceExportBase &operator=(ResourceExportBase &&b) = delete;
+  ResourceExportBase &operator=(ResourceExportBase const &) = delete;
+  handle_t get_handle() const { return handle; }
+  handle_t into_handle() {
+    handle_t result = handle;
+    handle = invalid;
+    return result;
+  }
+};
+
+/// @brief A Resource imported from the host (guest side)
+///
+/// Wraps the identifier and can be forwarded but not duplicated
+class ResourceImportBase {
+public:
+#ifdef WIT_SYMMETRIC
+  typedef uint8_t *handle_t;
+  static constexpr handle_t invalid = nullptr;
+#else
+  typedef int32_t handle_t;
+  static const handle_t invalid = -1;
+#endif
+
+protected:
+  handle_t handle;
+
+public:
+  ResourceImportBase(handle_t h = invalid) : handle(h) {}
+  ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) {
+    r.handle = invalid;
+  }
+  ResourceImportBase(ResourceImportBase const &) = delete;
+  void set_handle(handle_t h) { handle = h; }
+  handle_t get_handle() const { return handle; }
+  handle_t into_handle() {
+    handle_t h = handle;
+    handle = invalid;
+    return h;
+  }
+  ResourceImportBase &operator=(ResourceImportBase &&r) {
+    assert(handle == invalid);
+    handle = r.handle;
+    r.handle = invalid;
+    return *this;
+  }
+  ResourceImportBase &operator=(ResourceImportBase const &r) = delete;
+};
+} // namespace wit
diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h
new file mode 100644
index 000000000..bd9ae704f
--- /dev/null
+++ b/crates/cpp/helper-types/wit-host.h
@@ -0,0 +1,261 @@
+#pragma once
+
+#include "wit-common.h"
+#include <malloc.h>
+#include <memory> // unique_ptr
+#include <stdint.h>
+#include <string.h>
+#include <string_view>
+
+#ifndef WIT_HOST_DIRECT
+#define WIT_HOST_WAMR
+#endif
+
+// #ifdef WIT_HOST_DIRECT
+// #define WIT_WASI64
+// #endif
+
+namespace wit {
+#ifdef WIT_HOST_DIRECT
+typedef uint8_t *guest_address;
+typedef size_t guest_size;
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+#define INVALID_GUEST_ADDRESS nullptr
+#elif defined(WIT_WASI64)
+typedef uint64_t guest_address;
+typedef uint64_t guest_size;
+#define INVALID_GUEST_ADDRESS 0
+#else
+typedef uint32_t guest_address;
+typedef uint32_t guest_size;
+#define INVALID_GUEST_ADDRESS 0
+#endif
+} // namespace wit
+
+#ifdef WIT_HOST_WAMR
+#include <wasm_c_api.h>
+#include <wasm_export.h>
+#endif
+
+namespace wit {
+#ifdef WIT_HOST_WAMR
+typedef void (*guest_cabi_post_t)(WASMExecEnv *, guest_address);
+typedef guest_address (*guest_alloc_t)(WASMExecEnv *, guest_size size,
+                                       guest_size align);
+#endif
+
+/// A string in linear memory (host-side handle)
+// host code never de-allocates directly
+class string {
+  guest_address data_;
+  guest_size length;
+
+public:
+#ifdef WIT_HOST_WAMR
+  std::string_view get_view(WASMExecEnv *inst) const {
+    return std::string_view((char const *)wasm_runtime_addr_app_to_native(
+                                wasm_runtime_get_module_inst(inst), data_),
+                            length);
+  }
+#elif defined(WIT_HOST_DIRECT)
+  std::string_view get_view() const {
+    return std::string_view((char const *)data_, length);
+  }
+#endif
+  string(guest_address a, guest_size s) : data_(a), length(s) {}
+  guest_address data() const { return data_; }
+  guest_size size() const { return length; }
+  // add a convenient way to create a string
+
+#if defined(WIT_HOST_DIRECT)
+  static string from_view(std::string_view v) {
+    void *addr = cabi_realloc(nullptr, 0, 1, v.length());
+    memcpy(addr, v.data(), v.length());
+    return string((guest_address)addr, v.length());
+  }
+#endif
+#if defined(WIT_HOST_WAMR)
+  static string from_view(wasm_exec_env_t exec_env, std::string_view v) {
+    void *addr = nullptr;
+    wasm_function_inst_t wasm_func = wasm_runtime_lookup_function(
+        wasm_runtime_get_module_inst(exec_env), "cabi_realloc"/*, "(*$ii)*"*/);
+
+    wasm_val_t wasm_results[1] = {WASM_INIT_VAL};
+    wasm_val_t wasm_args[4] = {
+        WASM_I32_VAL(0 /*nullptr*/),
+        WASM_I32_VAL(0),
+        WASM_I32_VAL(1),
+        WASM_I32_VAL(0 /*v.length()*/),
+    };
+    bool wasm_ok;
+    wasm_args[3].of.i32 = v.length();
+    wasm_ok = wasm_runtime_call_wasm_a(exec_env, wasm_func, 1, wasm_results, 4,
+                                       wasm_args);
+    assert(wasm_ok);
+    assert(wasm_results[0].kind == WASM_I32);
+    auto ret = wasm_results[0].of.i32;
+    addr = (void *)wasm_runtime_addr_app_to_native(
+        wasm_runtime_get_module_inst(exec_env), ret);
+    memcpy(addr, v.data(), v.length());
+    return string((guest_address)ret, v.length());
+  }
+#endif
+};
+
+/// A vector in linear memory (host-side handle)
+template <class T> class vector {
+  guest_address data_;
+  guest_size length;
+
+public:
+#ifdef WIT_HOST_WAMR
+  std::string_view get_view(WASMExecEnv *inst) const {
+    return wit::span((T const *)wasm_runtime_addr_app_to_native(
+                         wasm_runtime_get_module_inst(inst), data_),
+                     length);
+  }
+#elif defined(WIT_HOST_DIRECT)
+  std::string_view get_view() const {
+    return wit::span((T const *)data_, length);
+  }
+#endif
+  vector(guest_address a, guest_size s) : data_(a), length(s) {}
+  guest_address data() const { return data_; }
+  guest_size size() const { return length; }
+};
+
+/// Wrapper for specialized de-allocation of a returned type (calling
+/// cabi_post_*)
+template <class T> class guest_owned : public T {
+  guest_address data_;
+#ifdef WIT_HOST_WAMR
+  wasm_function_inst_t free_func;
+  WASMExecEnv *exec_env;
+#elif defined(WIT_HOST_DIRECT)
+  void (*free_func)(guest_address);
+#endif
+public:
+  guest_owned(guest_owned const &) = delete;
+  guest_owned &operator=(guest_owned const &) = delete;
+  ~guest_owned() {
+    if (data_) {
+#ifdef WIT_HOST_WAMR
+      wasm_val_t *wasm_results = nullptr;
+      wasm_val_t wasm_args[1] = {
+          WASM_I32_VAL((int32_t)data_),
+      };
+      wasm_runtime_call_wasm_a(exec_env, free_func, 0, wasm_results, 1,
+                               wasm_args);
+#elif defined(WIT_HOST_DIRECT)
+      (*free_func)(data_);
+#endif
+    }
+  }
+  guest_owned(guest_owned &&b)
+      : T(b), data_(b.data_), free_func(b.free_func)
+#ifdef WIT_HOST_WAMR
+        ,
+        exec_env(b.exec_env)
+#endif
+  {
+    b.data_ = INVALID_GUEST_ADDRESS;
+  }
+  guest_owned(T &&t, guest_address a,
+#ifdef WIT_HOST_WAMR
+              wasm_function_inst_t f, WASMExecEnv *e
+#elif defined(WIT_HOST_DIRECT)
+              void (*f)(guest_address)
+#endif
+              )
+      : T(std::move(t)), data_(a), free_func(f)
+#ifdef WIT_HOST_WAMR
+        ,
+        exec_env(e)
+#endif
+  {
+  }
+  T const &inner() const { return *static_cast<T const *>(this); }
+
+#ifdef WIT_HOST_WAMR
+  // not necessary? as the only way to get a guest_owned object
+  // is to pass exec_env
+  // WASMExecEnv* get_exec_env() const {
+  //     return exec_env;
+  // }
+#endif
+};
+
+/// Guest exported resource (host side handle)
+class ResourceExportBase : public ResourceTable<guest_address> {
+protected:
+  guest_address rep;
+  int32_t index;
+
+public:
+  ResourceExportBase() : rep(INVALID_GUEST_ADDRESS), index(-1) {}
+  ResourceExportBase(int32_t i) : rep(*lookup_resource(i)), index(i) {}
+  ResourceExportBase(ResourceExportBase &&b) : rep(b.rep), index(b.index) {
+    b.rep = 0;
+  }
+  ResourceExportBase(ResourceExportBase const &) = delete;
+  ResourceExportBase &operator=(ResourceExportBase const &) = delete;
+  ResourceExportBase &operator=(ResourceExportBase &&b) {
+    assert(rep == 0);
+    rep = b.rep;
+    index = b.index;
+    b.rep = 0;
+  }
+  ~ResourceExportBase() {
+    if (index >= 0 && rep != INVALID_GUEST_ADDRESS) {
+      remove_resource(index);
+    }
+  }
+  int32_t get_handle() const { return index; }
+  guest_address get_rep() const { return rep; }
+  guest_address take_rep() {
+    guest_address res = rep;
+    rep = 0;
+    return res;
+  }
+};
+
+/// Host defined resource (host side definition)
+template <class R> class ResourceImportBase : public ResourceTable<R *> {
+  int32_t index;
+
+public:
+  struct Deleter {
+    void operator()(R *ptr) const { R::Dtor(ptr); }
+  };
+  typedef std::unique_ptr<R, Deleter> Owned;
+
+  static const int32_t invalid = -1;
+  ResourceImportBase() : index(this->store_resource((R *)this)) {}
+  ~ResourceImportBase() {}
+  ResourceImportBase(ResourceImportBase &&b) = delete;
+  ResourceImportBase(ResourceImportBase const &) = delete;
+  ResourceImportBase &operator=(ResourceImportBase const &) = delete;
+  ResourceImportBase &operator=(ResourceImportBase &&) = delete;
+  int32_t get_handle() { return index; }
+};
+
+/// Host representation of a resource defined in another component
+///
+/// Acts like ResourceImportBase (e.g. definition);
+/// R should derive from ResourceExportBase
+template <class R> class ResourceForwarder : public R {
+  typedef R Owned;
+  ResourceForwarder(int32_t id) : R(ResourceExportBase(id)) {}
+  std::optional<Owned> lookup_resource(int32_t id) {
+    // TODO: Handle not found
+    return R(ResourceExportBase(id));
+  }
+  std::optional<Owned> remove_resource(int32_t id) {
+    std::optional<R *> result = R::remove_resource(id);
+    if (!result.has_value())
+      return std::optional<Owned>();
+    return *result;
+  }
+};
+} // namespace wit
diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs
new file mode 100644
index 000000000..e4d1f8836
--- /dev/null
+++ b/crates/cpp/src/lib.rs
@@ -0,0 +1,3973 @@
+use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use std::{
+    collections::{HashMap, HashSet},
+    fmt::Write as FmtWrite,
+    io::{Read, Write},
+    process::{Command, Stdio},
+    str::FromStr,
+};
+use wit_bindgen_c::to_c_ident;
+use wit_bindgen_core::{
+    abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType},
+    make_external_component, make_external_symbol, symmetric, uwrite, uwriteln,
+    wit_parser::{
+        Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId,
+        Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey,
+    },
+    Files, InterfaceGenerator, Source, WorldGenerator,
+};
+
+mod wamr;
+
+pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase";
+pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase";
+pub const RESOURCE_TABLE_NAME: &str = "ResourceTable";
+pub const OWNED_CLASS_NAME: &str = "Owned";
+pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)";
+// these types are always defined in the non-exports namespace
+const NOT_IN_EXPORTED_NAMESPACE: bool = false;
+
+type CppType = String;
+
+#[derive(Clone, Copy, Debug)]
+enum Flavor {
+    Argument(AbiVariant),
+    Result(AbiVariant),
+    InStruct,
+    BorrowedArgument,
+}
+
+impl Flavor {
+    fn is_guest_export(&self) -> bool {
+        match self {
+            Flavor::Argument(var) => matches!(var, AbiVariant::GuestExport),
+            Flavor::Result(var) => matches!(var, AbiVariant::GuestExport),
+            Flavor::InStruct | Flavor::BorrowedArgument => false,
+        }
+    }
+}
+
+#[derive(Default)]
+struct HighlevelSignature {
+    /// this is a constructor or destructor without a written type
+    // implicit_result: bool, -> empty result
+    const_member: bool,
+    static_member: bool,
+    result: CppType,
+    arguments: Vec<(String, CppType)>,
+    name: String,
+    namespace: Vec<String>,
+    implicit_self: bool,
+    post_return: bool,
+}
+
+// follows https://google.github.io/styleguide/cppguide.html
+
+#[derive(Default)]
+struct Includes {
+    needs_vector: bool,
+    needs_expected: bool,
+    needs_string: bool,
+    needs_string_view: bool,
+    needs_optional: bool,
+    needs_cstring: bool,
+    needs_guest_alloc: bool,
+    needs_imported_resources: bool,
+    needs_exported_resources: bool,
+    needs_variant: bool,
+    needs_tuple: bool,
+    needs_assert: bool,
+    // needs wit types
+    needs_wit: bool,
+    needs_memory: bool,
+}
+
+#[derive(Clone)]
+struct HostFunction {
+    wasm_name: String,
+    wamr_signature: String,
+    host_name: String,
+}
+
+#[derive(Default)]
+struct SourceWithState {
+    src: Source,
+    namespace: Vec<String>,
+}
+
+#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
+enum Direction {
+    Import,
+    Export,
+}
+
+#[derive(Default)]
+struct Cpp {
+    opts: Opts,
+    c_src: SourceWithState,
+    h_src: SourceWithState,
+    c_src_head: Source,
+    // interface_includes: Vec<String>,
+    // interface_header: SourceWithState,
+    extern_c_decls: Source,
+    dependencies: Includes,
+    includes: Vec<String>,
+    host_functions: HashMap<String, Vec<HostFunction>>,
+    world: String,
+    world_id: Option<WorldId>,
+    imported_interfaces: HashSet<InterfaceId>,
+    user_class_files: HashMap<String, String>,
+    defined_types: HashSet<(Vec<String>, String)>,
+
+    // needed for symmetric disambiguation
+    interface_prefixes: HashMap<(Direction, WorldKey), String>,
+    import_prefix: Option<String>,
+}
+
+#[derive(Default, Debug, Clone, Copy)]
+pub enum Ownership {
+    /// Generated types will be composed entirely of owning fields, regardless
+    /// of whether they are used as parameters to imports or not.
+    #[default]
+    Owning,
+
+    /// Generated types used as parameters to imports will be "deeply
+    /// borrowing", i.e. contain references rather than owned values when
+    /// applicable.
+    Borrowing {
+        /// Whether or not to generate "duplicate" type definitions for a single
+        /// WIT type if necessary, for example if it's used as both an import
+        /// and an export, or if it's used both as a parameter to an import and
+        /// a return value from an import.
+        duplicate_if_necessary: bool,
+    },
+}
+
+impl FromStr for Ownership {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "owning" => Ok(Self::Owning),
+            "borrowing" => Ok(Self::Borrowing {
+                duplicate_if_necessary: false,
+            }),
+            "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing {
+                duplicate_if_necessary: true,
+            }),
+            _ => Err(format!(
+                "unrecognized ownership: `{s}`; \
+                 expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`"
+            )),
+        }
+    }
+}
+
+impl core::fmt::Display for Ownership {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        f.write_str(match self {
+            Ownership::Owning => "owning",
+            Ownership::Borrowing {
+                duplicate_if_necessary: false,
+            } => "borrowing",
+            Ownership::Borrowing {
+                duplicate_if_necessary: true,
+            } => "borrowing-duplicate-if-necessary",
+        })
+    }
+}
+
+#[derive(Default, Debug, Clone)]
+#[cfg_attr(feature = "clap", derive(clap::Args))]
+pub struct Opts {
+    /// Generate host bindings
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub host: bool,
+    /// Generate code for directly linking to guest code (WIP)
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default(), alias = "direct"))]
+    pub short_cut: bool,
+    /// Call clang-format on the generated code
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub format: bool,
+    /// 64bit guest
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub wasm64: bool,
+
+    /// Place each interface in its own file,
+    /// this enables sharing bindings across projects
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub split_interfaces: bool,
+
+    /// Optionally prefix any export names with the specified value.
+    ///
+    /// This is useful to avoid name conflicts when testing.
+    #[cfg_attr(feature = "clap", arg(long))]
+    pub export_prefix: Option<String>,
+
+    /// Wrap all C++ classes inside a custom namespace.
+    ///
+    /// This avoids identical names across components, useful for native
+    #[cfg_attr(feature = "clap", arg(long))]
+    pub internal_prefix: Option<String>,
+
+    /// Whether to generate owning or borrowing type definitions.
+    ///
+    /// Valid values include:
+    ///
+    /// - `owning`: Generated types will be composed entirely of owning fields,
+    /// regardless of whether they are used as parameters to imports or not.
+    ///
+    /// - `borrowing`: Generated types used as parameters to imports will be
+    /// "deeply borrowing", i.e. contain references rather than owned values
+    /// when applicable.
+    ///
+    /// - `borrowing-duplicate-if-necessary`: As above, but generating distinct
+    /// types for borrowing and owning, if necessary.
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))]
+    pub ownership: Ownership,
+
+    /// Symmetric ABI, this enables to directly link components to each
+    /// other and removes the primary distinction between host and guest.
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub symmetric: bool,
+
+    /// Symmetric API, same API for imported and exported functions.
+    /// Reduces the allocation overhead for symmetric ABI.
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub new_api: bool,
+}
+
+impl Opts {
+    pub fn build(self) -> Box<dyn WorldGenerator> {
+        let mut r = Cpp::new();
+        r.opts = self;
+        Box::new(r)
+    }
+
+    fn host_side(&self) -> bool {
+        self.short_cut || self.host
+    }
+
+    fn is_only_handle(&self, variant: AbiVariant) -> bool {
+        self.host_side() == matches!(variant, AbiVariant::GuestExport)
+    }
+
+    fn ptr_type(&self) -> &'static str {
+        if !self.host {
+            "uint8_t*"
+        } else if self.wasm64 {
+            "int64_t"
+        } else {
+            "int32_t"
+        }
+    }
+
+    // we need to map pointers depending on context
+    fn wasm_type(&self, ty: WasmType) -> &'static str {
+        match ty {
+            WasmType::Pointer => self.ptr_type(),
+            _ => wit_bindgen_c::wasm_type(ty),
+        }
+    }
+}
+
+impl Cpp {
+    fn new() -> Cpp {
+        Cpp::default()
+    }
+
+    pub fn is_first_definition(&mut self, ns: &Vec<String>, name: &str) -> bool {
+        let owned = (ns.to_owned(), name.to_owned());
+        if !self.defined_types.contains(&owned) {
+            self.defined_types.insert(owned);
+            true
+        } else {
+            false
+        }
+    }
+
+    fn include(&mut self, s: &str) {
+        self.includes.push(s.to_string());
+    }
+
+    fn interface<'a>(
+        &'a mut self,
+        resolve: &'a Resolve,
+        name: Option<&'a WorldKey>,
+        in_guest_import: bool,
+        wasm_import_module: Option<String>,
+    ) -> CppInterfaceGenerator<'a> {
+        let mut sizes = if self.opts.symmetric {
+            SizeAlign::new_symmetric()
+        } else {
+            SizeAlign::default()
+        };
+        sizes.fill(resolve);
+
+        CppInterfaceGenerator {
+            _src: Source::default(),
+            gen: self,
+            resolve,
+            interface: None,
+            _name: name,
+            sizes,
+            // public_anonymous_types: BTreeSet::new(),
+            in_guest_import,
+            // export_funcs: Vec::new(),
+            // return_pointer_area_size: 0,
+            // return_pointer_area_align: 0,
+            wasm_import_module,
+        }
+    }
+
+    fn clang_format(code: &mut String) {
+        let mut child = Command::new("clang-format")
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .spawn()
+            .expect("failed to spawn `clang-format`");
+        child
+            .stdin
+            .take()
+            .unwrap()
+            .write_all(code.as_bytes())
+            .unwrap();
+        code.truncate(0);
+        child.stdout.take().unwrap().read_to_string(code).unwrap();
+        let status = child.wait().unwrap();
+        assert!(status.success());
+    }
+
+    fn perform_cast(&mut self, op: &str, cast: &Bitcast) -> String {
+        match cast {
+            Bitcast::I32ToF32 | Bitcast::I64ToF32 => {
+                format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op)
+            }
+            Bitcast::F32ToI32 | Bitcast::F32ToI64 => {
+                format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op)
+            }
+            Bitcast::I64ToF64 => {
+                format!("((union {{ int64_t a; double b; }}){{ {} }}).b", op)
+            }
+            Bitcast::F64ToI64 => {
+                format!("((union {{ double a; int64_t b; }}){{ {} }}).b", op)
+            }
+            Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => {
+                format!("(int64_t) {}", op)
+            }
+            Bitcast::I64ToI32 | Bitcast::PToI32 | Bitcast::LToI32 => {
+                format!("(int32_t) {}", op)
+            }
+            Bitcast::P64ToI64 | Bitcast::None | Bitcast::I64ToP64 => op.to_string(),
+            Bitcast::P64ToP | Bitcast::I32ToP | Bitcast::LToP => {
+                format!("(uint8_t*) {}", op)
+            }
+            Bitcast::PToL | Bitcast::I32ToL | Bitcast::I64ToL => {
+                format!("(size_t) {}", op)
+            }
+            Bitcast::Sequence(sequence) => {
+                let [first, second] = &**sequence;
+                let inner = self.perform_cast(op, first);
+                self.perform_cast(&inner, second)
+            }
+        }
+    }
+
+    fn finish_includes(&mut self) {
+        self.include("<cstdint>");
+        self.include("<utility>"); // for std::move
+        if self.dependencies.needs_string {
+            self.include("<string>");
+        }
+        if self.dependencies.needs_string_view {
+            self.include("<string_view>");
+        }
+        if self.dependencies.needs_vector {
+            self.include("<vector>");
+        }
+        if self.dependencies.needs_expected {
+            self.include("<expected>");
+        }
+        if self.dependencies.needs_optional {
+            self.include("<optional>");
+        }
+        if self.dependencies.needs_cstring {
+            self.include("<cstring>");
+        }
+        if self.dependencies.needs_imported_resources {
+            self.include("<cassert>");
+        }
+        if self.dependencies.needs_exported_resources {
+            self.include("<map>");
+        }
+        if self.dependencies.needs_variant {
+            self.include("<variant>");
+        }
+        if self.dependencies.needs_tuple {
+            self.include("<tuple>");
+        }
+        if self.dependencies.needs_wit {
+            if self.opts.host_side() {
+                self.include("<wit-host.h>");
+            } else {
+                self.include("<wit-guest.h>");
+            }
+        }
+        if self.dependencies.needs_memory {
+            self.include("<memory>");
+        }
+    }
+
+    fn start_new_file(&mut self, condition: Option<bool>) -> FileContext {
+        if condition == Some(true) || self.opts.split_interfaces {
+            FileContext {
+                includes: std::mem::replace(&mut self.includes, Default::default()),
+                src: std::mem::replace(&mut self.h_src, Default::default()),
+                dependencies: std::mem::replace(&mut self.dependencies, Default::default()),
+            }
+        } else {
+            Default::default()
+        }
+    }
+
+    fn finish_file(&mut self, namespace: &[String], store: FileContext) {
+        if !store.src.src.is_empty() {
+            //        self.opts.split_interfaces {
+            let mut header = String::default();
+            self.finish_includes();
+            self.h_src.change_namespace(&Default::default());
+            uwriteln!(header, "#pragma once");
+            if self.opts.symmetric {
+                uwriteln!(header, "#define WIT_SYMMETRIC");
+            }
+            for include in self.includes.iter() {
+                uwriteln!(header, "#include {include}");
+            }
+            header.push_str(&self.h_src.src);
+            let mut filename = namespace.join("-");
+            filename.push_str(".h");
+            if self.opts.format {
+                Self::clang_format(&mut header);
+            }
+            self.user_class_files.insert(filename.clone(), header);
+
+            let _ = std::mem::replace(&mut self.includes, store.includes);
+            let _ = std::mem::replace(&mut self.h_src, store.src);
+            let _ = std::mem::replace(&mut self.dependencies, store.dependencies);
+            self.includes.push(String::from("\"") + &filename + "\"");
+        }
+    }
+}
+
+#[derive(Default)]
+struct FileContext {
+    includes: Vec<String>,
+    src: SourceWithState,
+    dependencies: Includes,
+}
+
+impl WorldGenerator for Cpp {
+    fn preprocess(&mut self, resolve: &Resolve, world: WorldId) {
+        let name = &resolve.worlds[world].name;
+        self.world = name.to_string();
+        self.world_id = Some(world);
+        //        self.sizes.fill(resolve);
+        if !self.opts.host_side() {
+            uwriteln!(
+                self.c_src_head,
+                r#"#include "{}_cpp.h"
+            #include <cstdlib> // realloc
+
+            extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size);
+
+            __attribute__((__weak__, __export_name__("cabi_realloc")))
+            void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {{
+                (void) old_size;
+                if (new_size == 0) return (void*) align;
+                void *ret = realloc(ptr, new_size);
+                if (!ret) abort();
+                return ret;
+            }}
+
+            "#,
+                self.world.to_snake_case(),
+            );
+        }
+    }
+
+    fn import_interface(
+        &mut self,
+        resolve: &Resolve,
+        name: &WorldKey,
+        id: InterfaceId,
+        _files: &mut Files,
+    ) -> anyhow::Result<()> {
+        if let Some(prefix) = self
+            .interface_prefixes
+            .get(&(Direction::Import, name.clone()))
+        {
+            self.import_prefix = Some(prefix.clone());
+        }
+
+        let store = self.start_new_file(None);
+        self.imported_interfaces.insert(id);
+        let wasm_import_module = resolve.name_world_key(name);
+        let binding = Some(name);
+        let mut gen = self.interface(resolve, binding, true, Some(wasm_import_module));
+        gen.interface = Some(id);
+        gen.types(id);
+        let namespace = namespace(resolve, &TypeOwner::Interface(id), false, &gen.gen.opts);
+
+        for (_name, func) in resolve.interfaces[id].functions.iter() {
+            if matches!(func.kind, FunctionKind::Freestanding) {
+                gen.gen.h_src.change_namespace(&namespace);
+                gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport);
+            }
+        }
+        self.finish_file(&namespace, store);
+        let _ = self.import_prefix.take();
+        Ok(())
+    }
+
+    fn export_interface(
+        &mut self,
+        resolve: &Resolve,
+        name: &WorldKey,
+        id: InterfaceId,
+        _files: &mut Files,
+    ) -> anyhow::Result<()> {
+        let old_prefix = self.opts.export_prefix.clone();
+        if let Some(prefix) = self
+            .interface_prefixes
+            .get(&(Direction::Export, name.clone()))
+        {
+            self.opts.export_prefix =
+                Some(prefix.clone() + old_prefix.as_ref().unwrap_or(&String::new()));
+        }
+        let store = self.start_new_file(None);
+        self.h_src
+            .src
+            .push_str(&format!("// export_interface {name:?}\n"));
+        self.imported_interfaces.remove(&id);
+        let wasm_import_module = resolve.name_world_key(name);
+        let binding = Some(name);
+        let mut gen = self.interface(resolve, binding, false, Some(wasm_import_module));
+        gen.interface = Some(id);
+        gen.types(id);
+        let namespace = namespace(resolve, &TypeOwner::Interface(id), true, &gen.gen.opts);
+
+        for (_name, func) in resolve.interfaces[id].functions.iter() {
+            if matches!(func.kind, FunctionKind::Freestanding) {
+                gen.gen.h_src.change_namespace(&namespace);
+                gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestExport);
+            }
+        }
+        self.finish_file(&namespace, store);
+        self.opts.export_prefix = old_prefix;
+        Ok(())
+    }
+
+    fn import_funcs(
+        &mut self,
+        resolve: &Resolve,
+        world: WorldId,
+        funcs: &[(&str, &Function)],
+        _files: &mut Files,
+    ) {
+        let name = WorldKey::Name("$root".to_string()); //WorldKey::Name(resolve.worlds[world].name.clone());
+        let wasm_import_module = resolve.name_world_key(&name);
+        let binding = Some(name);
+        let mut gen = self.interface(resolve, binding.as_ref(), true, Some(wasm_import_module));
+        let namespace = namespace(resolve, &TypeOwner::World(world), false, &gen.gen.opts);
+
+        for (_name, func) in funcs.iter() {
+            if matches!(func.kind, FunctionKind::Freestanding) {
+                gen.gen.h_src.change_namespace(&namespace);
+                gen.generate_function(func, &TypeOwner::World(world), AbiVariant::GuestImport);
+            }
+        }
+    }
+
+    fn export_funcs(
+        &mut self,
+        resolve: &Resolve,
+        world: WorldId,
+        funcs: &[(&str, &Function)],
+        _files: &mut Files,
+    ) -> anyhow::Result<()> {
+        let name = WorldKey::Name(resolve.worlds[world].name.clone());
+        // let wasm_import_module = resolve.name_world_key(&name);
+        let binding = Some(name);
+        let mut gen = self.interface(resolve, binding.as_ref(), false, None);
+        let namespace = namespace(resolve, &TypeOwner::World(world), true, &gen.gen.opts);
+
+        for (_name, func) in funcs.iter() {
+            if matches!(func.kind, FunctionKind::Freestanding) {
+                gen.gen.h_src.change_namespace(&namespace);
+                gen.generate_function(func, &TypeOwner::World(world), AbiVariant::GuestExport);
+            }
+        }
+        Ok(())
+    }
+
+    fn import_types(
+        &mut self,
+        _resolve: &Resolve,
+        _world: WorldId,
+        types: &[(&str, TypeId)],
+        _files: &mut Files,
+    ) {
+        for i in types.iter() {
+            uwriteln!(self.h_src.src, "// import_type {}", i.0);
+        }
+    }
+
+    fn finish(
+        &mut self,
+        resolve: &Resolve,
+        world_id: WorldId,
+        files: &mut Files,
+    ) -> std::result::Result<(), anyhow::Error> {
+        let world = &resolve.worlds[world_id];
+        let snake = world.name.to_snake_case();
+        let linking_symbol = wit_bindgen_c::component_type_object::linking_symbol(&world.name);
+
+        let mut h_str = SourceWithState::default();
+        let mut c_str = SourceWithState::default();
+
+        let version = env!("CARGO_PKG_VERSION");
+        uwriteln!(
+            h_str.src,
+            "// Generated by `wit-bindgen` {version}. DO NOT EDIT!"
+        );
+
+        if self.opts.short_cut {
+            uwrite!(
+                h_str.src,
+                "#ifndef __CPP_NATIVE_BINDINGS_{0}_H
+                #define __CPP_NATIVE_BINDINGS_{0}_H\n",
+                world.name.to_shouty_snake_case(),
+            );
+        } else if !self.opts.host {
+            uwrite!(
+                h_str.src,
+                "#ifndef __CPP_GUEST_BINDINGS_{0}_H
+                #define __CPP_GUEST_BINDINGS_{0}_H\n",
+                world.name.to_shouty_snake_case(),
+            );
+        } else {
+            uwrite!(
+                h_str.src,
+                "#ifndef __CPP_HOST_BINDINGS_{0}_H
+                #define __CPP_HOST_BINDINGS_{0}_H
+                struct WASMExecEnv; // WAMR execution environment\n",
+                world.name.to_shouty_snake_case(),
+            );
+        }
+        self.finish_includes();
+
+        if self.opts.short_cut {
+            uwriteln!(h_str.src, "#define WIT_HOST_DIRECT");
+        } else if self.opts.symmetric {
+            uwriteln!(h_str.src, "#define WIT_SYMMETRIC");
+        }
+        for include in self.includes.iter() {
+            uwriteln!(h_str.src, "#include {include}");
+        }
+
+        uwriteln!(
+            c_str.src,
+            "// Generated by `wit-bindgen` {version}. DO NOT EDIT!"
+        );
+        if self.opts.short_cut {
+            uwriteln!(c_str.src, "#include \"{snake}_cpp_native.h\"");
+        } else if !self.opts.host {
+            uwriteln!(
+                c_str.src,
+                "\n// Ensure that the *_component_type.o object is linked in"
+            );
+            uwrite!(
+                c_str.src,
+                "#ifdef __wasm32__
+                   extern void {linking_symbol}(void);
+                   void {linking_symbol}_public_use_in_this_compilation_unit(void) {{
+                       {linking_symbol}();
+                   }}
+                   #endif
+               ",
+            );
+        } else {
+            uwriteln!(c_str.src, "#include \"{snake}_cpp_host.h\"");
+            uwriteln!(
+                c_str.src,
+                "#include <wasm_export.h> // wasm-micro-runtime header\n\
+                 #include <wasm_c_api.h>\n\
+                 #include <assert.h>"
+            );
+
+            if c_str.src.len() > 0 {
+                c_str.src.push_str("\n");
+            }
+            if self.dependencies.needs_guest_alloc {
+                uwriteln!(
+                    c_str.src,
+                    "int32_t guest_alloc(wasm_exec_env_t exec_env, uint32_t size);"
+                );
+            }
+        }
+        if self.opts.host_side() && self.dependencies.needs_exported_resources {
+            uwriteln!(
+                c_str.src,
+                "template <class R> std::map<int32_t, R> wit::{RESOURCE_TABLE_NAME}<R>::resources;"
+            );
+        }
+        if self.dependencies.needs_assert {
+            uwriteln!(c_str.src, "#include <assert.h>");
+        }
+
+        h_str.change_namespace(&Vec::default());
+
+        self.c_src.change_namespace(&Vec::default());
+        c_str.src.push_str(&self.c_src_head);
+        c_str.src.push_str(&self.extern_c_decls);
+        c_str.src.push_str(&self.c_src.src);
+        self.h_src.change_namespace(&Vec::default());
+        h_str.src.push_str(&self.h_src.src);
+
+        uwriteln!(c_str.src, "\n// Component Adapters");
+
+        if !self.opts.short_cut && self.opts.host {
+            uwriteln!(
+                h_str.src,
+                "extern \"C\" void register_{}();",
+                world.name.to_snake_case()
+            );
+            uwriteln!(
+                c_str.src,
+                "void register_{}() {{",
+                world.name.to_snake_case()
+            );
+            for i in self.host_functions.iter() {
+                uwriteln!(
+                    c_str.src,
+                    "  static NativeSymbol {}_funs[] = {{",
+                    i.0.replace(&[':', '.', '-', '+'], "_").to_snake_case()
+                );
+                for f in i.1.iter() {
+                    uwriteln!(
+                        c_str.src,
+                        "    {{ \"{}\", (void*){}, \"{}\", nullptr }},",
+                        f.wasm_name,
+                        f.host_name,
+                        f.wamr_signature
+                    );
+                }
+                uwriteln!(c_str.src, "  }};");
+            }
+            for i in self.host_functions.iter() {
+                uwriteln!(c_str.src, "  wasm_runtime_register_natives(\"{}\", {1}_funs, sizeof({1}_funs)/sizeof(NativeSymbol));", i.0, i.0.replace(&[':','.','-','+'], "_").to_snake_case());
+            }
+            uwriteln!(c_str.src, "}}");
+        }
+
+        uwriteln!(
+            h_str.src,
+            "
+            #endif"
+        );
+
+        if self.opts.format {
+            Self::clang_format(&mut c_str.src.as_mut_string());
+            Self::clang_format(&mut h_str.src.as_mut_string());
+        }
+
+        if self.opts.short_cut {
+            files.push(&format!("{snake}_native.cpp"), c_str.src.as_bytes());
+            files.push(&format!("{snake}_cpp_native.h"), h_str.src.as_bytes());
+        } else if !self.opts.host {
+            files.push(&format!("{snake}.cpp"), c_str.src.as_bytes());
+            files.push(&format!("{snake}_cpp.h"), h_str.src.as_bytes());
+        } else {
+            files.push(&format!("{snake}_host.cpp"), c_str.src.as_bytes());
+            files.push(&format!("{snake}_cpp_host.h"), h_str.src.as_bytes());
+        }
+        for (name, content) in self.user_class_files.iter() {
+            // if the user class file exists create an updated .template
+            if std::path::Path::exists(&std::path::PathBuf::from(name)) {
+                files.push(&(String::from(name) + ".template"), content.as_bytes());
+            } else {
+                files.push(name, content.as_bytes());
+            }
+        }
+        files.push(
+            &format!("{snake}_component_type.o",),
+            wit_bindgen_c::component_type_object::object(
+                resolve,
+                world_id,
+                &world.name,
+                wit_component::StringEncoding::UTF8,
+                None,
+            )
+            .unwrap()
+            .as_slice(),
+        );
+        Ok(())
+    }
+
+    fn apply_resolve_options(&mut self, resolve: &mut Resolve, world: &mut WorldId) {
+        if self.opts.symmetric {
+            let world = &resolve.worlds[*world];
+            let exports: HashMap<&WorldKey, &wit_bindgen_core::wit_parser::WorldItem> =
+                world.exports.iter().collect();
+            for (key, _item) in world.imports.iter() {
+                // duplicate found
+                if exports.contains_key(key)
+                    && !self
+                        .interface_prefixes
+                        .contains_key(&(Direction::Import, key.clone()))
+                    && !self
+                        .interface_prefixes
+                        .contains_key(&(Direction::Export, key.clone()))
+                {
+                    self.interface_prefixes
+                        .insert((Direction::Import, key.clone()), "imp_".into());
+                    self.interface_prefixes
+                        .insert((Direction::Export, key.clone()), "exp_".into());
+                }
+            }
+        }
+    }
+}
+
+// determine namespace (for the lifted C++ function)
+fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool, opts: &Opts) -> Vec<String> {
+    let mut result = Vec::default();
+    if let Some(prefix) = &opts.internal_prefix {
+        result.push(prefix.clone());
+    }
+    if guest_export {
+        result.push(String::from("exports"));
+    }
+    match owner {
+        TypeOwner::World(w) => result.push(resolve.worlds[*w].name.to_snake_case()),
+        TypeOwner::Interface(i) => {
+            let iface = &resolve.interfaces[*i];
+            let pkg = &resolve.packages[iface.package.unwrap()];
+            result.push(pkg.name.namespace.to_snake_case());
+            result.push(pkg.name.name.to_snake_case());
+            if let Some(name) = &iface.name {
+                result.push(name.to_snake_case());
+            }
+        }
+        TypeOwner::None => (),
+    }
+    result
+}
+
+impl SourceWithState {
+    fn change_namespace(&mut self, target: &Vec<String>) {
+        let mut same = 0;
+        // itertools::fold_while?
+        for (a, b) in self.namespace.iter().zip(target.iter()) {
+            if a == b {
+                same += 1;
+            } else {
+                break;
+            }
+        }
+        for _i in same..self.namespace.len() {
+            uwrite!(self.src, "}}");
+        }
+        if same != self.namespace.len() {
+            // finish closing brackets by a newline
+            uwriteln!(self.src, "");
+        }
+        self.namespace.truncate(same);
+        for i in target.iter().skip(same) {
+            uwrite!(self.src, "namespace {} {{", i);
+            self.namespace.push(i.clone());
+        }
+    }
+
+    fn qualify(&mut self, target: &Vec<String>) {
+        let mut same = 0;
+        // let mut subpart = false;
+        // itertools::fold_while?
+        for (a, b) in self.namespace.iter().zip(target.iter()) {
+            if a == b {
+                same += 1;
+            } else {
+                break;
+            }
+        }
+        if same == 0 && !target.is_empty() {
+            // if the root namespace exists below the current namespace we need to start at root
+            if self.namespace.contains(&target.first().unwrap()) {
+                self.src.push_str("::");
+            }
+        }
+        for i in target.iter().skip(same) {
+            uwrite!(self.src, "{i}::");
+        }
+    }
+}
+
+struct CppInterfaceGenerator<'a> {
+    _src: Source,
+    gen: &'a mut Cpp,
+    resolve: &'a Resolve,
+    interface: Option<InterfaceId>,
+    _name: Option<&'a WorldKey>,
+    sizes: SizeAlign,
+    in_guest_import: bool,
+    // return_pointer_area_size: usize,
+    // return_pointer_area_align: usize,
+    pub wasm_import_module: Option<String>,
+}
+
+// I wish this was possible
+// impl Equivalent<(Vec<String>, String)> for (&Vec<String>, &str) {
+
+// }
+
+impl CppInterfaceGenerator<'_> {
+    fn types(&mut self, iface: InterfaceId) {
+        let iface = &self.resolve().interfaces[iface];
+        for (name, id) in iface.types.iter() {
+            self.define_type(name, *id);
+        }
+    }
+
+    fn define_type(&mut self, name: &str, id: TypeId) {
+        let ty = &self.resolve().types[id];
+        match &ty.kind {
+            TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs),
+            TypeDefKind::Resource => self.type_resource(id, name, &ty.docs),
+            TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &ty.docs),
+            TypeDefKind::Tuple(tuple) => self.type_tuple(id, name, tuple, &ty.docs),
+            TypeDefKind::Enum(enum_) => self.type_enum(id, name, enum_, &ty.docs),
+            TypeDefKind::Variant(variant) => self.type_variant(id, name, variant, &ty.docs),
+            TypeDefKind::Option(t) => self.type_option(id, name, t, &ty.docs),
+            TypeDefKind::Result(r) => self.type_result(id, name, r, &ty.docs),
+            TypeDefKind::List(t) => self.type_list(id, name, t, &ty.docs),
+            TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs),
+            TypeDefKind::Future(_) => todo!("generate for future"),
+            TypeDefKind::Stream(_) => todo!("generate for stream"),
+            TypeDefKind::Handle(_) => todo!("generate for handle"),
+            TypeDefKind::Unknown => unreachable!(),
+        }
+    }
+
+    /// This describes the C++ side name
+    fn func_namespace_name(
+        &self,
+        func: &Function,
+        guest_export: bool,
+        cpp_file: bool,
+    ) -> (Vec<String>, String) {
+        let (object, owner) = match &func.kind {
+            FunctionKind::Freestanding => None,
+            FunctionKind::Method(i) => Some(i),
+            FunctionKind::Static(i) => Some(i),
+            FunctionKind::Constructor(i) => Some(i),
+            FunctionKind::AsyncFreestanding => todo!(),
+            FunctionKind::AsyncMethod(_id) => todo!(),
+            FunctionKind::AsyncStatic(_id) => todo!(),
+        }
+        .map(|i| {
+            let ty = &self.resolve.types[*i];
+            (ty.name.as_ref().unwrap().to_pascal_case(), ty.owner)
+        })
+        .unwrap_or((
+            Default::default(),
+            self.interface
+                .map(|id| TypeOwner::Interface(id))
+                .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())),
+        ));
+        let mut namespace = namespace(self.resolve, &owner, guest_export, &self.gen.opts);
+        let is_drop = is_special_method(func);
+        let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) {
+            namespace.push(object.clone());
+            if let FunctionKind::Constructor(_i) = &func.kind {
+                if guest_export && cpp_file {
+                    String::from("New")
+                } else {
+                    object.clone()
+                }
+            } else {
+                match is_drop {
+                    SpecialMethod::ResourceDrop => {
+                        if self.gen.opts.host_side() && !guest_export {
+                            "Dtor".to_string()
+                        } else if guest_export {
+                            "ResourceDrop".to_string()
+                        } else {
+                            "~".to_string() + &object
+                        }
+                    }
+                    SpecialMethod::Dtor => {
+                        if self.gen.opts.host_side() && guest_export {
+                            "~".to_string() + &object
+                        } else {
+                            "Dtor".to_string()
+                        }
+                    }
+                    SpecialMethod::ResourceNew => "ResourceNew".to_string(),
+                    SpecialMethod::ResourceRep => "ResourceRep".to_string(),
+                    SpecialMethod::Allocate => "New".to_string(),
+                    // SpecialMethod::Deallocate => "Deallocate".to_string(),
+                    SpecialMethod::None => func.item_name().to_pascal_case(),
+                }
+            }
+        } else {
+            func.name.to_pascal_case()
+        };
+        (namespace, func_name_h)
+    }
+
+    // local patching of borrows function needs more complex solution
+    fn patched_wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature {
+        abi::wasm_signature_symmetric(self.resolve, variant, func, self.gen.opts.symmetric)
+        // if matches!(res.params.get(0), Some(WasmType::I32))
+        //     && matches!(func.kind, FunctionKind::Freestanding)
+        // {
+        //     if let Some((_, ty)) = func.params.get(0) {
+        //         if let Type::Id(id) = ty {
+        //             if let Some(td) = self.resolve.types.get(*id) {
+        //                 if let TypeDefKind::Handle(Handle::Borrow(id2)) = &td.kind {
+        //                     if let Some(ty2) = self.resolve.types.get(*id2) {
+        //                         dbg!((&self.gen.imported_interfaces, id2, ty2, &func));
+        //                     }
+        //                 }
+        //             }
+        //         }
+        //     }
+        // }
+    }
+
+    // print the signature of the guest export (lowered (wasm) function calling into highlevel)
+    fn print_export_signature(&mut self, func: &Function, variant: AbiVariant) -> Vec<String> {
+        let is_drop = is_special_method(func);
+        let id_type = if self.gen.opts.symmetric {
+            WasmType::Pointer
+        } else {
+            WasmType::I32
+        };
+        let signature = match is_drop {
+            SpecialMethod::ResourceDrop => WasmSignature {
+                params: vec![id_type],
+                results: Vec::new(),
+                indirect_params: false,
+                retptr: false,
+            },
+            SpecialMethod::ResourceRep => WasmSignature {
+                params: vec![id_type],
+                results: vec![WasmType::Pointer],
+                indirect_params: false,
+                retptr: false,
+            },
+            SpecialMethod::Dtor => WasmSignature {
+                params: vec![WasmType::Pointer],
+                results: Vec::new(),
+                indirect_params: false,
+                retptr: false,
+            },
+            SpecialMethod::ResourceNew => WasmSignature {
+                params: vec![WasmType::Pointer],
+                results: vec![id_type],
+                indirect_params: false,
+                retptr: false,
+            },
+            SpecialMethod::None => {
+                // TODO perhaps remember better names for the arguments
+                self.patched_wasm_signature(variant, func)
+            }
+            SpecialMethod::Allocate => WasmSignature {
+                params: vec![],
+                results: vec![],
+                indirect_params: false,
+                retptr: false,
+            },
+        };
+        let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone());
+        let mut symbol_variant = variant;
+        if self.gen.opts.symmetric && matches!(variant, AbiVariant::GuestExport) {
+            // symmetric doesn't distinguish
+            symbol_variant = AbiVariant::GuestImport;
+        }
+        if matches!(variant, AbiVariant::GuestExport)
+            && matches!(
+                is_drop,
+                SpecialMethod::ResourceNew
+                    | SpecialMethod::ResourceDrop
+                    | SpecialMethod::ResourceRep
+            )
+        {
+            module_name = Some(String::from("[export]") + &module_name.unwrap());
+            if self.gen.opts.host_side() {
+                symbol_variant = AbiVariant::GuestImport;
+            }
+        }
+        let func_name = if self.gen.opts.symmetric && matches!(is_drop, SpecialMethod::Dtor) {
+            // replace [dtor] with [resource_drop]
+            format!("[resource_drop]{}", &func.name[6..])
+        } else {
+            func.name.clone()
+        };
+        if self.gen.opts.short_cut {
+            uwrite!(self.gen.c_src.src, "extern \"C\" ");
+        } else if self.gen.opts.host {
+            self.gen.c_src.src.push_str("static ");
+        } else {
+            let module_prefix = module_name.as_ref().map_or(String::default(), |name| {
+                let mut res = name.clone();
+                res.push('#');
+                res
+            });
+            if self.gen.opts.symmetric {
+                uwriteln!(self.gen.c_src.src, r#"extern "C" "#);
+            } else {
+                uwriteln!(
+                    self.gen.c_src.src,
+                    r#"extern "C" __attribute__((__export_name__("{module_prefix}{func_name}")))"#
+                );
+            }
+        }
+        let return_via_pointer = signature.retptr && self.gen.opts.host_side();
+        self.gen
+            .c_src
+            .src
+            .push_str(if signature.results.is_empty() || return_via_pointer {
+                "void"
+            } else {
+                self.gen.opts.wasm_type(signature.results[0])
+            });
+        self.gen.c_src.src.push_str(" ");
+        let export_name = match module_name {
+            Some(ref module_name) => make_external_symbol(&module_name, &func_name, symbol_variant),
+            None => make_external_component(&func_name),
+        };
+        if let Some(prefix) = self.gen.opts.export_prefix.as_ref() {
+            self.gen.c_src.src.push_str(prefix);
+        }
+        self.gen.c_src.src.push_str(&export_name);
+        self.gen.c_src.src.push_str("(");
+        let mut first_arg = true;
+        if self.gen.opts.host {
+            self.gen.c_src.src.push_str("wasm_exec_env_t exec_env");
+            first_arg = false;
+        }
+        let mut params = Vec::new();
+        for (n, ty) in signature.params.iter().enumerate() {
+            let name = format!("arg{n}");
+            if !first_arg {
+                self.gen.c_src.src.push_str(", ");
+            } else {
+                first_arg = false;
+            }
+            self.gen.c_src.src.push_str(self.gen.opts.wasm_type(*ty));
+            self.gen.c_src.src.push_str(" ");
+            self.gen.c_src.src.push_str(&name);
+            params.push(name);
+        }
+        if return_via_pointer {
+            if !first_arg {
+                self.gen.c_src.src.push_str(", ");
+            }
+            // else {
+            //     first_arg = false;
+            // }
+            self.gen.c_src.src.push_str(self.gen.opts.ptr_type());
+            self.gen.c_src.src.push_str(" resultptr");
+            params.push("resultptr".into());
+        }
+        self.gen.c_src.src.push_str(")\n");
+        if self.gen.opts.host_side() {
+            let signature = wamr::wamr_signature(self.resolve, func);
+            let remember = HostFunction {
+                wasm_name: func_name.clone(),
+                wamr_signature: signature.to_string(),
+                host_name: export_name.clone(),
+            };
+            self.gen
+                .host_functions
+                .entry(module_name.unwrap_or(self.gen.world.clone()))
+                .and_modify(|v| v.push(remember.clone()))
+                .or_insert(vec![remember]);
+        }
+        params
+    }
+
+    fn high_level_signature(
+        &mut self,
+        func: &Function,
+        abi_variant: AbiVariant,
+        // import: bool,
+        _from_namespace: &Vec<String>,
+    ) -> HighlevelSignature {
+        let mut res = HighlevelSignature::default();
+        // let abi_variant = if import ^ self.gen.opts.host_side() {
+        //     AbiVariant::GuestImport
+        // } else {
+        //     AbiVariant::GuestExport
+        // };
+
+        let (namespace, func_name_h) =
+            self.func_namespace_name(func, matches!(abi_variant, AbiVariant::GuestExport), false);
+        res.name = func_name_h;
+        res.namespace = namespace;
+        let is_drop = is_special_method(func);
+        // we might want to separate c_sig and h_sig
+        // let mut sig = String::new();
+        if self.gen.opts.symmetric && matches!(is_drop, SpecialMethod::ResourceNew) {
+            res.result = "uint8_t*".into();
+        } else
+        // not for ctor nor imported dtor on guest
+        if !matches!(&func.kind, FunctionKind::Constructor(_))
+            && !(matches!(is_drop, SpecialMethod::ResourceDrop)
+                && matches!(abi_variant, AbiVariant::GuestImport)
+                && !self.gen.opts.host_side())
+            && !(matches!(is_drop, SpecialMethod::Dtor)
+                && matches!(abi_variant, AbiVariant::GuestExport)
+                && self.gen.opts.host_side())
+        {
+            if let Some(ty) = &func.result {
+                res.result.push_str(&self.type_name(
+                    ty,
+                    &res.namespace,
+                    Flavor::Result(abi_variant),
+                ));
+                res.result.push('>');
+            } else {
+                res.result = "void".into();
+            }
+            if matches!(abi_variant, AbiVariant::GuestExport)
+                && abi::guest_export_needs_post_return(self.resolve, func)
+            {
+                res.post_return = true;
+            }
+        }
+        if matches!(func.kind, FunctionKind::Static(_))
+            && !(matches!(&is_drop, SpecialMethod::ResourceDrop)
+                && matches!(abi_variant, AbiVariant::GuestImport)
+                && !self.gen.opts.host_side())
+            && !(matches!(&is_drop, SpecialMethod::Dtor)
+                && matches!(abi_variant, AbiVariant::GuestExport)
+                && self.gen.opts.host_side())
+        {
+            res.static_member = true;
+        }
+        for (i, (name, param)) in func.params.iter().enumerate() {
+            if i == 0
+                && name == "self"
+                && (matches!(&func.kind, FunctionKind::Method(_))
+                    || (matches!(&is_drop, SpecialMethod::ResourceDrop)
+                        && matches!(abi_variant, AbiVariant::GuestImport)
+                        && !self.gen.opts.host_side())
+                    || (matches!(&is_drop, SpecialMethod::Dtor)
+                        && matches!(abi_variant, AbiVariant::GuestExport)
+                        && self.gen.opts.host_side()))
+            {
+                res.implicit_self = true;
+                continue;
+            }
+            if self.gen.opts.symmetric
+                && matches!(
+                    &is_drop,
+                    SpecialMethod::ResourceRep | SpecialMethod::ResourceDrop
+                )
+            {
+                res.arguments
+                    .push((name.to_snake_case(), "uint8_t*".into()));
+            } else if matches!(
+                (&is_drop, self.gen.opts.host_side()),
+                (SpecialMethod::Dtor, _)
+                    | (SpecialMethod::ResourceNew, _)
+                    | (SpecialMethod::ResourceDrop, true)
+            ) {
+                res.arguments.push((
+                    name.to_snake_case(),
+                    self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)) + "*",
+                ));
+            } else {
+                res.arguments.push((
+                    name.to_snake_case(),
+                    self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)),
+                ));
+            }
+        }
+        // default to non-const when exporting a method
+        let import = matches!(abi_variant, AbiVariant::GuestImport) ^ self.gen.opts.host_side();
+        if matches!(func.kind, FunctionKind::Method(_)) && import {
+            res.const_member = true;
+        }
+        res
+    }
+
+    fn print_signature(
+        &mut self,
+        func: &Function,
+        variant: AbiVariant,
+        import: bool,
+    ) -> Vec<String> {
+        let is_special = is_special_method(func);
+        if !(import == true
+            && self.gen.opts.host_side()
+            && matches!(
+                &is_special,
+                SpecialMethod::ResourceDrop
+                    | SpecialMethod::ResourceNew
+                    | SpecialMethod::ResourceRep
+            ))
+        {
+            let from_namespace = self.gen.h_src.namespace.clone();
+            let cpp_sig = self.high_level_signature(func, variant, &from_namespace);
+            if cpp_sig.static_member {
+                self.gen.h_src.src.push_str("static ");
+            }
+            if cpp_sig.post_return && self.gen.opts.host_side() {
+                self.gen.h_src.src.push_str("wit::guest_owned<");
+            }
+            self.gen.h_src.src.push_str(&cpp_sig.result);
+            if cpp_sig.post_return && self.gen.opts.host_side() {
+                self.gen.h_src.src.push_str(">");
+            }
+            if !cpp_sig.result.is_empty() {
+                self.gen.h_src.src.push_str(" ");
+            }
+            self.gen.h_src.src.push_str(&cpp_sig.name);
+            self.gen.h_src.src.push_str("(");
+            if
+            /*import &&*/
+            self.gen.opts.host && !matches!(func.kind, FunctionKind::Method(_)) {
+                self.gen.h_src.src.push_str("WASMExecEnv* exec_env");
+                if !cpp_sig.arguments.is_empty() {
+                    self.gen.h_src.src.push_str(", ");
+                }
+            }
+            for (num, (arg, typ)) in cpp_sig.arguments.iter().enumerate() {
+                if num > 0 {
+                    self.gen.h_src.src.push_str(", ");
+                }
+                self.gen.h_src.src.push_str(typ);
+                self.gen.h_src.src.push_str(" ");
+                self.gen.h_src.src.push_str(arg);
+            }
+            self.gen.h_src.src.push_str(")");
+            if cpp_sig.const_member {
+                self.gen.h_src.src.push_str(" const");
+            }
+            match (&is_special, self.gen.opts.host_side(), &variant) {
+                (SpecialMethod::Allocate, _, _) => {
+                    uwrite!(
+                        self.gen.h_src.src,
+                        "{{\
+                        return {OWNED_CLASS_NAME}(new {}({}));\
+                    }}",
+                        cpp_sig.namespace.last().unwrap(), //join("::"),
+                        cpp_sig
+                            .arguments
+                            .iter()
+                            .map(|(arg, _)| arg.clone())
+                            .collect::<Vec<_>>()
+                            .join(", ")
+                    );
+                    // body is inside the header
+                    return Vec::default();
+                }
+                (SpecialMethod::Dtor, _, AbiVariant::GuestImport)
+                | (SpecialMethod::ResourceDrop, true, _) => {
+                    uwrite!(
+                        self.gen.h_src.src,
+                        "{{\
+                        delete {};\
+                    }}",
+                        cpp_sig.arguments.get(0).unwrap().0
+                    );
+                }
+                // SpecialMethod::None => todo!(),
+                // SpecialMethod::ResourceDrop => todo!(),
+                // SpecialMethod::ResourceNew => todo!(),
+                _ => self.gen.h_src.src.push_str(";\n"),
+            }
+        }
+        //        drop(cpp_sig);
+
+        // we want to separate the lowered signature (wasm) and the high level signature
+        if (!import
+            && (self.gen.opts.host_side()
+                || !matches!(
+                    &is_special,
+                    SpecialMethod::ResourceDrop
+                        | SpecialMethod::ResourceNew
+                        | SpecialMethod::ResourceRep
+                )))
+            || (import
+                && self.gen.opts.host_side()
+                && matches!(
+                    &is_special,
+                    SpecialMethod::ResourceDrop
+                        | SpecialMethod::ResourceNew
+                        | SpecialMethod::ResourceRep
+                ))
+        {
+            self.print_export_signature(func, variant)
+        } else {
+            // recalulate with c file namespace
+            let c_namespace = self.gen.c_src.namespace.clone();
+            let cpp_sig = self.high_level_signature(func, variant, &c_namespace);
+            let mut params = Vec::new();
+            if cpp_sig.post_return && self.gen.opts.host_side() {
+                self.gen.c_src.src.push_str("wit::guest_owned<");
+            }
+            self.gen.c_src.src.push_str(&cpp_sig.result);
+            if cpp_sig.post_return && self.gen.opts.host_side() {
+                self.gen.c_src.src.push_str(">");
+            }
+            if !cpp_sig.result.is_empty() {
+                self.gen.c_src.src.push_str(" ");
+            }
+            self.gen.c_src.qualify(&cpp_sig.namespace);
+            self.gen.c_src.src.push_str(&cpp_sig.name);
+            self.gen.c_src.src.push_str("(");
+            if import && self.gen.opts.host && !matches!(func.kind, FunctionKind::Method(_)) {
+                self.gen.c_src.src.push_str("wasm_exec_env_t exec_env");
+                if !cpp_sig.arguments.is_empty() || cpp_sig.implicit_self {
+                    self.gen.c_src.src.push_str(", ");
+                }
+            }
+            if cpp_sig.implicit_self {
+                params.push("(*this)".into());
+            }
+            for (num, (arg, typ)) in cpp_sig.arguments.iter().enumerate() {
+                if num > 0 {
+                    self.gen.c_src.src.push_str(", ");
+                }
+                self.gen.c_src.src.push_str(typ);
+                self.gen.c_src.src.push_str(" ");
+                self.gen.c_src.src.push_str(arg);
+                params.push(arg.clone());
+            }
+            self.gen.c_src.src.push_str(")");
+            if cpp_sig.const_member {
+                self.gen.c_src.src.push_str(" const");
+            }
+            self.gen.c_src.src.push_str("\n");
+            params
+        }
+    }
+
+    fn generate_function(
+        &mut self,
+        func: &Function,
+        owner: &TypeOwner,
+        //interface: InterfaceId,
+        variant: AbiVariant,
+    ) {
+        fn class_namespace(
+            cifg: &CppInterfaceGenerator,
+            func: &Function,
+            variant: AbiVariant,
+        ) -> Vec<String> {
+            let owner = &cifg.resolve.types[match &func.kind {
+                FunctionKind::Static(id) => *id,
+                _ => panic!("special func should be static"),
+            }];
+            let mut namespace = namespace(
+                cifg.resolve,
+                &owner.owner,
+                matches!(variant, AbiVariant::GuestExport),
+                &cifg.gen.opts,
+            );
+            namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case());
+            namespace
+        }
+
+        let export = match variant {
+            AbiVariant::GuestImport => self.gen.opts.host_side(),
+            AbiVariant::GuestExport => !self.gen.opts.host_side(),
+            AbiVariant::GuestImportAsync => todo!(),
+            AbiVariant::GuestExportAsync => todo!(),
+            AbiVariant::GuestExportAsyncStackful => todo!(),
+        };
+        let params = self.print_signature(func, variant, !export);
+        let special = is_special_method(func);
+        if !matches!(special, SpecialMethod::Allocate) {
+            self.gen.c_src.src.push_str("{\n");
+            let needs_dealloc = if self.gen.opts.new_api
+                && matches!(variant, AbiVariant::GuestExport)
+                && ((!self.gen.opts.symmetric
+                    && symmetric::needs_dealloc(self.resolve, &func.params))
+                    || (self.gen.opts.symmetric
+                        && symmetric::has_non_canonical_list(self.resolve, &func.params)))
+            {
+                self.gen
+                    .c_src
+                    .src
+                    .push_str("std::vector<void*> _deallocate;\n");
+                self.gen.dependencies.needs_vector = true;
+                true
+            } else {
+                false
+            };
+            let lift_lower = if self.gen.opts.symmetric {
+                LiftLower::Symmetric
+            } else if export {
+                LiftLower::LiftArgsLowerResults
+            } else {
+                LiftLower::LowerArgsLiftResults
+            };
+            match is_special_method(func) {
+                SpecialMethod::ResourceDrop => match lift_lower {
+                    LiftLower::LiftArgsLowerResults => {
+                        if self.gen.opts.host_side() {
+                            let namespace = class_namespace(self, func, variant);
+                            uwrite!(self.gen.c_src.src, "  auto ptr = ");
+                            self.gen.c_src.qualify(&namespace);
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "remove_resource({});
+                                assert(ptr.has_value());",
+                                params[0]
+                            );
+                            self.gen.dependencies.needs_assert = true;
+                            self.gen.c_src.qualify(&namespace);
+                            uwriteln!(self.gen.c_src.src, "Dtor(*ptr);")
+                        } else {
+                            let module_name = String::from("[export]")
+                                + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                            let wasm_sig = self.declare_import(
+                                &module_name,
+                                &func.name,
+                                &[WasmType::I32],
+                                &[],
+                            );
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "{wasm_sig}({});",
+                                func.params.get(0).unwrap().0
+                            );
+                        }
+                    }
+                    LiftLower::LowerArgsLiftResults => {
+                        if self.gen.opts.host_side() {
+                            let namespace = class_namespace(self, func, variant);
+                            self.gen.c_src.qualify(&namespace);
+                            uwriteln!(self.gen.c_src.src, "remove_resource(arg0);");
+                        } else {
+                            let module_name =
+                                self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                            let name = self.declare_import(
+                                &module_name,
+                                &func.name,
+                                &[WasmType::I32],
+                                &[],
+                            );
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "   if (handle>=0) {{
+                                {name}(handle);
+                            }}"
+                            );
+                        }
+                    }
+                    LiftLower::Symmetric => {
+                        let module_name =
+                            self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                        if matches!(variant, AbiVariant::GuestExport) {
+                            let mut namespace = class_namespace(self, func, variant);
+                            self.gen.c_src.qualify(&namespace);
+                            self.gen.c_src.src.push_str("Dtor((");
+                            let classname = namespace.pop().unwrap_or_default();
+                            self.gen.c_src.qualify(&namespace);
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "{classname}*){});",
+                                func.params.get(0).unwrap().0
+                            );
+                        } else {
+                            let name = self.declare_import(
+                                &module_name,
+                                &func.name,
+                                &[WasmType::Pointer],
+                                &[],
+                            );
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "   if (handle!=nullptr) {{
+                                {name}(handle);
+                            }}"
+                            );
+                        }
+                    }
+                },
+                SpecialMethod::Dtor => {
+                    if self.gen.opts.host_side() {
+                        let module_name =
+                            self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                        let name = self.declare_import(
+                            &module_name,
+                            &func.name,
+                            &[WasmType::Pointer],
+                            &[],
+                        );
+                        uwriteln!(
+                            self.gen.c_src.src,
+                            "if (this->rep) {{ {name}(this->rep); }}"
+                        );
+                    } else {
+                        let classname = class_namespace(self, func, variant).join("::");
+                        if self.gen.opts.symmetric {
+                            uwriteln!(
+                                self.gen.c_src.src,
+                                "{}::ResourceDrop(({})arg0);",
+                                classname,
+                                self.gen.opts.ptr_type()
+                            );
+                        } else {
+                            uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;");
+                            uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname);
+                        }
+                    }
+                }
+                SpecialMethod::ResourceNew => {
+                    if self.gen.opts.symmetric {
+                        uwriteln!(
+                            self.gen.c_src.src,
+                            "return ({}){};",
+                            self.gen.opts.ptr_type(),
+                            func.params.get(0).unwrap().0
+                        );
+                    } else if !self.gen.opts.host_side() {
+                        let module_name = String::from("[export]")
+                            + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                        let wasm_sig = self.declare_import(
+                            &module_name,
+                            &func.name,
+                            &[WasmType::Pointer],
+                            &[WasmType::I32],
+                        );
+                        uwriteln!(
+                            self.gen.c_src.src,
+                            "return {wasm_sig}(({}){});",
+                            self.gen.opts.ptr_type(),
+                            func.params.get(0).unwrap().0
+                        );
+                    } else {
+                        uwriteln!(self.gen.c_src.src, "return ");
+                        let namespace = class_namespace(self, func, variant);
+                        self.gen.c_src.qualify(&namespace);
+                        uwriteln!(self.gen.c_src.src, "store_resource(std::move(arg0));");
+                    }
+                }
+                SpecialMethod::ResourceRep => {
+                    if self.gen.opts.symmetric {
+                        let classname = class_namespace(self, func, variant).join("::");
+                        uwriteln!(
+                            self.gen.c_src.src,
+                            "return ({}*){};",
+                            classname,
+                            func.params.get(0).unwrap().0
+                        );
+                    } else if !self.gen.opts.host_side() {
+                        let module_name = String::from("[export]")
+                            + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap();
+                        let wasm_sig = self.declare_import(
+                            &module_name,
+                            &func.name,
+                            &[WasmType::I32],
+                            &[WasmType::Pointer],
+                        );
+                        let classname = class_namespace(self, func, variant).join("::");
+                        uwriteln!(
+                            self.gen.c_src.src,
+                            "return ({}*){wasm_sig}({});",
+                            classname,
+                            func.params.get(0).unwrap().0
+                        );
+                    } else {
+                        uwriteln!(self.gen.c_src.src, "return *");
+                        let namespace = class_namespace(self, func, variant);
+                        self.gen.c_src.qualify(&namespace);
+                        uwriteln!(self.gen.c_src.src, "lookup_resource(arg0);",);
+                    }
+                }
+                SpecialMethod::Allocate => unreachable!(),
+                SpecialMethod::None => {
+                    // normal methods
+                    let namespace = if matches!(func.kind, FunctionKind::Freestanding) {
+                        namespace(
+                            self.resolve,
+                            owner,
+                            matches!(variant, AbiVariant::GuestExport),
+                            &self.gen.opts,
+                        )
+                    } else {
+                        let owner = &self.resolve.types[match &func.kind {
+                            FunctionKind::Static(id) => *id,
+                            FunctionKind::Constructor(id) => *id,
+                            FunctionKind::Method(id) => *id,
+                            FunctionKind::Freestanding => unreachable!(),
+                            FunctionKind::AsyncFreestanding => todo!(),
+                            FunctionKind::AsyncMethod(_id) => todo!(),
+                            FunctionKind::AsyncStatic(_id) => todo!(),
+                        }]
+                        .clone();
+                        let mut namespace = namespace(
+                            self.resolve,
+                            &owner.owner,
+                            matches!(variant, AbiVariant::GuestExport),
+                            &self.gen.opts,
+                        );
+                        namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case());
+                        namespace
+                    };
+                    let mut f = FunctionBindgen::new(self, params);
+                    if !export {
+                        f.namespace = namespace.clone();
+                        f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func));
+                    }
+                    f.variant = variant;
+                    f.needs_dealloc = needs_dealloc;
+                    f.cabi_post = if matches!(variant, AbiVariant::GuestExport)
+                        && f.gen.gen.opts.host_side()
+                        && abi::guest_export_needs_post_return(f.gen.resolve, func)
+                    {
+                        let module_name = f
+                            .gen
+                            .wasm_import_module
+                            .as_ref()
+                            .map(|e| e.clone())
+                            .unwrap();
+                        let cpp_sig = f.gen.high_level_signature(func, variant, &namespace);
+                        Some(CabiPostInformation {
+                            module: module_name,
+                            name: func.name.clone(),
+                            ret_type: cpp_sig.result,
+                        })
+                    } else {
+                        None
+                    };
+                    abi::call(f.gen.resolve, variant, lift_lower, func, &mut f, false);
+                    let code = String::from(f.src);
+                    self.gen.c_src.src.push_str(&code);
+                }
+            }
+            self.gen.c_src.src.push_str("}\n");
+            // cabi_post
+            if !self.gen.opts.host_side()
+                && !matches!(lift_lower, LiftLower::Symmetric)
+                && matches!(variant, AbiVariant::GuestExport)
+                && abi::guest_export_needs_post_return(self.resolve, func)
+            {
+                let sig = self.patched_wasm_signature(variant, func);
+                let module_name = self.wasm_import_module.as_ref().map(|e| e.clone());
+                let export_name = match module_name {
+                    Some(ref module_name) => {
+                        // let symbol_variant = if self.gen.opts.symmetric {
+                        //     AbiVariant::GuestImport
+                        // } else {
+                        //     variant
+                        // };
+                        // make_external_symbol(module_name, &func.name, symbol_variant)
+                        format!("{module_name}#{}", func.name)
+                    }
+                    None => make_external_component(&func.name),
+                };
+                //let export_name = func.core_export_name(Some(&module_name));
+                let import_name = match module_name {
+                    Some(ref module_name) => make_external_symbol(
+                        module_name,
+                        &func.name,
+                        if self.gen.opts.symmetric {
+                            AbiVariant::GuestImport
+                        } else {
+                            AbiVariant::GuestExport
+                        },
+                    ),
+                    None => make_external_component(&func.name),
+                };
+                // make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport);
+                // let module_prefix = module_name.as_ref().map_or(String::default(), |name| {
+                //         let mut res = name.clone();
+                //         res.push('#');
+                //         res
+                //     });
+                uwriteln!(
+                    self.gen.c_src.src,
+                    "extern \"C\" __attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))"
+                );
+                uwrite!(self.gen.c_src.src, "void cabi_post_{import_name}(");
+
+                let mut params = Vec::new();
+                for (i, result) in sig.results.iter().enumerate() {
+                    let name = format!("arg{i}");
+                    uwrite!(
+                        self.gen.c_src.src,
+                        "{} {name}",
+                        self.gen.opts.wasm_type(*result)
+                    );
+                    params.push(name);
+                }
+                if sig.retptr && self.gen.opts.symmetric {
+                    let name = "retptr";
+                    uwrite!(
+                        self.gen.c_src.src,
+                        "{} {name}",
+                        self.gen.opts.wasm_type(WasmType::Pointer)
+                    );
+                    params.push(name.into());
+                }
+                self.gen.c_src.src.push_str(") {\n");
+
+                let mut f = FunctionBindgen::new(self, params.clone());
+                f.params = params;
+                abi::post_return(f.gen.resolve, func, &mut f, false);
+                let FunctionBindgen { src, .. } = f;
+                self.gen.c_src.src.push_str(&src);
+                self.gen.c_src.src.push_str("}\n");
+            }
+        }
+    }
+
+    pub fn type_path(&self, id: TypeId, owned: bool) -> String {
+        self.type_path_with_name(
+            id,
+            if owned {
+                self.result_name(id)
+            } else {
+                self.param_name(id)
+            },
+        )
+    }
+
+    fn type_path_with_name(&self, id: TypeId, name: String) -> String {
+        if let TypeOwner::Interface(id) = self.resolve.types[id].owner {
+            if let Some(path) = self.path_to_interface(id) {
+                return format!("{path}::{name}");
+            }
+        }
+        name
+    }
+
+    fn path_to_interface(&self, interface: InterfaceId) -> Option<String> {
+        let iface = &self.resolve.interfaces[interface];
+        let name = iface.name.as_ref().unwrap();
+        let mut full_path = String::new();
+        full_path.push_str(name);
+        Some(full_path)
+    }
+
+    fn param_name(&self, ty: TypeId) -> String {
+        self.resolve.types[ty]
+            .name
+            .as_ref()
+            .unwrap()
+            .to_upper_camel_case()
+    }
+
+    fn result_name(&self, ty: TypeId) -> String {
+        self.resolve.types[ty]
+            .name
+            .as_ref()
+            .unwrap()
+            .to_upper_camel_case()
+    }
+
+    // in C this is print_optional_ty
+    fn optional_type_name(
+        &mut self,
+        ty: Option<&Type>,
+        from_namespace: &Vec<String>,
+        flavor: Flavor,
+    ) -> String {
+        match ty {
+            Some(ty) => self.type_name(ty, from_namespace, flavor),
+            None => "void".into(),
+        }
+    }
+
+    fn scoped_type_name(
+        &self,
+        id: TypeId,
+        from_namespace: &Vec<String>,
+        guest_export: bool,
+    ) -> String {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(self.resolve, &ty.owner, guest_export, &self.gen.opts);
+        let mut relative = SourceWithState::default();
+        relative.namespace = from_namespace.clone();
+        relative.qualify(&namespc);
+        format!(
+            "{}{}",
+            relative.src.to_string(),
+            ty.name.as_ref().unwrap().to_pascal_case()
+        )
+    }
+
+    fn type_name(&mut self, ty: &Type, from_namespace: &Vec<String>, flavor: Flavor) -> String {
+        match ty {
+            Type::Bool => "bool".into(),
+            Type::Char => "uint32_t".into(),
+            Type::U8 => "uint8_t".into(),
+            Type::S8 => "int8_t".into(),
+            Type::U16 => "uint16_t".into(),
+            Type::S16 => "int16_t".into(),
+            Type::U32 => "uint32_t".into(),
+            Type::S32 => "int32_t".into(),
+            Type::U64 => "uint64_t".into(),
+            Type::S64 => "int64_t".into(),
+            Type::F32 => "float".into(),
+            Type::F64 => "double".into(),
+            Type::String => match flavor {
+                Flavor::BorrowedArgument => {
+                    self.gen.dependencies.needs_string_view = true;
+                    "std::string_view".into()
+                }
+                Flavor::Argument(var)
+                    if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api =>
+                {
+                    self.gen.dependencies.needs_string_view = true;
+                    "std::string_view".into()
+                }
+                Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host_side() => {
+                    self.gen.dependencies.needs_wit = true;
+                    "wit::string &&".into()
+                }
+                Flavor::Result(AbiVariant::GuestExport) if self.gen.opts.host_side() => {
+                    self.gen.dependencies.needs_string_view = true;
+                    "std::string_view".into()
+                }
+                _ => {
+                    self.gen.dependencies.needs_wit = true;
+                    "wit::string".into()
+                }
+            },
+            Type::Id(id) => match &self.resolve.types[*id].kind {
+                TypeDefKind::Record(_r) => {
+                    self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE)
+                }
+                TypeDefKind::Resource => {
+                    self.scoped_type_name(*id, from_namespace, flavor.is_guest_export())
+                }
+                TypeDefKind::Handle(Handle::Own(id)) => {
+                    let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor);
+                    match (self.gen.opts.host_side(), flavor) {
+                        (false, Flavor::Argument(AbiVariant::GuestImport))
+                        | (true, Flavor::Argument(AbiVariant::GuestExport)) => {
+                            typename.push_str("&&")
+                        }
+                        (false, Flavor::Argument(AbiVariant::GuestExport))
+                        | (false, Flavor::Result(AbiVariant::GuestExport))
+                        | (true, Flavor::Argument(AbiVariant::GuestImport))
+                        | (true, Flavor::Result(AbiVariant::GuestImport)) => {
+                            typename.push_str(&format!("::{OWNED_CLASS_NAME}"))
+                        }
+                        (false, Flavor::Result(AbiVariant::GuestImport))
+                        | (true, Flavor::Result(AbiVariant::GuestExport)) => (),
+                        (_, Flavor::InStruct) => (),
+                        (false, Flavor::BorrowedArgument) => (),
+                        (_, _) => todo!(),
+                    }
+                    typename
+                }
+                TypeDefKind::Handle(Handle::Borrow(id)) => {
+                    "std::reference_wrapper<const ".to_string()
+                        + &self.type_name(&Type::Id(*id), from_namespace, flavor)
+                        + ">"
+                }
+                TypeDefKind::Flags(_f) => {
+                    self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE)
+                }
+                TypeDefKind::Tuple(t) => {
+                    let types = t.types.iter().fold(String::new(), |mut a, b| {
+                        if !a.is_empty() {
+                            a += ", ";
+                        }
+                        a + &self.type_name(b, from_namespace, flavor)
+                    });
+                    self.gen.dependencies.needs_tuple = true;
+                    String::from("std::tuple<") + &types + ">"
+                }
+                TypeDefKind::Variant(_v) => {
+                    self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE)
+                }
+                TypeDefKind::Enum(_e) => {
+                    self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE)
+                }
+                TypeDefKind::Option(o) => {
+                    self.gen.dependencies.needs_optional = true;
+                    "std::optional<".to_string() + &self.type_name(o, from_namespace, flavor) + ">"
+                }
+                TypeDefKind::Result(r) => {
+                    self.gen.dependencies.needs_expected = true;
+                    "std::expected<".to_string()
+                        + &self.optional_type_name(r.ok.as_ref(), from_namespace, flavor)
+                        + ", "
+                        + &self.optional_type_name(r.err.as_ref(), from_namespace, flavor)
+                        + ">"
+                }
+                TypeDefKind::List(ty) => {
+                    let inner = self.type_name(ty, from_namespace, flavor);
+                    match flavor {
+                        Flavor::BorrowedArgument => {
+                            self.gen.dependencies.needs_wit = true;
+                            format!("wit::span<{inner} const>")
+                        }
+                        //self.gen.dependencies.needs_vector = true;
+                        Flavor::Argument(var)
+                            if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api =>
+                        {
+                            self.gen.dependencies.needs_wit = true;
+                            format!("wit::span<{inner} const>")
+                        }
+                        Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host => {
+                            self.gen.dependencies.needs_wit = true;
+                            format!("wit::vector<{inner}>&&")
+                        }
+                        Flavor::Result(AbiVariant::GuestExport) if self.gen.opts.host => {
+                            self.gen.dependencies.needs_wit = true;
+                            format!("wit::span<{inner} const>")
+                        }
+                        _ => {
+                            self.gen.dependencies.needs_wit = true;
+                            format!("wit::vector<{inner}>")
+                        }
+                    }
+                }
+                TypeDefKind::Future(_) => todo!(),
+                TypeDefKind::Stream(_) => todo!(),
+                TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor),
+                TypeDefKind::Unknown => todo!(),
+            },
+            Type::ErrorContext => todo!(),
+        }
+    }
+
+    fn declare_import2(
+        &self,
+        module_name: &str,
+        name: &str,
+        args: &str,
+        result: &str,
+        variant: AbiVariant,
+    ) -> (String, String) {
+        let extern_name = make_external_symbol(module_name, name, variant);
+        let import = if self.gen.opts.symmetric {
+            format!("extern \"C\" {result} {extern_name}({args});\n")
+        } else {
+            format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n")
+        };
+        (extern_name, import)
+    }
+
+    fn declare_import(
+        &mut self,
+        module_name: &str,
+        name: &str,
+        params: &[WasmType],
+        results: &[WasmType],
+    ) -> String {
+        let mut args = String::default();
+        for (n, param) in params.iter().enumerate() {
+            args.push_str(self.gen.opts.wasm_type(*param));
+            if n + 1 != params.len() {
+                args.push_str(", ");
+            }
+        }
+        let result = if results.is_empty() {
+            "void"
+        } else {
+            self.gen.opts.wasm_type(results[0])
+        };
+        let variant = if self.gen.opts.short_cut {
+            AbiVariant::GuestExport
+        } else {
+            AbiVariant::GuestImport
+        };
+        let (name, code) = self.declare_import2(module_name, name, &args, result, variant);
+        self.gen.extern_c_decls.push_str(&code);
+        name
+    }
+
+    fn docs(src: &mut Source, docs: &Docs) {
+        if let Some(docs) = docs.contents.as_ref() {
+            for line in docs.trim().lines() {
+                src.push_str("/// ");
+                src.push_str(line);
+                src.push_str("\n");
+            }
+        }
+    }
+}
+
+impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> {
+    fn resolve(&self) -> &'a Resolve {
+        self.resolve
+    }
+
+    fn type_record(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        record: &wit_bindgen_core::wit_parser::Record,
+        docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(
+            self.resolve,
+            &ty.owner,
+            NOT_IN_EXPORTED_NAMESPACE,
+            &self.gen.opts,
+        );
+        if self.gen.is_first_definition(&namespc, name) {
+            self.gen.h_src.change_namespace(&namespc);
+            Self::docs(&mut self.gen.h_src.src, docs);
+            let pascal = name.to_pascal_case();
+            uwriteln!(self.gen.h_src.src, "struct {pascal} {{");
+            for field in record.fields.iter() {
+                Self::docs(&mut self.gen.h_src.src, &field.docs);
+                let typename = self.type_name(&field.ty, &namespc, Flavor::InStruct);
+                let fname = field.name.to_snake_case();
+                uwriteln!(self.gen.h_src.src, "{typename} {fname};");
+            }
+            uwriteln!(self.gen.h_src.src, "}};");
+        }
+    }
+
+    fn type_resource(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let type_ = &self.resolve.types[id];
+        if let TypeOwner::Interface(intf) = type_.owner {
+            let guest_import = self.gen.imported_interfaces.contains(&intf);
+            let definition = !(guest_import ^ self.gen.opts.host_side());
+            let store = self.gen.start_new_file(Some(definition));
+            let mut world_name = self.gen.world.to_snake_case();
+            world_name.push_str("::");
+            // let mut headerfile = SourceWithState::default();
+            let namespc = namespace(self.resolve, &type_.owner, !guest_import, &self.gen.opts);
+            let pascal = name.to_upper_camel_case();
+            let mut user_filename = namespc.clone();
+            user_filename.push(pascal.clone());
+            //namespc.join("-") + "-" + &pascal + ".h";
+            if definition {
+                // includes should be outside of namespaces
+                //self.gen.h_src.change_namespace(&Vec::default());
+                // temporarily redirect header file declarations to an user controlled include file
+                //std::mem::swap(&mut headerfile, &mut self.gen.h_src);
+                uwriteln!(
+                    self.gen.h_src.src,
+                    r#"/* User class definition file, autogenerated once, then user modified
+                    * Updated versions of this file are generated into {pascal}.template.
+                    */"#
+                );
+            }
+            self.gen.h_src.change_namespace(&namespc);
+
+            if !definition {
+                self.gen.dependencies.needs_imported_resources = true;
+            } else {
+                self.gen.dependencies.needs_exported_resources = true;
+            }
+            self.gen.dependencies.needs_wit = true;
+            // for unique_ptr
+            // self.gen.dependencies.needs_memory = true;
+
+            let base_type = match (definition, self.gen.opts.host_side()) {
+                (true, false) => format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>"),
+                (false, false) => {
+                    String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME
+                }
+                (false, true) => {
+                    String::from_str("wit::").unwrap() + RESOURCE_EXPORT_BASE_CLASS_NAME
+                }
+                (true, true) => format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}<{pascal}>"),
+            };
+            let derive = format!(" : public {base_type}");
+            uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n");
+            uwriteln!(self.gen.h_src.src, "public:\n");
+            let variant = if guest_import {
+                AbiVariant::GuestImport
+            } else {
+                AbiVariant::GuestExport
+            };
+            {
+                // destructor
+                let name = match variant {
+                    AbiVariant::GuestImport => "[resource-drop]",
+                    AbiVariant::GuestExport => "[dtor]",
+                    AbiVariant::GuestImportAsync => todo!(),
+                    AbiVariant::GuestExportAsync => todo!(),
+                    AbiVariant::GuestExportAsyncStackful => todo!(),
+                }
+                // let name = match (variant, self.gen.opts.host_side()) {
+                //     (AbiVariant::GuestImport, false) | (AbiVariant::GuestExport, true) => {
+                //         "[resource-drop]"
+                //     }
+                //     (AbiVariant::GuestExport, false) | (AbiVariant::GuestImport, true) => "[dtor]",
+                // }
+                .to_string()
+                    + &name;
+                let func = Function {
+                    name: name,
+                    kind: FunctionKind::Static(id),
+                    params: vec![("self".into(), Type::Id(id))],
+                    result: None,
+                    docs: Docs::default(),
+                    stability: Stability::Unknown,
+                };
+                self.generate_function(&func, &TypeOwner::Interface(intf), variant);
+            }
+            // uwriteln!(self.gen.h_src.src, "struct Deleter {{
+            //             void operator()({pascal}* ptr) const {{ {pascal}::Dtor(ptr); }}
+            //         }};
+            //         typedef std::unique_ptr<{pascal}, {pascal}::Deleter> Owned;");
+            let funcs = self.resolve.interfaces[intf].functions.values();
+            for func in funcs {
+                if match &func.kind {
+                    FunctionKind::Freestanding => false,
+                    FunctionKind::Method(mid) => *mid == id,
+                    FunctionKind::Static(mid) => *mid == id,
+                    FunctionKind::Constructor(mid) => *mid == id,
+                    FunctionKind::AsyncFreestanding => todo!(),
+                    FunctionKind::AsyncMethod(_id) => todo!(),
+                    FunctionKind::AsyncStatic(_id) => todo!(),
+                } {
+                    self.generate_function(func, &TypeOwner::Interface(intf), variant);
+                    if matches!(func.kind, FunctionKind::Constructor(_))
+                        && matches!(variant, AbiVariant::GuestExport) != self.gen.opts.host_side()
+                    {
+                        // functional safety requires the option to use a different allocator, so move new into the implementation
+                        let func2 = Function {
+                            name: "$alloc".to_string(),
+                            kind: FunctionKind::Static(id),
+                            // same params as constructor
+                            params: func.params.clone(),
+                            result: Some(Type::Id(id)),
+                            docs: Docs::default(),
+                            stability: Stability::Unknown,
+                        };
+                        self.generate_function(&func2, &TypeOwner::Interface(intf), variant);
+                    }
+                }
+            }
+
+            if !definition {
+                // consuming constructor from handle (bindings)
+                uwriteln!(self.gen.h_src.src, "{pascal}({base_type} &&);",);
+                uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;");
+                uwriteln!(
+                    self.gen.h_src.src,
+                    "{pascal}& operator=({pascal}&&) = default;"
+                );
+                self.gen.c_src.qualify(&namespc);
+                uwriteln!(
+                    self.gen.c_src.src,
+                    "{pascal}::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}"
+                );
+            }
+            if matches!(variant, AbiVariant::GuestExport) {
+                let id_type = if self.gen.opts.symmetric {
+                    Type::Id(id)
+                } else {
+                    Type::S32
+                };
+                let func = Function {
+                    name: "[resource-new]".to_string() + &name,
+                    kind: FunctionKind::Static(id),
+                    params: vec![("self".into(), Type::Id(id))],
+                    result: Some(id_type),
+                    docs: Docs::default(),
+                    stability: Stability::Unknown,
+                };
+                self.generate_function(&func, &TypeOwner::Interface(intf), variant);
+
+                let func1 = Function {
+                    name: "[resource-rep]".to_string() + &name,
+                    kind: FunctionKind::Static(id),
+                    params: vec![("id".into(), id_type)],
+                    result: Some(Type::Id(id)),
+                    docs: Docs::default(),
+                    stability: Stability::Unknown,
+                };
+                self.generate_function(&func1, &TypeOwner::Interface(intf), variant);
+
+                let func2 = Function {
+                    name: "[resource-drop]".to_string() + &name,
+                    kind: FunctionKind::Static(id),
+                    params: vec![("id".into(), id_type)],
+                    result: None,
+                    docs: Docs::default(),
+                    stability: Stability::Unknown,
+                };
+                self.generate_function(&func2, &TypeOwner::Interface(intf), variant);
+            }
+            uwriteln!(self.gen.h_src.src, "}};\n");
+            self.gen.finish_file(&user_filename, store);
+            // if definition {
+            //     // Finish the user controlled class template
+            //     self.gen.h_src.change_namespace(&Vec::default());
+            //     std::mem::swap(&mut headerfile, &mut self.gen.h_src);
+            //     uwriteln!(self.gen.h_src.src, "#include \"{user_filename}\"");
+            //     if self.gen.opts.format {
+            //         Cpp::clang_format(&mut headerfile.src);
+            //     }
+            //     self.gen
+            //         .user_class_files
+            //         .insert(user_filename, headerfile.src.to_string());
+            // }
+        }
+    }
+
+    fn type_flags(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        flags: &wit_bindgen_core::wit_parser::Flags,
+        docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(
+            self.resolve,
+            &ty.owner,
+            NOT_IN_EXPORTED_NAMESPACE,
+            &self.gen.opts,
+        );
+        if self.gen.is_first_definition(&namespc, name) {
+            self.gen.h_src.change_namespace(&namespc);
+            Self::docs(&mut self.gen.h_src.src, docs);
+            let pascal = name.to_pascal_case();
+            let int_repr = wit_bindgen_c::int_repr(wit_bindgen_c::flags_repr(flags));
+            uwriteln!(self.gen.h_src.src, "enum class {pascal} : {int_repr} {{");
+            uwriteln!(self.gen.h_src.src, "k_None = 0,");
+            for (n, field) in flags.flags.iter().enumerate() {
+                Self::docs(&mut self.gen.h_src.src, &field.docs);
+                let fname = field.name.to_pascal_case();
+                uwriteln!(self.gen.h_src.src, "k{fname} = (1ULL<<{n}),");
+            }
+            uwriteln!(self.gen.h_src.src, "}};");
+            uwriteln!(
+                self.gen.h_src.src,
+                r#"static inline {pascal} operator|({pascal} a, {pascal} b) {{ return {pascal}({int_repr}(a)|{int_repr}(b)); }}
+        static inline {pascal} operator&({pascal} a, {pascal} b) {{ return {pascal}({int_repr}(a)&{int_repr}(b)); }}"#
+            );
+        }
+    }
+
+    fn type_tuple(
+        &mut self,
+        _id: TypeId,
+        _name: &str,
+        _flags: &wit_bindgen_core::wit_parser::Tuple,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        // I assume I don't need to do anything ...
+    }
+
+    fn type_variant(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        variant: &wit_bindgen_core::wit_parser::Variant,
+        docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(
+            self.resolve,
+            &ty.owner,
+            NOT_IN_EXPORTED_NAMESPACE,
+            &self.gen.opts,
+        );
+        self.gen.h_src.change_namespace(&namespc);
+        Self::docs(&mut self.gen.h_src.src, docs);
+        let pascal = name.to_pascal_case();
+        uwriteln!(self.gen.h_src.src, "struct {pascal} {{");
+        let mut all_types = String::new();
+        for case in variant.cases.iter() {
+            Self::docs(&mut self.gen.h_src.src, &case.docs);
+            let case_pascal = case.name.to_pascal_case();
+            if !all_types.is_empty() {
+                all_types += ", ";
+            }
+            all_types += &case_pascal;
+            uwrite!(self.gen.h_src.src, "struct {case_pascal} {{");
+            if let Some(ty) = case.ty.as_ref() {
+                let typestr = self.type_name(ty, &namespc, Flavor::InStruct);
+                uwrite!(self.gen.h_src.src, " {typestr} value; ")
+            }
+            uwriteln!(self.gen.h_src.src, "}};");
+        }
+        uwriteln!(self.gen.h_src.src, "  std::variant<{all_types}> variants;");
+        uwriteln!(self.gen.h_src.src, "}};");
+        self.gen.dependencies.needs_variant = true;
+    }
+
+    fn type_option(
+        &mut self,
+        _id: TypeId,
+        _name: &str,
+        _payload: &wit_bindgen_core::wit_parser::Type,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        // I assume I don't need to do anything ...
+    }
+
+    fn type_result(
+        &mut self,
+        _id: TypeId,
+        _name: &str,
+        _result: &wit_bindgen_core::wit_parser::Result_,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        // I assume I don't need to do anything ...
+    }
+
+    fn type_enum(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        enum_: &wit_bindgen_core::wit_parser::Enum,
+        docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(
+            self.resolve,
+            &ty.owner,
+            NOT_IN_EXPORTED_NAMESPACE,
+            &self.gen.opts,
+        );
+        if self.gen.is_first_definition(&namespc, name) {
+            self.gen.h_src.change_namespace(&namespc);
+            let pascal = name.to_pascal_case();
+            Self::docs(&mut self.gen.h_src.src, docs);
+            let int_t = wit_bindgen_c::int_repr(enum_.tag());
+            uwriteln!(self.gen.h_src.src, "enum class {pascal} : {int_t} {{");
+            for (i, case) in enum_.cases.iter().enumerate() {
+                Self::docs(&mut self.gen.h_src.src, &case.docs);
+                uwriteln!(
+                    self.gen.h_src.src,
+                    " k{} = {i},",
+                    case.name.to_pascal_case(),
+                );
+            }
+            uwriteln!(self.gen.h_src.src, "}};\n");
+        }
+    }
+
+    fn type_alias(
+        &mut self,
+        id: TypeId,
+        name: &str,
+        alias_type: &wit_bindgen_core::wit_parser::Type,
+        docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        let ty = &self.resolve.types[id];
+        let namespc = namespace(
+            self.resolve,
+            &ty.owner,
+            NOT_IN_EXPORTED_NAMESPACE,
+            &self.gen.opts,
+        );
+        self.gen.h_src.change_namespace(&namespc);
+        let pascal = name.to_pascal_case();
+        Self::docs(&mut self.gen.h_src.src, docs);
+        let typename = self.type_name(alias_type, &namespc, Flavor::InStruct);
+        uwriteln!(self.gen.h_src.src, "using {pascal} = {typename};");
+    }
+
+    fn type_list(
+        &mut self,
+        _id: TypeId,
+        _name: &str,
+        _ty: &wit_bindgen_core::wit_parser::Type,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        // I assume I don't need to do anything ... we could create a typedef though
+    }
+
+    fn type_builtin(
+        &mut self,
+        _id: TypeId,
+        _name: &str,
+        _ty: &wit_bindgen_core::wit_parser::Type,
+        _docs: &wit_bindgen_core::wit_parser::Docs,
+    ) {
+        todo!()
+    }
+
+    fn type_future(&mut self, _id: TypeId, _name: &str, _ty: &Option<Type>, _docs: &Docs) {
+        todo!()
+    }
+
+    fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option<Type>, _docs: &Docs) {
+        todo!()
+    }
+}
+
+struct CabiPostInformation {
+    module: String,
+    name: String,
+    ret_type: String,
+}
+
+struct FunctionBindgen<'a, 'b> {
+    gen: &'b mut CppInterfaceGenerator<'a>,
+    params: Vec<String>,
+    tmp: usize,
+    // import_return_pointer_area_size: usize,
+    // import_return_pointer_area_align: usize,
+    namespace: Vec<String>,
+    src: Source,
+    block_storage: Vec<wit_bindgen_core::Source>,
+    /// intermediate calculations for contained objects
+    blocks: Vec<(String, Vec<String>)>,
+    payloads: Vec<String>,
+    // caching for wasm
+    wamr_signature: Option<wamr::WamrSig>,
+    variant: AbiVariant,
+    cabi_post: Option<CabiPostInformation>,
+    needs_dealloc: bool,
+    leak_on_insertion: Option<String>,
+}
+
+impl<'a, 'b> FunctionBindgen<'a, 'b> {
+    fn new(gen: &'b mut CppInterfaceGenerator<'a>, params: Vec<String>) -> Self {
+        Self {
+            gen,
+            params,
+            tmp: 0,
+            // import_return_pointer_area_size: 0,
+            // import_return_pointer_area_align: 0,
+            namespace: Default::default(),
+            src: Default::default(),
+            block_storage: Default::default(),
+            blocks: Default::default(),
+            payloads: Default::default(),
+            wamr_signature: None,
+            variant: AbiVariant::GuestImport,
+            cabi_post: None,
+            needs_dealloc: false,
+            leak_on_insertion: None,
+        }
+    }
+
+    fn tmp(&mut self) -> usize {
+        let ret = self.tmp;
+        self.tmp += 1;
+        ret
+    }
+
+    fn tempname(&self, base: &str, idx: usize) -> String {
+        format!("{base}{idx}")
+    }
+
+    fn push_str(&mut self, s: &str) {
+        self.src.push_str(s);
+    }
+
+    fn typename_lift(&self, id: TypeId) -> String {
+        self.gen.type_path(id, true)
+    }
+
+    fn let_results(&mut self, amt: usize, results: &mut Vec<String>) {
+        if amt > 0 {
+            let tmp = self.tmp();
+            let res = format!("result{}", tmp);
+            self.push_str("auto ");
+            self.push_str(&res);
+            self.push_str(" = ");
+            if amt == 1 {
+                results.push(res);
+            } else {
+                for i in 0..amt {
+                    results.push(format!("std::get<{i}>({res})"));
+                }
+            }
+        }
+    }
+
+    fn load(
+        &mut self,
+        ty: &str,
+        offset: ArchitectureSize,
+        operands: &[String],
+        results: &mut Vec<String>,
+    ) {
+        if self.gen.gen.opts.host {
+            results.push(format!("*(({}*) wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), ({} + {})))", ty, operands[0], offset.format(POINTER_SIZE_EXPRESSION)));
+        } else {
+            results.push(format!(
+                "*(({}*) ({} + {}))",
+                ty,
+                operands[0],
+                offset.format(POINTER_SIZE_EXPRESSION)
+            ));
+        }
+    }
+
+    fn load_ext(
+        &mut self,
+        ty: &str,
+        offset: ArchitectureSize,
+        operands: &[String],
+        results: &mut Vec<String>,
+    ) {
+        self.load(ty, offset, operands, results);
+        let result = results.pop().unwrap();
+        results.push(format!("(int32_t) ({})", result));
+    }
+
+    fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) {
+        if self.gen.gen.opts.host {
+            uwriteln!(
+                self.src,
+                "*(({}*)wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), ({} + {}))) = {};",
+                ty,
+                operands[1],
+                offset.format(POINTER_SIZE_EXPRESSION),
+                operands[0]
+            );
+        } else {
+            uwriteln!(
+                self.src,
+                "*(({}*)({} + {})) = {};",
+                ty,
+                operands[1],
+                offset.format(POINTER_SIZE_EXPRESSION),
+                operands[0]
+            );
+        }
+    }
+
+    fn has_resources2(&self, ty: &Type) -> bool {
+        match ty {
+            Type::Bool
+            | Type::U8
+            | Type::U16
+            | Type::U32
+            | Type::U64
+            | Type::S8
+            | Type::S16
+            | Type::S32
+            | Type::S64
+            | Type::F32
+            | Type::F64
+            | Type::Char => false,
+            Type::String => false,
+            Type::Id(id) => self.has_resources(id),
+            Type::ErrorContext => todo!(),
+        }
+    }
+    fn has_resources(&self, id: &TypeId) -> bool {
+        match &self.gen.resolve.types[*id].kind {
+            TypeDefKind::Record(_) => todo!(),
+            TypeDefKind::Resource => true,
+            TypeDefKind::Handle(_) => true,
+            TypeDefKind::Flags(_) => false,
+            TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)),
+            TypeDefKind::Variant(_) => todo!(),
+            TypeDefKind::Enum(_) => false,
+            TypeDefKind::Option(_) => todo!(),
+            TypeDefKind::Result(_) => todo!(),
+            TypeDefKind::List(_) => todo!(),
+            TypeDefKind::Future(_) => todo!(),
+            TypeDefKind::Stream(_) => todo!(),
+            TypeDefKind::Type(ty) => match ty {
+                Type::Id(id) => self.has_resources(id),
+                _ => false,
+            },
+            TypeDefKind::Unknown => todo!(),
+        }
+    }
+}
+
+impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
+    type Operand = String;
+
+    fn emit(
+        &mut self,
+        _resolve: &Resolve,
+        inst: &wit_bindgen_core::abi::Instruction<'_>,
+        operands: &mut Vec<Self::Operand>,
+        results: &mut Vec<Self::Operand>,
+    ) {
+        let mut top_as = |cvt: &str| {
+            results.push(format!("({cvt}({}))", operands.pop().unwrap()));
+        };
+
+        match inst {
+            abi::Instruction::GetArg { nth } => {
+                if *nth == 0 && self.params[0].as_str() == "self" {
+                    if self.gen.in_guest_import ^ self.gen.gen.opts.host {
+                        results.push("(*this)".to_string());
+                    } else {
+                        results.push("(*lookup_resource(self))".to_string());
+                    }
+                } else {
+                    results.push(self.params[*nth].clone());
+                }
+            }
+            abi::Instruction::I32Const { val } => results.push(format!("(int32_t({}))", val)),
+            abi::Instruction::Bitcasts { casts } => {
+                for (cast, op) in casts.iter().zip(operands) {
+                    // let op = op;
+                    results.push(self.gen.gen.perform_cast(op, cast));
+                }
+            }
+            abi::Instruction::ConstZero { tys } => {
+                for ty in tys.iter() {
+                    match ty {
+                        WasmType::I32 => results.push("int32_t(0)".to_string()),
+                        WasmType::I64 => results.push("int64_t(0)".to_string()),
+                        WasmType::F32 => results.push("0.0f".to_string()),
+                        WasmType::F64 => results.push("0.0".to_string()),
+                        WasmType::Length => results.push("size_t(0)".to_string()),
+                        WasmType::Pointer => results.push("nullptr".to_string()),
+                        WasmType::PointerOrI64 => results.push("int64_t(0)".to_string()),
+                    }
+                }
+            }
+            abi::Instruction::I32Load { offset } => {
+                let tmp = self.tmp();
+                uwriteln!(
+                    self.src,
+                    "int32_t l{tmp} = *((int32_t const*)({} + {offset}));",
+                    operands[0],
+                    offset = offset.format(POINTER_SIZE_EXPRESSION)
+                );
+                results.push(format!("l{tmp}"));
+            }
+            abi::Instruction::I32Load8U { offset } => {
+                self.load_ext("uint8_t", *offset, operands, results)
+            }
+            abi::Instruction::I32Load8S { offset } => {
+                self.load_ext("int8_t", *offset, operands, results)
+            }
+            abi::Instruction::I32Load16U { offset } => {
+                self.load_ext("uint16_t", *offset, operands, results)
+            }
+            abi::Instruction::I32Load16S { offset } => {
+                self.load_ext("int16_t", *offset, operands, results)
+            }
+            abi::Instruction::I64Load { offset } => {
+                self.load("int64_t", *offset, operands, results)
+            }
+            abi::Instruction::F32Load { offset } => self.load("float", *offset, operands, results),
+            abi::Instruction::F64Load { offset } => self.load("double", *offset, operands, results),
+            abi::Instruction::I32Store { offset } => self.store("int32_t", *offset, operands),
+            abi::Instruction::I32Store8 { offset } => self.store("int8_t", *offset, operands),
+            abi::Instruction::I32Store16 { offset } => self.store("int16_t", *offset, operands),
+            abi::Instruction::I64Store { offset } => self.store("int64_t", *offset, operands),
+            abi::Instruction::F32Store { offset } => self.store("float", *offset, operands),
+            abi::Instruction::F64Store { offset } => self.store("double", *offset, operands),
+            abi::Instruction::I32FromChar
+            | abi::Instruction::I32FromBool
+            | abi::Instruction::I32FromU8
+            | abi::Instruction::I32FromS8
+            | abi::Instruction::I32FromU16
+            | abi::Instruction::I32FromS16
+            | abi::Instruction::I32FromU32
+            | abi::Instruction::I32FromS32 => top_as("int32_t"),
+            abi::Instruction::I64FromU64 | abi::Instruction::I64FromS64 => top_as("int64_t"),
+            abi::Instruction::F32FromCoreF32 => top_as("float"),
+            abi::Instruction::F64FromCoreF64 => top_as("double"),
+            abi::Instruction::S8FromI32 => top_as("int8_t"),
+            abi::Instruction::U8FromI32 => top_as("uint8_t"),
+            abi::Instruction::S16FromI32 => top_as("int16_t"),
+            abi::Instruction::U16FromI32 => top_as("uint16_t"),
+            abi::Instruction::S32FromI32 => top_as("int32_t"),
+            abi::Instruction::U32FromI32 => top_as("uint32_t"),
+            abi::Instruction::S64FromI64 => top_as("int64_t"),
+            abi::Instruction::U64FromI64 => top_as("uint64_t"),
+            abi::Instruction::CharFromI32 => top_as("uint32_t"),
+            abi::Instruction::CoreF32FromF32 => top_as("float"),
+            abi::Instruction::CoreF64FromF64 => top_as("double"),
+            abi::Instruction::BoolFromI32 => top_as("bool"),
+            abi::Instruction::ListCanonLower { realloc, .. } => {
+                let tmp = self.tmp();
+                let val = format!("vec{}", tmp);
+                let ptr = format!("ptr{}", tmp);
+                let len = format!("len{}", tmp);
+                // let result = format!("result{}", tmp);
+                self.push_str(&format!("auto const&{} = {};\n", val, operands[0]));
+                if self.gen.gen.opts.host_side() {
+                    self.push_str(&format!("auto {} = {}.data();\n", ptr, val));
+                    self.push_str(&format!("auto {} = {}.size();\n", len, val));
+                } else {
+                    self.push_str(&format!(
+                        "auto {} = ({})({}.data());\n",
+                        ptr,
+                        self.gen.gen.opts.ptr_type(),
+                        val
+                    ));
+                    self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val));
+                }
+                if realloc.is_none() {
+                    results.push(ptr);
+                } else {
+                    if !self.gen.gen.opts.host_side()
+                        && !(self.gen.gen.opts.symmetric
+                            && matches!(self.variant, AbiVariant::GuestImport))
+                    {
+                        uwriteln!(self.src, "{}.leak();\n", operands[0]);
+                    }
+                    results.push(ptr);
+                }
+                results.push(len);
+            }
+            abi::Instruction::StringLower { realloc } => {
+                let tmp = self.tmp();
+                let val = format!("vec{}", tmp);
+                let ptr = format!("ptr{}", tmp);
+                let len = format!("len{}", tmp);
+                // let result = format!("result{}", tmp);
+                self.push_str(&format!("auto const&{} = {};\n", val, operands[0]));
+                if self.gen.gen.opts.host_side() {
+                    self.push_str(&format!("auto {} = {}.data();\n", ptr, val));
+                    self.push_str(&format!("auto {} = {}.size();\n", len, val));
+                } else {
+                    self.push_str(&format!(
+                        "auto {} = ({})({}.data());\n",
+                        ptr,
+                        self.gen.gen.opts.ptr_type(),
+                        val
+                    ));
+                    self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val));
+                }
+                if realloc.is_none() {
+                    results.push(ptr);
+                } else {
+                    if !self.gen.gen.opts.host_side()
+                        && !(self.gen.gen.opts.symmetric
+                            && matches!(self.variant, AbiVariant::GuestImport))
+                    {
+                        uwriteln!(self.src, "{}.leak();\n", operands[0]);
+                    }
+                    results.push(ptr);
+                }
+                results.push(len);
+            }
+            abi::Instruction::ListLower {
+                element: _,
+                realloc,
+            } => {
+                let tmp = self.tmp();
+                let val = format!("vec{}", tmp);
+                let ptr = format!("ptr{}", tmp);
+                let len = format!("len{}", tmp);
+                self.push_str(&format!("auto const&{} = {};\n", val, operands[0]));
+                if self.gen.gen.opts.host_side() {
+                    self.push_str(&format!("auto {} = {}.data();\n", ptr, val));
+                    self.push_str(&format!("auto {} = {}.size();\n", len, val));
+                } else {
+                    self.push_str(&format!(
+                        "auto {} = ({})({}.data());\n",
+                        ptr,
+                        self.gen.gen.opts.ptr_type(),
+                        val
+                    ));
+                    self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val));
+                }
+                if realloc.is_none() {
+                    results.push(ptr);
+                } else {
+                    if !self.gen.gen.opts.host_side()
+                        && !(self.gen.gen.opts.symmetric
+                            && matches!(self.variant, AbiVariant::GuestImport))
+                    {
+                        uwriteln!(self.src, "{}.leak();\n", operands[0]);
+                    }
+                    results.push(ptr);
+                }
+                results.push(len);
+            }
+            abi::Instruction::ListCanonLift { element, .. } => {
+                let tmp = self.tmp();
+                let len = format!("len{}", tmp);
+                let inner = self
+                    .gen
+                    .type_name(element, &self.namespace, Flavor::InStruct);
+                self.push_str(&format!("auto {} = {};\n", len, operands[1]));
+                let result = if self.gen.gen.opts.host {
+                    uwriteln!(self.src, "{inner} const* ptr{tmp} = ({inner} const*)wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {});\n", operands[0]);
+                    format!("wit::span<{inner} const>(ptr{}, (size_t){len})", tmp)
+                } else if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestExport)
+                {
+                    if self.gen.gen.opts.symmetric {
+                        format!(
+                            "wit::span<{inner} const>(({inner}*)({}), {len})",
+                            operands[0]
+                        )
+                    } else {
+                        format!(
+                            "wit::vector<{inner} const>(({inner}*)({}), {len}).get_view()",
+                            operands[0]
+                        )
+                    }
+                } else {
+                    format!("wit::vector<{inner}>(({inner}*)({}), {len})", operands[0])
+                };
+                results.push(result);
+            }
+            abi::Instruction::StringLift => {
+                let tmp = self.tmp();
+                let len = format!("len{}", tmp);
+                uwriteln!(self.src, "auto {} = {};\n", len, operands[1]);
+                let result = if self.gen.gen.opts.symmetric
+                    && !self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestExport)
+                {
+                    uwriteln!(self.src, "auto string{tmp} = wit::string::from_view(std::string_view((char const *)({}), {len}));\n", operands[0]);
+                    format!("std::move(string{tmp})")
+                } else if self.gen.gen.opts.host {
+                    uwriteln!(self.src, "char const* ptr{} = (char const*)wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {});\n", tmp, operands[0]);
+                    format!("std::string_view(ptr{}, {len})", tmp)
+                } else if self.gen.gen.opts.short_cut
+                    || (self.gen.gen.opts.new_api
+                        && matches!(self.variant, AbiVariant::GuestExport))
+                {
+                    if self.gen.gen.opts.new_api
+                        && matches!(self.variant, AbiVariant::GuestExport)
+                        && !self.gen.gen.opts.symmetric
+                    {
+                        assert!(self.needs_dealloc);
+                        uwriteln!(
+                            self.src,
+                            "if ({len}>0) _deallocate.push_back({});\n",
+                            operands[0]
+                        );
+                    }
+                    format!("std::string_view((char const*)({}), {len})", operands[0])
+                } else {
+                    format!("wit::string((char const*)({}), {len})", operands[0])
+                };
+                results.push(result);
+            }
+            abi::Instruction::ListLift { element, .. } => {
+                let body = self.blocks.pop().unwrap();
+                let tmp = self.tmp();
+                let size = self.gen.sizes.size(element);
+                let _align = self.gen.sizes.align(element);
+                let flavor = if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestExport)
+                {
+                    Flavor::BorrowedArgument
+                } else {
+                    Flavor::InStruct
+                };
+                let vtype = self.gen.type_name(element, &self.namespace, flavor);
+                let len = format!("len{tmp}");
+                let base = format!("base{tmp}");
+                let result = format!("result{tmp}");
+                self.push_str(&format!(
+                    "auto {base} = {operand0};\n",
+                    operand0 = operands[0]
+                ));
+                self.push_str(&format!(
+                    "auto {len} = {operand1};\n",
+                    operand1 = operands[1]
+                ));
+                self.push_str(&format!(
+                    r#"auto {result} = wit::vector<{vtype}>::allocate({len});
+                    "#,
+                ));
+                if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestExport)
+                    && !self.gen.gen.opts.symmetric
+                {
+                    assert!(self.needs_dealloc);
+                    self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n"));
+                }
+
+                uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{");
+                uwriteln!(
+                    self.src,
+                    "auto base = {base} + i * {size};",
+                    size = size.format(POINTER_SIZE_EXPRESSION)
+                );
+                uwrite!(self.src, "{}", body.0);
+                uwriteln!(self.src, "auto e{tmp} = {};", body.1[0]);
+                if let Some(code) = self.leak_on_insertion.take() {
+                    assert!(self.needs_dealloc);
+                    uwriteln!(self.src, "{code}");
+                }
+                // inplace construct
+                uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));");
+                uwriteln!(self.src, "}}");
+                if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestImport)
+                    && self.gen.gen.opts.symmetric
+                {
+                    // we converted the result, free the returned vector
+                    uwriteln!(self.src, "free({base});");
+                }
+                if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) {
+                    results.push(format!("{result}.get_const_view()"));
+                    if !self.gen.gen.opts.symmetric
+                        || (self.gen.gen.opts.new_api
+                            && matches!(self.variant, AbiVariant::GuestExport))
+                    {
+                        self.leak_on_insertion.replace(format!(
+                            "if ({len}>0) _deallocate.push_back((void*){result}.leak());\n"
+                        ));
+                    }
+                } else {
+                    results.push(format!("std::move({result})"));
+                }
+            }
+            abi::Instruction::IterElem { .. } => results.push("IterElem".to_string()),
+            abi::Instruction::IterBasePointer => results.push("base".to_string()),
+            abi::Instruction::RecordLower { record, .. } => {
+                let op = &operands[0];
+                for f in record.fields.iter() {
+                    results.push(format!("({}).{}", op, to_c_ident(&f.name)));
+                }
+            }
+            abi::Instruction::RecordLift { record, ty, .. } => {
+                //                let t = self.gen.resolve().types[*ty];
+                let mut result =
+                    self.gen
+                        .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct);
+                // self.typename_lift(*ty);
+                result.push_str("{");
+                for (_field, val) in record.fields.iter().zip(operands) {
+                    result.push_str("std::move(");
+                    result.push_str(&val);
+                    result.push_str("), ");
+                }
+                result.push_str("}");
+                results.push(result);
+            }
+            abi::Instruction::HandleLower {
+                handle: Handle::Own(_ty),
+                ..
+            } => {
+                let op = &operands[0];
+                if self.gen.gen.opts.host_side() {
+                    if matches!(self.variant, AbiVariant::GuestImport) {
+                        results.push(format!("{op}.release()->get_handle()"));
+                    } else {
+                        let tmp = self.tmp();
+                        let var = self.tempname("rep", tmp);
+                        uwriteln!(self.src, "auto {var} = {op}.take_rep();");
+                        results.push(format!("{op}.get_handle()"));
+                    }
+                } else {
+                    if matches!(self.variant, AbiVariant::GuestImport) {
+                        results.push(format!("{op}.into_handle()"));
+                    } else {
+                        results.push(format!("{op}.release()->handle"));
+                    }
+                }
+            }
+            abi::Instruction::HandleLower {
+                handle: Handle::Borrow(_),
+                ..
+            } => {
+                let op = &operands[0];
+                if self.gen.gen.opts.host_side() {
+                    if op == "(*this)" {
+                        results.push(format!("{op}.get_rep()"));
+                    } else {
+                        results.push(format!("{op}.get().get_rep()"));
+                    }
+                } else if op == "(*this)" {
+                    // TODO is there a better way to decide?
+                    results.push(format!("{op}.get_handle()"));
+                } else {
+                    results.push(format!("{op}.get().get_handle()"));
+                }
+            }
+            abi::Instruction::HandleLift { handle, .. } => {
+                let op = &operands[0];
+                match (handle, self.gen.gen.opts.host_side()) {
+                    (Handle::Own(ty), true) => match self.variant {
+                        AbiVariant::GuestExport => {
+                            results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}"))
+                        }
+                        AbiVariant::GuestImport => {
+                            let tmp = self.tmp();
+                            let var = self.tempname("obj", tmp);
+                            let tname = self.gen.type_name(
+                                &Type::Id(*ty),
+                                &self.namespace,
+                                Flavor::Argument(self.variant),
+                            );
+                            uwriteln!(
+                                self.src,
+                                "auto {var} = {tname}::remove_resource({op});
+                                assert({var}.has_value());"
+                            );
+                            results.push(format!("{tname}::Owned(*{var})"));
+                        }
+                        AbiVariant::GuestImportAsync => todo!(),
+                        AbiVariant::GuestExportAsync => todo!(),
+                        AbiVariant::GuestExportAsyncStackful => todo!(),
+                    },
+                    (Handle::Own(ty), false) => match self.variant {
+                        AbiVariant::GuestImport => {
+                            results.push(format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}{{{op}}}"))
+                        }
+                        AbiVariant::GuestExport => {
+                            let tmp = self.tmp();
+                            let var = self.tempname("obj", tmp);
+                            let tname = self.gen.type_name(
+                                &Type::Id(*ty),
+                                &self.namespace,
+                                Flavor::Argument(self.variant),
+                            );
+                            uwriteln!(
+                                self.src,
+                                "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));"
+                            );
+                            if !self.gen.gen.opts.symmetric {
+                                uwriteln!(self.src, "{var}->into_handle();");
+                            }
+                            results.push(format!("std::move({var})"))
+                        }
+                        AbiVariant::GuestImportAsync => todo!(),
+                        AbiVariant::GuestExportAsync => todo!(),
+                        AbiVariant::GuestExportAsyncStackful => todo!(),
+                    },
+                    (Handle::Borrow(ty), true) => {
+                        let tname = self.gen.type_name(
+                            &Type::Id(*ty),
+                            &self.namespace,
+                            Flavor::Argument(self.variant),
+                        );
+                        results.push(format!("**{tname}::lookup_resource({op})"));
+                    }
+                    (Handle::Borrow(ty), false) => match self.variant {
+                        AbiVariant::GuestImport => results.push(op.clone()),
+                        AbiVariant::GuestExport => {
+                            let tname = self.gen.type_name(
+                                &Type::Id(*ty),
+                                &self.namespace,
+                                Flavor::Argument(self.variant),
+                            );
+                            results.push(format!("std::ref(*({tname} *){op})"));
+                        }
+                        AbiVariant::GuestImportAsync => todo!(),
+                        AbiVariant::GuestExportAsync => todo!(),
+                        AbiVariant::GuestExportAsyncStackful => todo!(),
+                    },
+                }
+            }
+            abi::Instruction::TupleLower { tuple, .. } => {
+                let op = &operands[0];
+                for n in 0..tuple.types.len() {
+                    results.push(format!("std::get<{n}>({op})"));
+                }
+            }
+            abi::Instruction::TupleLift { tuple, .. } => {
+                let name = format!("tuple{}", self.tmp());
+                uwrite!(self.src, "auto {name} = std::tuple<");
+                self.src.push_str(
+                    &(tuple
+                        .types
+                        .iter()
+                        .map(|t| self.gen.type_name(t, &self.namespace, Flavor::InStruct)))
+                    .collect::<Vec<_>>()
+                    .join(", "),
+                );
+                self.src.push_str(">(");
+                self.src.push_str(&operands.join(", "));
+                self.src.push_str(");\n");
+                results.push(format!("std::move({name})"));
+            }
+            abi::Instruction::FlagsLower { flags, ty, .. } => {
+                match wit_bindgen_c::flags_repr(flags) {
+                    Int::U8 | Int::U16 | Int::U32 => {
+                        results.push(format!("((int32_t){})", operands.pop().unwrap()));
+                    }
+                    Int::U64 => {
+                        let name =
+                            self.gen
+                                .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct);
+                        let tmp = self.tmp();
+                        let tempname = self.tempname("flags", tmp);
+                        uwriteln!(self.src, "{name} {tempname} = {};", operands[0]);
+                        results.push(format!("(int32_t)(((uint64_t){tempname}) & 0xffffffff)"));
+                        results.push(format!(
+                            "(int32_t)((((uint64_t){tempname}) >> 32) & 0xffffffff)"
+                        ));
+                    }
+                }
+            }
+            abi::Instruction::FlagsLift { flags, ty, .. } => {
+                let typename =
+                    self.gen
+                        .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct);
+                match wit_bindgen_c::flags_repr(flags) {
+                    Int::U8 | Int::U16 | Int::U32 => {
+                        results.push(format!("(({typename}){})", operands.pop().unwrap()));
+                    }
+                    Int::U64 => {
+                        let op0 = &operands[0];
+                        let op1 = &operands[1];
+                        results.push(format!(
+                            "(({typename})(({op0}) | (((uint64_t)({op1})) << 32)))"
+                        ));
+                    }
+                }
+            }
+            abi::Instruction::VariantPayloadName => {
+                let name = format!("payload{}", self.tmp());
+                results.push(name.clone());
+                self.payloads.push(name);
+            }
+            abi::Instruction::VariantLower {
+                variant,
+                results: result_types,
+                ..
+            } => {
+                //let name = self.gen.type_name(*ty);
+                // let op0 = &operands[0];
+                // self.push_str(&format!("({name}){op0}"));
+                let blocks = self
+                    .blocks
+                    .drain(self.blocks.len() - variant.cases.len()..)
+                    .collect::<Vec<_>>();
+                let payloads = self
+                    .payloads
+                    .drain(self.payloads.len() - variant.cases.len()..)
+                    .collect::<Vec<_>>();
+
+                let mut variant_results = Vec::with_capacity(result_types.len());
+                for ty in result_types.iter() {
+                    let name = format!("variant{}", self.tmp());
+                    results.push(name.clone());
+                    self.src.push_str(self.gen.gen.opts.wasm_type(*ty));
+                    self.src.push_str(" ");
+                    self.src.push_str(&name);
+                    self.src.push_str(";\n");
+                    variant_results.push(name);
+                }
+
+                let expr_to_match = format!("({}).tag", operands[0]);
+
+                uwriteln!(self.src, "switch ((int32_t) {}) {{", expr_to_match);
+                for (i, ((case, (block, block_results)), payload)) in
+                    variant.cases.iter().zip(blocks).zip(payloads).enumerate()
+                {
+                    uwriteln!(self.src, "case {}: {{", i);
+                    if let Some(ty) = case.ty.as_ref() {
+                        let ty = self.gen.type_name(ty, &self.namespace, Flavor::InStruct);
+                        uwrite!(
+                            self.src,
+                            "const {} *{} = &({}).val",
+                            ty,
+                            payload,
+                            operands[0],
+                        );
+                        self.src.push_str(".");
+                        self.src.push_str(&to_c_ident(&case.name));
+                        self.src.push_str(";\n");
+                    }
+                    self.src.push_str(&block);
+
+                    for (name, result) in variant_results.iter().zip(&block_results) {
+                        uwriteln!(self.src, "{} = {};", name, result);
+                    }
+                    self.src.push_str("break;\n}\n");
+                }
+                self.src.push_str("}\n");
+            }
+            abi::Instruction::VariantLift { variant, ty, .. } => {
+                let mut result = String::new();
+                result.push_str("{");
+
+                let named_enum = variant.cases.iter().all(|c| c.ty.is_none());
+                // let blocks = self
+                //     .blocks
+                //     .drain(self.blocks.len() - variant.cases.len()..)
+                //     .collect::<Vec<_>>();
+                let op0 = &operands[0];
+
+                if named_enum {
+                    // In unchecked mode when this type is a named enum then we know we
+                    // defined the type so we can transmute directly into it.
+                    // result.push_str("#[cfg(not(debug_assertions))]");
+                    // result.push_str("{");
+                    // result.push_str("::core::mem::transmute::<_, ");
+                    // result.push_str(&name.to_upper_camel_case());
+                    // result.push_str(">(");
+                    // result.push_str(op0);
+                    // result.push_str(" as ");
+                    // result.push_str(int_repr(variant.tag()));
+                    // result.push_str(")");
+                    // result.push_str("}");
+                }
+
+                // if named_enum {
+                //     result.push_str("#[cfg(debug_assertions)]");
+                // }
+                let blocks: Vec<String> = Vec::new();
+                result.push_str("{");
+                result.push_str(&format!("match {op0} {{\n"));
+                let name = self.typename_lift(*ty);
+                for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() {
+                    let pat = i.to_string();
+                    let block = if case.ty.is_some() {
+                        format!("({block})")
+                    } else {
+                        String::new()
+                    };
+                    let case = case.name.to_upper_camel_case();
+                    // if i == variant.cases.len() - 1 {
+                    //     result.push_str("#[cfg(debug_assertions)]");
+                    //     result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
+                    //     result.push_str("#[cfg(not(debug_assertions))]");
+                    //     result.push_str(&format!("_ => {name}::{case}{block},\n"));
+                    // } else {
+                    result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
+                    // }
+                }
+                // result.push_str("#[cfg(debug_assertions)]");
+                // result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
+                result.push_str("}");
+                result.push_str("}");
+
+                result.push_str("}");
+                results.push(result);
+            }
+            abi::Instruction::EnumLower { .. } => results.push(format!("int32_t({})", operands[0])),
+            abi::Instruction::EnumLift { ty, .. } => {
+                let typename =
+                    self.gen
+                        .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct);
+                results.push(format!("({typename}){}", &operands[0]));
+            }
+            abi::Instruction::OptionLower {
+                payload,
+                results: result_types,
+                ..
+            } => {
+                let (mut some, some_results) = self.blocks.pop().unwrap();
+                let (mut none, none_results) = self.blocks.pop().unwrap();
+                let some_payload = self.payloads.pop().unwrap();
+                let _none_payload = self.payloads.pop().unwrap();
+
+                for (i, ty) in result_types.iter().enumerate() {
+                    let tmp = self.tmp();
+                    let name = self.tempname("option", tmp);
+                    results.push(name.clone());
+                    self.src.push_str(self.gen.gen.opts.wasm_type(*ty));
+                    self.src.push_str(" ");
+                    self.src.push_str(&name);
+                    self.src.push_str(";\n");
+                    let some_result = &some_results[i];
+                    uwriteln!(some, "{name} = {some_result};");
+                    let none_result = &none_results[i];
+                    uwriteln!(none, "{name} = {none_result};");
+                }
+
+                let op0 = &operands[0];
+                let flavor = if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestImport)
+                {
+                    Flavor::BorrowedArgument
+                } else {
+                    Flavor::InStruct
+                };
+                let ty = self.gen.type_name(payload, &self.namespace, flavor);
+                let bind_some = format!("{ty} {some_payload} = (std::move({op0})).value();");
+
+                uwrite!(
+                    self.src,
+                    "\
+                    if (({op0}).has_value()) {{
+                        {bind_some}
+                        {some}}} else {{
+                        {none}}}
+                    "
+                );
+            }
+            abi::Instruction::OptionLift { payload, .. } => {
+                let (some, some_results) = self.blocks.pop().unwrap();
+                let (_none, none_results) = self.blocks.pop().unwrap();
+                assert!(none_results.len() == 0);
+                assert!(some_results.len() == 1);
+                // let some_result = &some_results[0];
+                let flavor = if self.gen.gen.opts.new_api
+                    && matches!(self.variant, AbiVariant::GuestExport)
+                {
+                    Flavor::BorrowedArgument
+                } else {
+                    Flavor::InStruct
+                };
+                let type_name = self.gen.type_name(*payload, &self.namespace, flavor);
+                let full_type = format!("std::optional<{type_name}>");
+                let op0 = &operands[0];
+
+                let tmp = self.tmp();
+                let resultname = self.tempname("option", tmp);
+                uwriteln!(
+                    self.src,
+                    "{full_type} {resultname};
+                    if ({op0}) {{
+                        {some}
+                        {resultname}.emplace({});
+                    }}",
+                    some_results[0]
+                );
+                results.push(format!("std::move({resultname})"));
+            }
+            abi::Instruction::ResultLower {
+                results: result_types,
+                result,
+                ..
+            } => {
+                let (mut err, err_results) = self.blocks.pop().unwrap();
+                let (mut ok, ok_results) = self.blocks.pop().unwrap();
+                let err_payload = self.payloads.pop().unwrap();
+                let ok_payload = self.payloads.pop().unwrap();
+
+                for (i, ty) in result_types.iter().enumerate() {
+                    let tmp = self.tmp();
+                    let name = self.tempname("result", tmp);
+                    results.push(name.clone());
+                    self.src.push_str(self.gen.gen.opts.wasm_type(*ty));
+                    self.src.push_str(" ");
+                    self.src.push_str(&name);
+                    self.src.push_str(";\n");
+                    let ok_result = &ok_results[i];
+                    uwriteln!(ok, "{name} = {ok_result};");
+                    let err_result = &err_results[i];
+                    uwriteln!(err, "{name} = {err_result};");
+                }
+
+                let op0 = &operands[0];
+                let ok_ty = self.gen.optional_type_name(
+                    result.ok.as_ref(),
+                    &self.namespace,
+                    Flavor::InStruct,
+                );
+                let err_ty = self.gen.optional_type_name(
+                    result.err.as_ref(),
+                    &self.namespace,
+                    Flavor::InStruct,
+                );
+                let bind_ok = if let Some(_ok) = result.ok.as_ref() {
+                    format!("{ok_ty} {ok_payload} = std::move({op0}).value();")
+                } else {
+                    String::new()
+                };
+                let bind_err = if let Some(_err) = result.err.as_ref() {
+                    format!("{err_ty} {err_payload} = std::move({op0}).error();")
+                } else {
+                    String::new()
+                };
+
+                uwrite!(
+                    self.src,
+                    "\
+                    if (({op0}).has_value()) {{
+                        {bind_ok}
+                        {ok}}} else {{
+                        {bind_err}
+                        {err}}}
+                    "
+                );
+            }
+            abi::Instruction::ResultLift { result, .. } => {
+                let (mut err, err_results) = self.blocks.pop().unwrap();
+                let (mut ok, ok_results) = self.blocks.pop().unwrap();
+                let mut ok_result = String::new();
+                let mut err_result = String::new();
+                if result.ok.is_none() {
+                    ok.clear();
+                } else {
+                    ok_result = format!("std::move({})", ok_results[0]);
+                }
+                if result.err.is_none() {
+                    err.clear();
+                } else {
+                    err_result = format!("std::move({})", err_results[0]);
+                }
+                let ok_type = self.gen.optional_type_name(
+                    result.ok.as_ref(),
+                    &self.namespace,
+                    Flavor::InStruct,
+                );
+                let err_type = self.gen.optional_type_name(
+                    result.err.as_ref(),
+                    &self.namespace,
+                    Flavor::InStruct,
+                );
+                let full_type = format!("std::expected<{ok_type}, {err_type}>",);
+                let err_type = "std::unexpected";
+                let operand = &operands[0];
+
+                let tmp = self.tmp();
+                let resultname = self.tempname("result", tmp);
+                uwriteln!(
+                    self.src,
+                    "{full_type} {resultname};
+                    if ({operand}==0) {{
+                        {ok}
+                        {resultname}.emplace({ok_result});
+                    }} else {{
+                        {err}
+                        {resultname}={err_type}{{{err_result}}};
+                    }}"
+                );
+                results.push(resultname);
+            }
+            abi::Instruction::CallWasm {
+                name,
+                sig,
+                module_prefix,
+            } => {
+                let module_name = self
+                    .gen
+                    .wasm_import_module
+                    .as_ref()
+                    .map(|e| {
+                        self.gen
+                            .gen
+                            .import_prefix
+                            .as_ref()
+                            .cloned()
+                            .unwrap_or_default()
+                            + *module_prefix
+                            + e
+                    })
+                    .unwrap();
+                if self.gen.gen.opts.host {
+                    uwriteln!(self.src, "wasm_function_inst_t wasm_func = wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \n\
+                            \"{}#{}\", \"{}\");", module_name, name, self.wamr_signature.as_ref().unwrap().to_string());
+                    if !sig.results.is_empty() {
+                        uwriteln!(
+                            self.src,
+                            "wasm_val_t wasm_results[{}] = {{ WASM_INIT_VAL }};",
+                            sig.results.len()
+                        );
+                    } else {
+                        uwriteln!(self.src, "wasm_val_t *wasm_results = nullptr;");
+                    }
+                    if !sig.params.is_empty() {
+                        uwrite!(self.src, "wasm_val_t wasm_args[{}] = {{", sig.params.len());
+                        for (typ, value) in sig.params.iter().zip(operands.iter()) {
+                            match typ {
+                                WasmType::I32 => uwrite!(self.src, "WASM_I32_VAL({}),", value),
+                                WasmType::I64 => uwrite!(self.src, "WASM_I64_VAL({}),", value),
+                                WasmType::F32 => uwrite!(self.src, "WASM_F32_VAL({}),", value),
+                                WasmType::F64 => uwrite!(self.src, "WASM_F64_VAL({}),", value),
+                                WasmType::Length => {
+                                    if self.gen.gen.opts.wasm64 {
+                                        uwrite!(self.src, "WASM_I64_VAL({}),", value)
+                                    } else {
+                                        uwrite!(self.src, "WASM_I32_VAL((int32_t){}),", value)
+                                    }
+                                }
+                                WasmType::Pointer => {
+                                    if self.gen.gen.opts.wasm64 {
+                                        uwrite!(self.src, "WASM_I64_VAL({}),", value)
+                                    } else {
+                                        uwrite!(self.src, "WASM_I32_VAL((int32_t){}),", value)
+                                    }
+                                }
+                                WasmType::PointerOrI64 => {
+                                    uwrite!(self.src, "WASM_I64_VAL({}),", value)
+                                }
+                            }
+                        }
+                        self.src.push_str("};\n");
+                    } else {
+                        uwriteln!(self.src, "wasm_val_t *wasm_args = nullptr;");
+                    }
+                    uwriteln!(self.src, "bool wasm_ok = wasm_runtime_call_wasm_a(exec_env, wasm_func, {}, wasm_results, {}, wasm_args);", sig.results.len(), sig.params.len());
+                    uwriteln!(self.src, "assert(wasm_ok);");
+                    if sig.results.len() > 0 {
+                        let (kind, elem) = match sig.results.first() {
+                            Some(WasmType::I32) => (String::from("WASM_I32"), String::from("i32")),
+                            Some(WasmType::I64) => (String::from("WASM_I64"), String::from("i64")),
+                            Some(WasmType::F32) => (String::from("WASM_F32"), String::from("f32")),
+                            Some(WasmType::F64) => (String::from("WASM_F64"), String::from("f64")),
+                            Some(WasmType::Pointer) => {
+                                if self.gen.gen.opts.wasm64 {
+                                    (String::from("WASM_I64"), String::from("i64"))
+                                } else {
+                                    (String::from("WASM_I32"), String::from("i32"))
+                                }
+                            }
+                            Some(WasmType::Length) => {
+                                if self.gen.gen.opts.wasm64 {
+                                    (String::from("WASM_I64"), String::from("i64"))
+                                } else {
+                                    (String::from("WASM_I32"), String::from("i32"))
+                                }
+                            }
+                            Some(WasmType::PointerOrI64) => {
+                                (String::from("WASM_I64"), String::from("i64"))
+                            }
+                            None => todo!(),
+                        };
+                        uwriteln!(self.src, "assert(wasm_results[0].kind=={kind});");
+                        uwriteln!(self.src, "auto ret = wasm_results[0].of.{elem};");
+                        results.push("ret".to_string());
+                    }
+                } else {
+                    let func =
+                        self.gen
+                            .declare_import(&module_name, name, &sig.params, &sig.results);
+
+                    // ... then call the function with all our operands
+                    if sig.results.len() > 0 {
+                        self.src.push_str("auto ret = ");
+                        results.push("ret".to_string());
+                    }
+                    self.src.push_str(&func);
+                    self.src.push_str("(");
+                    self.src.push_str(&operands.join(", "));
+                    self.src.push_str(");\n");
+                }
+            }
+            abi::Instruction::CallInterface { func, .. } => {
+                // dbg!(func);
+                self.let_results(if func.result.is_some() { 1 } else { 0 }, results);
+                let (mut namespace, func_name_h) =
+                    self.gen
+                        .func_namespace_name(func, !self.gen.gen.opts.host_side(), true);
+                if matches!(func.kind, FunctionKind::Method(_)) {
+                    let this = operands.remove(0);
+                    if self.gen.gen.opts.host_side() {
+                        uwrite!(self.src, "({this}).");
+                    } else {
+                        //let objtype = namespace.join("::");
+                        uwrite!(self.src, "({this}).get().");
+                        // uwrite!(self.src, "(({objtype}*){this})->",);
+                    }
+                } else {
+                    if matches!(func.kind, FunctionKind::Constructor(_))
+                        && self.gen.gen.opts.host_side()
+                    {
+                        let _ = namespace.pop();
+                    }
+                    let mut relative = SourceWithState::default();
+                    // relative.namespace = self.namespace.clone();
+                    relative.qualify(&namespace);
+                    self.push_str(&relative.src);
+                    // self.gen.gen.c_src.qualify(&namespace);
+                }
+                self.src.push_str(&func_name_h);
+                if matches!(func.kind, FunctionKind::Constructor(_))
+                    && self.gen.gen.opts.host_side()
+                {
+                    self.push_str("::New");
+                }
+                self.push_str("(");
+                if self.gen.gen.opts.host {
+                    if !matches!(func.kind, FunctionKind::Method(_)) {
+                        self.push_str("exec_env");
+                        if !operands.is_empty() {
+                            self.push_str(", ");
+                        }
+                    }
+                }
+                self.push_str(&operands.join(", "));
+                if false
+                    && matches!(func.kind, FunctionKind::Constructor(_))
+                    && !self.gen.gen.opts.is_only_handle(self.variant)
+                {
+                    // acquire object from unique_ptr
+                    self.push_str(").release();");
+                    results[0] = format!("(*({}))", results[0]);
+                } else {
+                    self.push_str(");\n");
+                }
+                if self.needs_dealloc {
+                    uwriteln!(
+                        self.src,
+                        "for (auto i: _deallocate) {{ free(i); }}\n
+                        _deallocate.clear();"
+                    );
+                }
+            }
+            abi::Instruction::Return { amt, func } => {
+                // let guest_import = matches!(self.variant, AbiVariant::GuestImport);
+                match amt {
+                    0 => {}
+                    _ => {
+                        assert!(*amt == operands.len());
+                        match &func.kind {
+                            FunctionKind::Constructor(_)
+                                if self.gen.gen.opts.is_only_handle(self.variant) =>
+                            {
+                                // strange but works
+                                if matches!(self.variant, AbiVariant::GuestExport) {
+                                    self.src.push_str("this->index = ");
+                                } else {
+                                    self.src.push_str("this->handle = ");
+                                }
+                            }
+                            _ => self.src.push_str("return "),
+                        }
+                        if let Some(CabiPostInformation {
+                            module: _,
+                            name: _cabi_post_name,
+                            ret_type: cabi_post_type,
+                        }) = self.cabi_post.as_ref()
+                        {
+                            self.src.push_str("wit::guest_owned<");
+                            self.src.push_str(&cabi_post_type);
+                            self.src.push_str(">(");
+                        }
+                        if *amt == 1 {
+                            if operands[0].starts_with("std::move(") {
+                                // remove the std::move due to return value optimization (and complex rules about when std::move harms)
+                                self.src.push_str(&operands[0][9..]);
+                            } else {
+                                self.src.push_str(&operands[0]);
+                            }
+                        } else {
+                            todo!();
+                            // self.src.push_str("std::tuple<");
+                            // if let Results::Named(params) = &func.results {
+                            //     for (num, (_name, ty)) in params.iter().enumerate() {
+                            //         if num > 0 {
+                            //             self.src.push_str(", ");
+                            //         }
+                            //         let tname =
+                            //             self.gen.type_name(ty, &self.namespace, Flavor::InStruct);
+                            //         self.src.push_str(&tname);
+                            //     }
+                            // }
+                            // self.src.push_str(">(");
+                            // self.src.push_str(&operands.join(", "));
+                            // self.src.push_str(")");
+                        }
+                        if let Some(CabiPostInformation {
+                            module: func_module,
+                            name: func_name,
+                            ret_type: _cabi_post_type,
+                        }) = self.cabi_post.as_ref()
+                        {
+                            if self.gen.gen.opts.host {
+                                let cabi_post_name = make_external_symbol(
+                                    &func_module,
+                                    &func_name,
+                                    AbiVariant::GuestExport,
+                                );
+                                self.src.push_str(&format!(", wasm_results[0].of.i32, wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \"cabi_post_{}\", \"(i)\"), exec_env)", cabi_post_name));
+                            } else {
+                                let cabi_post_name = self.gen.declare_import(
+                                    &format!("cabi_post_{func_module}"),
+                                    func_name,
+                                    &[WasmType::Pointer],
+                                    &[],
+                                );
+                                self.src.push_str(&format!(", ret, {})", cabi_post_name));
+                            }
+                        }
+                        if matches!(func.kind, FunctionKind::Constructor(_))
+                            && self.gen.gen.opts.is_only_handle(self.variant)
+                        {
+                            // we wrapped the handle in an object, so unpack it
+                            if self.gen.gen.opts.host_side() {
+                                self.src.push_str(
+                                    ".get_handle();
+                                    this->rep = *lookup_resource(ret)",
+                                );
+                            } else {
+                                self.src.push_str(".into_handle()");
+                            }
+                        }
+                        self.src.push_str(";\n");
+                    }
+                }
+            }
+            abi::Instruction::Malloc { .. } => todo!(),
+            abi::Instruction::GuestDeallocate { .. } => {
+                uwriteln!(self.src, "free((void*) ({}));", operands[0]);
+            }
+            abi::Instruction::GuestDeallocateString => {
+                uwriteln!(self.src, "if (({}) > 0) {{", operands[1]);
+                uwriteln!(
+                    self.src,
+                    "wit::string::drop_raw((void*) ({}));",
+                    operands[0]
+                );
+                uwriteln!(self.src, "}}");
+            }
+            abi::Instruction::GuestDeallocateList { element } => {
+                let (body, results) = self.blocks.pop().unwrap();
+                assert!(results.is_empty());
+                let tmp = self.tmp();
+                let ptr = self.tempname("ptr", tmp);
+                let len = self.tempname("len", tmp);
+                uwriteln!(self.src, "uint8_t* {ptr} = {};", operands[0]);
+                uwriteln!(self.src, "size_t {len} = {};", operands[1]);
+                let i = self.tempname("i", tmp);
+                uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{");
+                let size = self.gen.sizes.size(element);
+                uwriteln!(
+                    self.src,
+                    "uint8_t* base = {ptr} + {i} * {size};",
+                    size = size.format(POINTER_SIZE_EXPRESSION)
+                );
+                uwriteln!(self.src, "(void) base;");
+                uwrite!(self.src, "{body}");
+                uwriteln!(self.src, "}}");
+                uwriteln!(self.src, "if ({len} > 0) {{");
+                uwriteln!(self.src, "free((void*) ({ptr}));");
+                uwriteln!(self.src, "}}");
+            }
+            abi::Instruction::GuestDeallocateVariant { blocks } => {
+                let blocks = self
+                    .blocks
+                    .drain(self.blocks.len() - blocks..)
+                    .collect::<Vec<_>>();
+
+                uwriteln!(self.src, "switch ((int32_t) {}) {{", operands[0]);
+                for (i, (block, results)) in blocks.into_iter().enumerate() {
+                    assert!(results.is_empty());
+                    uwriteln!(self.src, "case {}: {{", i);
+                    self.src.push_str(&block);
+                    self.src.push_str("break;\n}\n");
+                }
+                self.src.push_str("}\n");
+            }
+            abi::Instruction::PointerLoad { offset } => {
+                let ptr_type = self.gen.gen.opts.ptr_type();
+                self.load(ptr_type, *offset, operands, results)
+            }
+            abi::Instruction::LengthLoad { offset } => {
+                self.load("size_t", *offset, operands, results)
+            }
+            abi::Instruction::PointerStore { offset } => {
+                let ptr_type = self.gen.gen.opts.ptr_type();
+                self.store(ptr_type, *offset, operands)
+            }
+            abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands),
+            abi::Instruction::FutureLower { .. } => todo!(),
+            abi::Instruction::FutureLift { .. } => todo!(),
+            abi::Instruction::StreamLower { .. } => todo!(),
+            abi::Instruction::StreamLift { .. } => todo!(),
+            abi::Instruction::ErrorContextLower { .. } => todo!(),
+            abi::Instruction::ErrorContextLift { .. } => todo!(),
+            abi::Instruction::AsyncCallWasm { .. } => todo!(),
+            abi::Instruction::AsyncPostCallInterface { .. } => todo!(),
+            abi::Instruction::AsyncCallReturn { .. } => todo!(),
+            abi::Instruction::Flush { amt } => {
+                for i in 0..*amt {
+                    let tmp = self.tmp();
+                    let result = format!("result{}", tmp);
+                    uwriteln!(self.src, "auto {result} = {};", operands[i]);
+                    results.push(result);
+                }
+            }
+        }
+    }
+
+    fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand {
+        let tmp = self.tmp();
+        let size_string = size.format(POINTER_SIZE_EXPRESSION);
+        //let elems = (size + (align - 1)) / align;
+        let tp = match align {
+            Alignment::Bytes(bytes) => match bytes.get() {
+                1 => "uint8_t",
+                2 => "uint16_t",
+                4 => "uint32_t",
+                8 => "uint64_t",
+                _ => todo!(),
+            },
+            Alignment::Pointer => "uintptr_t",
+        };
+        let static_var = if self.gen.in_guest_import {
+            ""
+        } else {
+            "static "
+        };
+        uwriteln!(
+            self.src,
+            "{static_var}{tp} ret_area[({size_string}+sizeof({tp})-1)/sizeof({tp})];"
+        );
+        uwriteln!(
+            self.src,
+            "{} ptr{tmp} = ({0})(&ret_area);",
+            self.gen.gen.opts.ptr_type(),
+        );
+
+        format!("ptr{}", tmp)
+    }
+
+    fn push_block(&mut self) {
+        let prev = core::mem::take(&mut self.src);
+        self.block_storage.push(prev);
+        //        uwriteln!(self.src, "// push_block()");
+    }
+
+    fn finish_block(&mut self, operands: &mut Vec<Self::Operand>) {
+        let to_restore = self.block_storage.pop().unwrap();
+        let src = core::mem::replace(&mut self.src, to_restore);
+        self.blocks.push((src.into(), core::mem::take(operands)));
+        //       uwriteln!(self.src, "// finish_block()");
+    }
+
+    fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign {
+        &self.gen.sizes
+    }
+
+    fn is_list_canonical(
+        &self,
+        resolve: &Resolve,
+        ty: &wit_bindgen_core::wit_parser::Type,
+    ) -> bool {
+        if !resolve.all_bits_valid(ty) {
+            return false;
+        }
+        match ty {
+            Type::Id(id) => !self.has_resources(id),
+            _ => true,
+        }
+    }
+}
+
+/// This describes the common ABI function referenced or implemented, the C++ side might correspond to a different type
+enum SpecialMethod {
+    None,
+    ResourceDrop, // ([export]) [resource-drop]
+    ResourceNew,  // [export][resource-new]
+    ResourceRep,  // [export][resource-rep]
+    Dtor,         // [dtor] (guest export only)
+    Allocate,     // internal: allocate new object (called from generated code)
+}
+
+fn is_special_method(func: &Function) -> SpecialMethod {
+    if matches!(func.kind, FunctionKind::Static(_)) {
+        if func.name.starts_with("[resource-drop]") {
+            SpecialMethod::ResourceDrop
+        } else if func.name.starts_with("[resource-new]") {
+            SpecialMethod::ResourceNew
+        } else if func.name.starts_with("[resource-rep]") {
+            SpecialMethod::ResourceRep
+        } else if func.name.starts_with("[dtor]") {
+            SpecialMethod::Dtor
+        } else if func.name == "$alloc" {
+            SpecialMethod::Allocate
+        } else {
+            SpecialMethod::None
+        }
+    } else {
+        SpecialMethod::None
+    }
+}
+
+// fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool {
+//     match ty {
+//         Type::Id(id) => match resolve.types[*id].kind {
+//             TypeDefKind::Type(t) => is_arg_by_pointer(resolve, &t),
+//             // this is different from C
+//             TypeDefKind::Resource => false,
+//             _ => wit_bindgen_c::is_arg_by_pointer(resolve, ty),
+//         },
+//         _ => wit_bindgen_c::is_arg_by_pointer(resolve, ty),
+//     }
+// }
diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs
new file mode 100644
index 000000000..ebdf5af12
--- /dev/null
+++ b/crates/cpp/src/wamr.rs
@@ -0,0 +1,142 @@
+use wit_bindgen_core::wit_parser::{Function, Resolve, Type, TypeDefKind};
+
+#[derive(Debug, Default)]
+pub struct WamrSig {
+    wamr_types: String,
+    wamr_result: String,
+}
+
+impl ToString for WamrSig {
+    fn to_string(&self) -> String {
+        "(".to_string() + &self.wamr_types + ")" + &self.wamr_result
+    }
+}
+
+fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) {
+    match ty {
+        Type::Bool
+        | Type::U8
+        | Type::U16
+        | Type::U32
+        | Type::S8
+        | Type::S16
+        | Type::S32
+        | Type::Char => {
+            params_str.push('i');
+        }
+        Type::U64 | Type::S64 => {
+            params_str.push('I');
+        }
+        Type::F32 => {
+            params_str.push('f');
+        }
+        Type::F64 => {
+            params_str.push('F');
+        }
+        Type::String => {
+            params_str.push_str("$~");
+        }
+        Type::Id(id) => match &resolve.types[*id].kind {
+            TypeDefKind::Type(t) => push_wamr(t, resolve, params_str),
+            TypeDefKind::Record(_r) => {
+                params_str.push_str("R");
+            }
+            TypeDefKind::Flags(_) => params_str.push_str("L"),
+            TypeDefKind::Tuple(_) => params_str.push_str("T"),
+            TypeDefKind::Variant(_) => params_str.push_str("V"),
+            TypeDefKind::Enum(_e) => {
+                params_str.push_str("i");
+            }
+            TypeDefKind::Option(_) => params_str.push_str("O"),
+            TypeDefKind::Result(_) => params_str.push_str("R"),
+            TypeDefKind::List(_t) => {
+                params_str.push_str("*~");
+            }
+            TypeDefKind::Future(_) => todo!(),
+            TypeDefKind::Stream(_) => todo!(),
+            TypeDefKind::Unknown => todo!(),
+            TypeDefKind::Resource => {
+                params_str.push('i');
+            }
+            TypeDefKind::Handle(_h) => {
+                params_str.push('i');
+            }
+        },
+        Type::ErrorContext => todo!(),
+    }
+}
+
+fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) {
+    match ty {
+        Type::Bool
+        | Type::U8
+        | Type::U16
+        | Type::U32
+        | Type::S8
+        | Type::S16
+        | Type::S32
+        | Type::Char => {
+            sig.wamr_result = "i".into();
+        }
+        Type::S64 | Type::U64 => {
+            sig.wamr_result = "I".into();
+        }
+        Type::F32 => {
+            sig.wamr_result = "f".into();
+        }
+        Type::F64 => {
+            sig.wamr_result = "F".into();
+        }
+        Type::String => {
+            sig.wamr_types.push('*');
+        }
+        Type::Id(id) => match &resolve.types[*id].kind {
+            TypeDefKind::Record(_r) => sig.wamr_types.push('R'),
+            TypeDefKind::Flags(fl) => {
+                sig.wamr_types
+                    .push(if fl.flags.len() > 32 { 'I' } else { 'i' })
+            }
+            TypeDefKind::Tuple(_) => sig.wamr_result.push('i'),
+            TypeDefKind::Variant(_) => {
+                sig.wamr_types.push('*');
+            }
+            TypeDefKind::Enum(_e) => {
+                sig.wamr_types.push('*');
+            }
+            TypeDefKind::Option(_o) => {
+                sig.wamr_types.push('*');
+            }
+            TypeDefKind::Result(_) => {
+                sig.wamr_types.push('*');
+            }
+            TypeDefKind::List(_) => {
+                sig.wamr_types.push('*');
+            }
+            TypeDefKind::Future(_) => todo!(),
+            TypeDefKind::Stream(_) => todo!(),
+            TypeDefKind::Type(ty) => wamr_add_result(sig, resolve, &ty),
+            TypeDefKind::Unknown => todo!(),
+            TypeDefKind::Resource => {
+                // resource-rep is returning a pointer
+                // perhaps i???
+                sig.wamr_result = "*".into();
+            }
+            TypeDefKind::Handle(_h) => {
+                sig.wamr_result = "i".into();
+            }
+        },
+        Type::ErrorContext => todo!(),
+    }
+}
+
+pub fn wamr_signature(resolve: &Resolve, func: &Function) -> WamrSig {
+    let mut result = WamrSig::default();
+    for (_name, param) in func.params.iter() {
+        push_wamr(param, resolve, &mut result.wamr_types);
+    }
+    match &func.result {
+        Some(ty) => wamr_add_result(&mut result, resolve, ty),
+        None => {}
+    }
+    result
+}
diff --git a/crates/cpp/test_headers/expected b/crates/cpp/test_headers/expected
new file mode 100644
index 000000000..fc3df0eed
--- /dev/null
+++ b/crates/cpp/test_headers/expected
@@ -0,0 +1,10 @@
+// work around g++ system header limitation
+#define unexpected unexpected_old
+#include <exception>
+#undef unexpected
+#include <expected.hpp>
+
+namespace std {
+    using ::tl::expected;
+    using ::tl::unexpected;
+}
diff --git a/crates/cpp/test_headers/expected.hpp b/crates/cpp/test_headers/expected.hpp
new file mode 100644
index 000000000..afee404d4
--- /dev/null
+++ b/crates/cpp/test_headers/expected.hpp
@@ -0,0 +1,2444 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 1
+#define TL_EXPECTED_VERSION_PATCH 0
+
+#include <exception>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+#define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define TL_EXPECTED_MSVC2015
+#define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 &&              \
+     !defined(__clang__))
+#define TL_EXPECTED_GCC55
+#endif
+
+#if !defined(TL_ASSERT)
+//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug
+#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49)
+#include <cassert>
+#define TL_ASSERT(x) assert(x)
+#else 
+#define TL_ASSERT(x)
+#endif
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 &&              \
+     !defined(__clang__))
+// GCC < 5 doesn't support overloading on const&& for member functions
+
+#define TL_EXPECTED_NO_CONSTRR
+// GCC < 5 doesn't support some standard C++11 type traits
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  std::has_trivial_copy_constructor<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::has_trivial_copy_assign<T>
+
+// This one will be different for GCC 5.7 if it's ever supported
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks
+// std::vector for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl {
+namespace detail {
+template <class T>
+struct is_trivially_copy_constructible
+    : std::is_trivially_copy_constructible<T> {};
+#ifdef _GLIBCXX_VECTOR
+template <class T, class A>
+struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {};
+#endif
+} // namespace detail
+} // namespace tl
+#endif
+
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  tl::detail::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+#else
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)                         \
+  std::is_trivially_copy_constructible<T>
+#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T)                            \
+  std::is_trivially_copy_assignable<T>
+#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)                               \
+  std::is_trivially_destructible<T>
+#endif
+
+#if __cplusplus > 201103L
+#define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+#define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+#define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) ||                \
+     defined(TL_EXPECTED_GCC49))
+#define TL_EXPECTED_11_CONSTEXPR
+#else
+#define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl {
+template <class T, class E> class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+#define TL_MONOSTATE_INPLACE_MUTEX
+class monostate {};
+
+struct in_place_t {
+  explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template <class E> class unexpected {
+public:
+  static_assert(!std::is_same<E, void>::value, "E must not be void");
+
+  unexpected() = delete;
+  constexpr explicit unexpected(const E &e) : m_val(e) {}
+
+  constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {}
+
+  template <class... Args, typename std::enable_if<std::is_constructible<
+                               E, Args &&...>::value>::type * = nullptr>
+  constexpr explicit unexpected(Args &&...args)
+      : m_val(std::forward<Args>(args)...) {}
+  template <
+      class U, class... Args,
+      typename std::enable_if<std::is_constructible<
+          E, std::initializer_list<U> &, Args &&...>::value>::type * = nullptr>
+  constexpr explicit unexpected(std::initializer_list<U> l, Args &&...args)
+      : m_val(l, std::forward<Args>(args)...) {}
+
+  constexpr const E &value() const & { return m_val; }
+  TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; }
+  TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); }
+  constexpr const E &&value() const && { return std::move(m_val); }
+
+private:
+  E m_val;
+};
+
+#ifdef __cpp_deduction_guides
+template <class E> unexpected(E) -> unexpected<E>;
+#endif
+
+template <class E>
+constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() == rhs.value();
+}
+template <class E>
+constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() != rhs.value();
+}
+template <class E>
+constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() < rhs.value();
+}
+template <class E>
+constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() <= rhs.value();
+}
+template <class E>
+constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() > rhs.value();
+}
+template <class E>
+constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) {
+  return lhs.value() >= rhs.value();
+}
+
+template <class E>
+unexpected<typename std::decay<E>::type> make_unexpected(E &&e) {
+  return unexpected<typename std::decay<E>::type>(std::forward<E>(e));
+}
+
+struct unexpect_t {
+  unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail {
+template <typename E>
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) {
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+  throw std::forward<E>(e);
+#else
+  (void)e;
+#ifdef _MSC_VER
+  __assume(0);
+#else
+  __builtin_unreachable();
+#endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+#define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template <class T> using remove_const_t = typename std::remove_const<T>::type;
+template <class T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <class T> using decay_t = typename std::decay<T>::type;
+template <bool E, class T = void>
+using enable_if_t = typename std::enable_if<E, T>::type;
+template <bool B, class T, class F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+
+// std::conjunction from C++17
+template <class...> struct conjunction : std::true_type {};
+template <class B> struct conjunction<B> : B {};
+template <class B, class... Bs>
+struct conjunction<B, Bs...>
+    : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+// In C++11 mode, there's an issue in libc++'s std::mem_fn
+// which results in a hard-error when using it in a noexcept expression
+// in some cases. This is a check to workaround the common failing case.
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template <class T>
+struct is_pointer_to_non_const_member_func : std::false_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &>
+    : std::true_type {};
+template <class T, class Ret, class... Args>
+struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile &&>
+    : std::true_type {};
+
+template <class T> struct is_const_or_const_ref : std::false_type {};
+template <class T> struct is_const_or_const_ref<T const &> : std::true_type {};
+template <class T> struct is_const_or_const_ref<T const> : std::true_type {};
+#endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template <
+    typename Fn, typename... Args,
+#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+    typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value &&
+                             is_const_or_const_ref<Args...>::value)>,
+#endif
+    typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+    noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+    -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+  return std::mem_fn(f)(std::forward<Args>(args)...);
+}
+
+template <typename Fn, typename... Args,
+          typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+constexpr auto invoke(Fn &&f, Args &&...args) noexcept(
+    noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+    -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+  return std::forward<Fn>(f)(std::forward<Args>(args)...);
+}
+
+// std::invoke_result from C++17
+template <class F, class, class... Us> struct invoke_result_impl;
+
+template <class F, class... Us>
+struct invoke_result_impl<
+    F,
+    decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
+    Us...> {
+  using type =
+      decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+};
+
+template <class F, class... Us>
+using invoke_result = invoke_result_impl<F, void, Us...>;
+
+template <class F, class... Us>
+using invoke_result_t = typename invoke_result<F, Us...>::type;
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template <class T, class U = T> struct is_swappable : std::true_type {};
+
+template <class T, class U = T> struct is_nothrow_swappable : std::true_type {};
+#else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests {
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag {};
+
+template <class T> tag swap(T &, T &);
+template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template <class, class> std::false_type can_swap(...) noexcept(false);
+template <class T, class U,
+          class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
+                                                    std::declval<U &>())));
+
+template <class, class> std::false_type uses_std(...);
+template <class T, class U>
+std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
+uses_std(int);
+
+template <class T>
+struct is_std_swap_noexcept
+    : std::integral_constant<bool,
+                             std::is_nothrow_move_constructible<T>::value &&
+                                 std::is_nothrow_move_assignable<T>::value> {};
+
+template <class T, std::size_t N>
+struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
+
+template <class T, class U>
+struct is_adl_swap_noexcept
+    : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
+} // namespace swap_adl_tests
+
+template <class T, class U = T>
+struct is_swappable
+    : std::integral_constant<
+          bool,
+          decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
+              (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
+               (std::is_move_assignable<T>::value &&
+                std::is_move_constructible<T>::value))> {};
+
+template <class T, std::size_t N>
+struct is_swappable<T[N], T[N]>
+    : std::integral_constant<
+          bool,
+          decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
+              (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(
+                   0))::value ||
+               is_swappable<T, T>::value)> {};
+
+template <class T, class U = T>
+struct is_nothrow_swappable
+    : std::integral_constant<
+          bool,
+          is_swappable<T, U>::value &&
+              ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+                detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
+               (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
+                detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> {};
+#endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template <class T> struct is_expected_impl : std::false_type {};
+template <class T, class E>
+struct is_expected_impl<expected<T, E>> : std::true_type {};
+template <class T> using is_expected = is_expected_impl<decay_t<T>>;
+
+template <class T, class E, class U>
+using expected_enable_forward_value = detail::enable_if_t<
+    std::is_constructible<T, U &&>::value &&
+    !std::is_same<detail::decay_t<U>, in_place_t>::value &&
+    !std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+    !std::is_same<unexpected<E>, detail::decay_t<U>>::value>;
+
+template <class T, class E, class U, class G, class UR, class GR>
+using expected_enable_from_other = detail::enable_if_t<
+    std::is_constructible<T, UR>::value &&
+    std::is_constructible<E, GR>::value &&
+    !std::is_constructible<T, expected<U, G> &>::value &&
+    !std::is_constructible<T, expected<U, G> &&>::value &&
+    !std::is_constructible<T, const expected<U, G> &>::value &&
+    !std::is_constructible<T, const expected<U, G> &&>::value &&
+    !std::is_convertible<expected<U, G> &, T>::value &&
+    !std::is_convertible<expected<U, G> &&, T>::value &&
+    !std::is_convertible<const expected<U, G> &, T>::value &&
+    !std::is_convertible<const expected<U, G> &&, T>::value>;
+
+template <class T, class U>
+using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>;
+
+template <class T>
+using is_copy_constructible_or_void =
+    is_void_or<T, std::is_copy_constructible<T>>;
+
+template <class T>
+using is_move_constructible_or_void =
+    is_void_or<T, std::is_move_constructible<T>>;
+
+template <class T>
+using is_copy_assignable_or_void = is_void_or<T, std::is_copy_assignable<T>>;
+
+template <class T>
+using is_move_assignable_or_void = is_void_or<T, std::is_move_assignable<T>>;
+
+} // namespace detail
+
+namespace detail {
+struct no_init_t {};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template <class T, class E, bool = std::is_trivially_destructible<T>::value,
+          bool = std::is_trivially_destructible<E>::value>
+struct expected_storage_base {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (m_has_val) {
+      m_val.~T();
+    } else {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template <class T, class E> struct expected_storage_base<T, E, true, true> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() = default;
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// T is trivial, E is not.
+template <class T, class E> struct expected_storage_base<T, E, true, false> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t)
+      : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (!m_has_val) {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// E is trivial, T is not.
+template <class T, class E> struct expected_storage_base<T, E, false, true> {
+  constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected_storage_base(in_place_t, Args &&...args)
+      : m_val(std::forward<Args>(args)...), m_has_val(true) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected_storage_base(in_place_t, std::initializer_list<U> il,
+                                  Args &&...args)
+      : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (m_has_val) {
+      m_val.~T();
+    }
+  }
+  union {
+    T m_val;
+    unexpected<E> m_unexpect;
+    char m_no_init;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, true> {
+  #if __GNUC__ <= 5
+  //no constexpr for GCC 4/5 bug
+  #else
+  TL_EXPECTED_MSVC2015_CONSTEXPR
+  #endif 
+  expected_storage_base() : m_has_val(true) {}
+     
+  constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+  constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() = default;
+  struct dummy {};
+  union {
+    unexpected<E> m_unexpect;
+    dummy m_val;
+  };
+  bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template <class E> struct expected_storage_base<void, E, false, false> {
+  constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+  constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+  constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected_storage_base(unexpect_t, Args &&...args)
+      : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected_storage_base(unexpect_t,
+                                           std::initializer_list<U> il,
+                                           Args &&...args)
+      : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
+
+  ~expected_storage_base() {
+    if (!m_has_val) {
+      m_unexpect.~unexpected<E>();
+    }
+  }
+
+  union {
+    unexpected<E> m_unexpect;
+    char m_dummy;
+  };
+  bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class T, class E>
+struct expected_operations_base : expected_storage_base<T, E> {
+  using expected_storage_base<T, E>::expected_storage_base;
+
+  template <class... Args> void construct(Args &&...args) noexcept {
+    new (std::addressof(this->m_val)) T(std::forward<Args>(args)...);
+    this->m_has_val = true;
+  }
+
+  template <class Rhs> void construct_with(Rhs &&rhs) noexcept {
+    new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get());
+    this->m_has_val = true;
+  }
+
+  template <class... Args> void construct_error(Args &&...args) noexcept {
+    new (std::addressof(this->m_unexpect))
+        unexpected<E>(std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+  // These assign overloads ensure that the most efficient assignment
+  // implementation is used while maintaining the strong exception guarantee.
+  // The problematic case is where rhs has a value, but *this does not.
+  //
+  // This overload handles the case where we can just copy-construct `T`
+  // directly into place without throwing.
+  template <class U = T,
+            detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(rhs.get());
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // This overload handles the case where we can attempt to create a copy of
+  // `T`, then no-throw move it into place if the copy was successful.
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+                                std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      T tmp = rhs.get();
+      geterr().~unexpected<E>();
+      construct(std::move(tmp));
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // This overload is the worst-case, where we have to move-construct the
+  // unexpected value into temporary storage, then try to copy the T into place.
+  // If the construction succeeds, then everything is fine, but if it throws,
+  // then we move the old unexpected value back into place before rethrowing the
+  // exception.
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value &&
+                                !std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(const expected_operations_base &rhs) {
+    if (!this->m_has_val && rhs.m_has_val) {
+      auto tmp = std::move(geterr());
+      geterr().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        construct(rhs.get());
+      } catch (...) {
+        geterr() = std::move(tmp);
+        throw;
+      }
+#else
+      construct(rhs.get());
+#endif
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  // These overloads do the same as above, but for rvalues
+  template <class U = T,
+            detail::enable_if_t<std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(expected_operations_base &&rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(std::move(rhs).get());
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value>
+                * = nullptr>
+  void assign(expected_operations_base &&rhs) {
+    if (!this->m_has_val && rhs.m_has_val) {
+      auto tmp = std::move(geterr());
+      geterr().~unexpected<E>();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        construct(std::move(rhs).get());
+      } catch (...) {
+        geterr() = std::move(tmp);
+        throw;
+      }
+#else
+      construct(std::move(rhs).get());
+#endif
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+#else
+
+  // If exceptions are disabled then we can just copy-construct
+  void assign(const expected_operations_base &rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(rhs.get());
+    } else {
+      assign_common(rhs);
+    }
+  }
+
+  void assign(expected_operations_base &&rhs) noexcept {
+    if (!this->m_has_val && rhs.m_has_val) {
+      geterr().~unexpected<E>();
+      construct(std::move(rhs).get());
+    } else {
+      assign_common(std::move(rhs));
+    }
+  }
+
+#endif
+
+  // The common part of move/copy assigning
+  template <class Rhs> void assign_common(Rhs &&rhs) {
+    if (this->m_has_val) {
+      if (rhs.m_has_val) {
+        get() = std::forward<Rhs>(rhs).get();
+      } else {
+        destroy_val();
+        construct_error(std::forward<Rhs>(rhs).geterr());
+      }
+    } else {
+      if (!rhs.m_has_val) {
+        geterr() = std::forward<Rhs>(rhs).geterr();
+      }
+    }
+  }
+
+  bool has_value() const { return this->m_has_val; }
+
+  TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; }
+  constexpr const T &get() const & { return this->m_val; }
+  TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const T &&get() const && { return std::move(this->m_val); }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+    return this->m_unexpect;
+  }
+  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+    return std::move(this->m_unexpect);
+  }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const unexpected<E> &&geterr() const && {
+    return std::move(this->m_unexpect);
+  }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); }
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template <class E>
+struct expected_operations_base<void, E> : expected_storage_base<void, E> {
+  using expected_storage_base<void, E>::expected_storage_base;
+
+  template <class... Args> void construct() noexcept { this->m_has_val = true; }
+
+  // This function doesn't use its argument, but needs it so that code in
+  // levels above this can work independently of whether T is void
+  template <class Rhs> void construct_with(Rhs &&) noexcept {
+    this->m_has_val = true;
+  }
+
+  template <class... Args> void construct_error(Args &&...args) noexcept {
+    new (std::addressof(this->m_unexpect))
+        unexpected<E>(std::forward<Args>(args)...);
+    this->m_has_val = false;
+  }
+
+  template <class Rhs> void assign(Rhs &&rhs) noexcept {
+    if (!this->m_has_val) {
+      if (rhs.m_has_val) {
+        geterr().~unexpected<E>();
+        construct();
+      } else {
+        geterr() = std::forward<Rhs>(rhs).geterr();
+      }
+    } else {
+      if (!rhs.m_has_val) {
+        construct_error(std::forward<Rhs>(rhs).geterr());
+      }
+    }
+  }
+
+  bool has_value() const { return this->m_has_val; }
+
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & {
+    return this->m_unexpect;
+  }
+  constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && {
+    return std::move(this->m_unexpect);
+  }
+#ifndef TL_EXPECTED_NO_CONSTRR
+  constexpr const unexpected<E> &&geterr() const && {
+    return std::move(this->m_unexpect);
+  }
+#endif
+
+  TL_EXPECTED_11_CONSTEXPR void destroy_val() {
+    // no-op
+  }
+};
+
+// This class manages conditionally having a trivial copy constructor
+// This specialization is for when T and E are trivially copy constructible
+template <class T, class E,
+          bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>::
+              value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value>
+struct expected_copy_base : expected_operations_base<T, E> {
+  using expected_operations_base<T, E>::expected_operations_base;
+};
+
+// This specialization is for when T or E are not trivially copy constructible
+template <class T, class E>
+struct expected_copy_base<T, E, false> : expected_operations_base<T, E> {
+  using expected_operations_base<T, E>::expected_operations_base;
+
+  expected_copy_base() = default;
+  expected_copy_base(const expected_copy_base &rhs)
+      : expected_operations_base<T, E>(no_init) {
+    if (rhs.has_value()) {
+      this->construct_with(rhs);
+    } else {
+      this->construct_error(rhs.geterr());
+    }
+  }
+
+  expected_copy_base(expected_copy_base &&rhs) = default;
+  expected_copy_base &operator=(const expected_copy_base &rhs) = default;
+  expected_copy_base &operator=(expected_copy_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move constructor
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_constructible. We
+// have to make do with a non-trivial move constructor even if T is trivially
+// move constructible
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+          bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value
+              &&std::is_trivially_move_constructible<E>::value>
+struct expected_move_base : expected_copy_base<T, E> {
+  using expected_copy_base<T, E>::expected_copy_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_base;
+#endif
+template <class T, class E>
+struct expected_move_base<T, E, false> : expected_copy_base<T, E> {
+  using expected_copy_base<T, E>::expected_copy_base;
+
+  expected_move_base() = default;
+  expected_move_base(const expected_move_base &rhs) = default;
+
+  expected_move_base(expected_move_base &&rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value)
+      : expected_copy_base<T, E>(no_init) {
+    if (rhs.has_value()) {
+      this->construct_with(std::move(rhs));
+    } else {
+      this->construct_error(std::move(rhs.geterr()));
+    }
+  }
+  expected_move_base &operator=(const expected_move_base &rhs) = default;
+  expected_move_base &operator=(expected_move_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial copy assignment operator
+template <class T, class E,
+          bool = is_void_or<
+              T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T),
+                             TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T),
+                             TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value
+              &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value
+                  &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value
+                      &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value>
+struct expected_copy_assign_base : expected_move_base<T, E> {
+  using expected_move_base<T, E>::expected_move_base;
+};
+
+template <class T, class E>
+struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> {
+  using expected_move_base<T, E>::expected_move_base;
+
+  expected_copy_assign_base() = default;
+  expected_copy_assign_base(const expected_copy_assign_base &rhs) = default;
+
+  expected_copy_assign_base(expected_copy_assign_base &&rhs) = default;
+  expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) {
+    this->assign(rhs);
+    return *this;
+  }
+  expected_copy_assign_base &
+  operator=(expected_copy_assign_base &&rhs) = default;
+};
+
+// This class manages conditionally having a trivial move assignment operator
+// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it
+// doesn't implement an analogue to std::is_trivially_move_assignable. We have
+// to make do with a non-trivial move assignment operator even if T is trivially
+// move assignable
+#ifndef TL_EXPECTED_GCC49
+template <class T, class E,
+          bool =
+              is_void_or<T, conjunction<std::is_trivially_destructible<T>,
+                                        std::is_trivially_move_constructible<T>,
+                                        std::is_trivially_move_assignable<T>>>::
+                  value &&std::is_trivially_destructible<E>::value
+                      &&std::is_trivially_move_constructible<E>::value
+                          &&std::is_trivially_move_assignable<E>::value>
+struct expected_move_assign_base : expected_copy_assign_base<T, E> {
+  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+};
+#else
+template <class T, class E, bool = false> struct expected_move_assign_base;
+#endif
+
+template <class T, class E>
+struct expected_move_assign_base<T, E, false>
+    : expected_copy_assign_base<T, E> {
+  using expected_copy_assign_base<T, E>::expected_copy_assign_base;
+
+  expected_move_assign_base() = default;
+  expected_move_assign_base(const expected_move_assign_base &rhs) = default;
+
+  expected_move_assign_base(expected_move_assign_base &&rhs) = default;
+
+  expected_move_assign_base &
+  operator=(const expected_move_assign_base &rhs) = default;
+
+  expected_move_assign_base &
+  operator=(expected_move_assign_base &&rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value
+          &&std::is_nothrow_move_assignable<T>::value) {
+    this->assign(std::move(rhs));
+    return *this;
+  }
+};
+
+// expected_delete_ctor_base will conditionally delete copy and move
+// constructors depending on whether T is copy/move constructible
+template <class T, class E,
+          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+                             std::is_copy_constructible<E>::value),
+          bool EnableMove = (is_move_constructible_or_void<T>::value &&
+                             std::is_move_constructible<E>::value)>
+struct expected_delete_ctor_base {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, true, false> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, true> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_ctor_base<T, E, false, false> {
+  expected_delete_ctor_base() = default;
+  expected_delete_ctor_base(const expected_delete_ctor_base &) = delete;
+  expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete;
+  expected_delete_ctor_base &
+  operator=(const expected_delete_ctor_base &) = default;
+  expected_delete_ctor_base &
+  operator=(expected_delete_ctor_base &&) noexcept = default;
+};
+
+// expected_delete_assign_base will conditionally delete copy and move
+// constructors depending on whether T and E are copy/move constructible +
+// assignable
+template <class T, class E,
+          bool EnableCopy = (is_copy_constructible_or_void<T>::value &&
+                             std::is_copy_constructible<E>::value &&
+                             is_copy_assignable_or_void<T>::value &&
+                             std::is_copy_assignable<E>::value),
+          bool EnableMove = (is_move_constructible_or_void<T>::value &&
+                             std::is_move_constructible<E>::value &&
+                             is_move_assignable_or_void<T>::value &&
+                             std::is_move_assignable<E>::value)>
+struct expected_delete_assign_base {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, true, false> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, true> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = delete;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = default;
+};
+
+template <class T, class E>
+struct expected_delete_assign_base<T, E, false, false> {
+  expected_delete_assign_base() = default;
+  expected_delete_assign_base(const expected_delete_assign_base &) = default;
+  expected_delete_assign_base(expected_delete_assign_base &&) noexcept =
+      default;
+  expected_delete_assign_base &
+  operator=(const expected_delete_assign_base &) = delete;
+  expected_delete_assign_base &
+  operator=(expected_delete_assign_base &&) noexcept = delete;
+};
+
+// This is needed to be able to construct the expected_default_ctor_base which
+// follows, while still conditionally deleting the default constructor.
+struct default_constructor_tag {
+  explicit constexpr default_constructor_tag() = default;
+};
+
+// expected_default_ctor_base will ensure that expected has a deleted default
+// consturctor if T is not default constructible.
+// This specialization is for when T is default constructible
+template <class T, class E,
+          bool Enable =
+              std::is_default_constructible<T>::value || std::is_void<T>::value>
+struct expected_default_ctor_base {
+  constexpr expected_default_ctor_base() noexcept = default;
+  constexpr expected_default_ctor_base(
+      expected_default_ctor_base const &) noexcept = default;
+  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+      default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base const &) noexcept = default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base &&) noexcept = default;
+
+  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+
+// This specialization is for when T is not default constructible
+template <class T, class E> struct expected_default_ctor_base<T, E, false> {
+  constexpr expected_default_ctor_base() noexcept = delete;
+  constexpr expected_default_ctor_base(
+      expected_default_ctor_base const &) noexcept = default;
+  constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept =
+      default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base const &) noexcept = default;
+  expected_default_ctor_base &
+  operator=(expected_default_ctor_base &&) noexcept = default;
+
+  constexpr explicit expected_default_ctor_base(default_constructor_tag) {}
+};
+} // namespace detail
+
+template <class E> class bad_expected_access : public std::exception {
+public:
+  explicit bad_expected_access(E e) : m_val(std::move(e)) {}
+
+  virtual const char *what() const noexcept override {
+    return "Bad expected access";
+  }
+
+  const E &error() const & { return m_val; }
+  E &error() & { return m_val; }
+  const E &&error() const && { return std::move(m_val); }
+  E &&error() && { return std::move(m_val); }
+
+private:
+  E m_val;
+};
+
+/// An `expected<T, E>` object is an object that contains the storage for
+/// another object and manages the lifetime of this contained object `T`.
+/// Alternatively it could contain the storage for another unexpected object
+/// `E`. The contained object may not be initialized after the expected object
+/// has been initialized, and may not be destroyed before the expected object
+/// has been destroyed. The initialization state of the contained object is
+/// tracked by the expected object.
+template <class T, class E>
+class expected : private detail::expected_move_assign_base<T, E>,
+                 private detail::expected_delete_ctor_base<T, E>,
+                 private detail::expected_delete_assign_base<T, E>,
+                 private detail::expected_default_ctor_base<T, E> {
+  static_assert(!std::is_reference<T>::value, "T must not be a reference");
+  static_assert(!std::is_same<T, std::remove_cv<in_place_t>::type>::value,
+                "T must not be in_place_t");
+  static_assert(!std::is_same<T, std::remove_cv<unexpect_t>::type>::value,
+                "T must not be unexpect_t");
+  static_assert(
+      !std::is_same<T, typename std::remove_cv<unexpected<E>>::type>::value,
+      "T must not be unexpected<E>");
+  static_assert(!std::is_reference<E>::value, "E must not be a reference");
+
+  T *valptr() { return std::addressof(this->m_val); }
+  const T *valptr() const { return std::addressof(this->m_val); }
+  unexpected<E> *errptr() { return std::addressof(this->m_unexpect); }
+  const unexpected<E> *errptr() const {
+    return std::addressof(this->m_unexpect);
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &val() {
+    return this->m_val;
+  }
+  TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &val() const {
+    return this->m_val;
+  }
+  constexpr const unexpected<E> &err() const { return this->m_unexpect; }
+
+  using impl_base = detail::expected_move_assign_base<T, E>;
+  using ctor_base = detail::expected_default_ctor_base<T, E>;
+
+public:
+  typedef T value_type;
+  typedef E error_type;
+  typedef unexpected<E> unexpected_type;
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto and_then(F &&f) const & {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F> constexpr auto and_then(F &&f) const && {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR auto
+  and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(),
+                                              std::forward<F>(f))) {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR auto
+  and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(),
+                                               std::forward<F>(f))) {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr auto and_then(F &&f) const & -> decltype(and_then_impl(
+      std::declval<expected const &>(), std::forward<F>(f))) {
+    return and_then_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr auto and_then(F &&f) const && -> decltype(and_then_impl(
+      std::declval<expected const &&>(), std::forward<F>(f))) {
+    return and_then_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto map(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto map(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+      std::declval<expected &>(), std::declval<F &&>()))
+  map(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+                                                      std::declval<F &&>()))
+  map(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+                                       std::declval<F &&>()))
+  map(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+                                       std::declval<F &&>()))
+  map(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(
+      std::declval<expected &>(), std::declval<F &&>()))
+  transform(F &&f) & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(),
+                                                      std::declval<F &&>()))
+  transform(F &&f) && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &>(),
+                                       std::declval<F &&>()))
+  transform(F &&f) const & {
+    return expected_map_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(expected_map_impl(std::declval<const expected &&>(),
+                                       std::declval<F &&>()))
+  transform(F &&f) const && {
+    return expected_map_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto map_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto map_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+                                                   std::declval<F &&>()))
+  map_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+                                                   std::declval<F &&>()))
+  map_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &>(),
+                                    std::declval<F &&>()))
+  map_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+                                    std::declval<F &&>()))
+  map_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F> constexpr auto transform_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#else
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(),
+                                                   std::declval<F &&>()))
+  transform_error(F &&f) & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+  template <class F>
+  TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(),
+                                                   std::declval<F &&>()))
+  transform_error(F &&f) && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &>(),
+                                    std::declval<F &&>()))
+  transform_error(F &&f) const & {
+    return map_error_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F>
+  constexpr decltype(map_error_impl(std::declval<const expected &&>(),
+                                    std::declval<F &&>()))
+  transform_error(F &&f) const && {
+    return map_error_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+#endif
+  template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & {
+    return or_else_impl(*this, std::forward<F>(f));
+  }
+
+  template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && {
+    return or_else_impl(std::move(*this), std::forward<F>(f));
+  }
+
+  template <class F> expected constexpr or_else(F &&f) const & {
+    return or_else_impl(*this, std::forward<F>(f));
+  }
+
+#ifndef TL_EXPECTED_NO_CONSTRR
+  template <class F> expected constexpr or_else(F &&f) const && {
+    return or_else_impl(std::move(*this), std::forward<F>(f));
+  }
+#endif
+  constexpr expected() = default;
+  constexpr expected(const expected &rhs) = default;
+  constexpr expected(expected &&rhs) = default;
+  expected &operator=(const expected &rhs) = default;
+  expected &operator=(expected &&rhs) = default;
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * =
+                nullptr>
+  constexpr expected(in_place_t, Args &&...args)
+      : impl_base(in_place, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr expected(in_place_t, std::initializer_list<U> il, Args &&...args)
+      : impl_base(in_place, il, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class G = E,
+            detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+                nullptr,
+            detail::enable_if_t<!std::is_convertible<const G &, E>::value> * =
+                nullptr>
+  explicit constexpr expected(const unexpected<G> &e)
+      : impl_base(unexpect, e.value()),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, const G &>::value> * =
+          nullptr,
+      detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr>
+  constexpr expected(unexpected<G> const &e)
+      : impl_base(unexpect, e.value()),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+      detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr>
+  explicit constexpr expected(unexpected<G> &&e) noexcept(
+      std::is_nothrow_constructible<E, G &&>::value)
+      : impl_base(unexpect, std::move(e.value())),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <
+      class G = E,
+      detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr,
+      detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr>
+  constexpr expected(unexpected<G> &&e) noexcept(
+      std::is_nothrow_constructible<E, G &&>::value)
+      : impl_base(unexpect, std::move(e.value())),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class... Args,
+            detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * =
+                nullptr>
+  constexpr explicit expected(unexpect_t, Args &&...args)
+      : impl_base(unexpect, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_constructible<
+                E, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  constexpr explicit expected(unexpect_t, std::initializer_list<U> il,
+                              Args &&...args)
+      : impl_base(unexpect, il, std::forward<Args>(args)...),
+        ctor_base(detail::default_constructor_tag{}) {}
+
+  template <class U, class G,
+            detail::enable_if_t<!(std::is_convertible<U const &, T>::value &&
+                                  std::is_convertible<G const &, E>::value)> * =
+                nullptr,
+            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+                * = nullptr>
+  explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(*rhs);
+    } else {
+      this->construct_error(rhs.error());
+    }
+  }
+
+  template <class U, class G,
+            detail::enable_if_t<(std::is_convertible<U const &, T>::value &&
+                                 std::is_convertible<G const &, E>::value)> * =
+                nullptr,
+            detail::expected_enable_from_other<T, E, U, G, const U &, const G &>
+                * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(*rhs);
+    } else {
+      this->construct_error(rhs.error());
+    }
+  }
+
+  template <
+      class U, class G,
+      detail::enable_if_t<!(std::is_convertible<U &&, T>::value &&
+                            std::is_convertible<G &&, E>::value)> * = nullptr,
+      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+  explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(std::move(*rhs));
+    } else {
+      this->construct_error(std::move(rhs.error()));
+    }
+  }
+
+  template <
+      class U, class G,
+      detail::enable_if_t<(std::is_convertible<U &&, T>::value &&
+                           std::is_convertible<G &&, E>::value)> * = nullptr,
+      detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs)
+      : ctor_base(detail::default_constructor_tag{}) {
+    if (rhs.has_value()) {
+      this->construct(std::move(*rhs));
+    } else {
+      this->construct_error(std::move(rhs.error()));
+    }
+  }
+
+  template <
+      class U = T,
+      detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+      detail::expected_enable_forward_value<T, E, U> * = nullptr>
+  explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+      : expected(in_place, std::forward<U>(v)) {}
+
+  template <
+      class U = T,
+      detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+      detail::expected_enable_forward_value<T, E, U> * = nullptr>
+  TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v)
+      : expected(in_place, std::forward<U>(v)) {}
+
+  template <
+      class U = T, class G = T,
+      detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * =
+          nullptr,
+      detail::enable_if_t<!std::is_void<G>::value> * = nullptr,
+      detail::enable_if_t<
+          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+           !detail::conjunction<std::is_scalar<T>,
+                                std::is_same<T, detail::decay_t<U>>>::value &&
+           std::is_constructible<T, U>::value &&
+           std::is_assignable<G &, U>::value &&
+           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+  expected &operator=(U &&v) {
+    if (has_value()) {
+      val() = std::forward<U>(v);
+    } else {
+      err().~unexpected<E>();
+      ::new (valptr()) T(std::forward<U>(v));
+      this->m_has_val = true;
+    }
+
+    return *this;
+  }
+
+  template <
+      class U = T, class G = T,
+      detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * =
+          nullptr,
+      detail::enable_if_t<!std::is_void<U>::value> * = nullptr,
+      detail::enable_if_t<
+          (!std::is_same<expected<T, E>, detail::decay_t<U>>::value &&
+           !detail::conjunction<std::is_scalar<T>,
+                                std::is_same<T, detail::decay_t<U>>>::value &&
+           std::is_constructible<T, U>::value &&
+           std::is_assignable<G &, U>::value &&
+           std::is_nothrow_move_constructible<E>::value)> * = nullptr>
+  expected &operator=(U &&v) {
+    if (has_value()) {
+      val() = std::forward<U>(v);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(std::forward<U>(v));
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(std::forward<U>(v));
+      this->m_has_val = true;
+#endif
+    }
+
+    return *this;
+  }
+
+  template <class G = E,
+            detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value &&
+                                std::is_assignable<G &, G>::value> * = nullptr>
+  expected &operator=(const unexpected<G> &rhs) {
+    if (!has_value()) {
+      err() = rhs;
+    } else {
+      this->destroy_val();
+      ::new (errptr()) unexpected<E>(rhs);
+      this->m_has_val = false;
+    }
+
+    return *this;
+  }
+
+  template <class G = E,
+            detail::enable_if_t<std::is_nothrow_move_constructible<G>::value &&
+                                std::is_move_assignable<G>::value> * = nullptr>
+  expected &operator=(unexpected<G> &&rhs) noexcept {
+    if (!has_value()) {
+      err() = std::move(rhs);
+    } else {
+      this->destroy_val();
+      ::new (errptr()) unexpected<E>(std::move(rhs));
+      this->m_has_val = false;
+    }
+
+    return *this;
+  }
+
+  template <class... Args, detail::enable_if_t<std::is_nothrow_constructible<
+                               T, Args &&...>::value> * = nullptr>
+  void emplace(Args &&...args) {
+    if (has_value()) {
+      val().~T();
+    } else {
+      err().~unexpected<E>();
+      this->m_has_val = true;
+    }
+    ::new (valptr()) T(std::forward<Args>(args)...);
+  }
+
+  template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible<
+                               T, Args &&...>::value> * = nullptr>
+  void emplace(Args &&...args) {
+    if (has_value()) {
+      val().~T();
+      ::new (valptr()) T(std::forward<Args>(args)...);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(std::forward<Args>(args)...);
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(std::forward<Args>(args)...);
+      this->m_has_val = true;
+#endif
+    }
+  }
+
+  template <class U, class... Args,
+            detail::enable_if_t<std::is_nothrow_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  void emplace(std::initializer_list<U> il, Args &&...args) {
+    if (has_value()) {
+      T t(il, std::forward<Args>(args)...);
+      val() = std::move(t);
+    } else {
+      err().~unexpected<E>();
+      ::new (valptr()) T(il, std::forward<Args>(args)...);
+      this->m_has_val = true;
+    }
+  }
+
+  template <class U, class... Args,
+            detail::enable_if_t<!std::is_nothrow_constructible<
+                T, std::initializer_list<U> &, Args &&...>::value> * = nullptr>
+  void emplace(std::initializer_list<U> il, Args &&...args) {
+    if (has_value()) {
+      T t(il, std::forward<Args>(args)...);
+      val() = std::move(t);
+    } else {
+      auto tmp = std::move(err());
+      err().~unexpected<E>();
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+      try {
+        ::new (valptr()) T(il, std::forward<Args>(args)...);
+        this->m_has_val = true;
+      } catch (...) {
+        err() = std::move(tmp);
+        throw;
+      }
+#else
+      ::new (valptr()) T(il, std::forward<Args>(args)...);
+      this->m_has_val = true;
+#endif
+    }
+  }
+
+private:
+  using t_is_void = std::true_type;
+  using t_is_not_void = std::false_type;
+  using t_is_nothrow_move_constructible = std::true_type;
+  using move_constructing_t_can_throw = std::false_type;
+  using e_is_nothrow_move_constructible = std::true_type;
+  using move_constructing_e_can_throw = std::false_type;
+
+  void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept {
+    // swapping void is a no-op
+  }
+
+  void swap_where_both_have_value(expected &rhs, t_is_not_void) {
+    using std::swap;
+    swap(val(), rhs.val());
+  }
+
+  void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept(
+      std::is_nothrow_move_constructible<E>::value) {
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    std::swap(this->m_has_val, rhs.m_has_val);
+  }
+
+  void swap_where_only_one_has_value(expected &rhs, t_is_not_void) {
+    swap_where_only_one_has_value_and_t_is_not_void(
+        rhs, typename std::is_nothrow_move_constructible<T>::type{},
+        typename std::is_nothrow_move_constructible<E>::type{});
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, t_is_nothrow_move_constructible,
+      e_is_nothrow_move_constructible) noexcept {
+    auto temp = std::move(val());
+    val().~T();
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    ::new (rhs.valptr()) T(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, t_is_nothrow_move_constructible,
+      move_constructing_e_can_throw) {
+    auto temp = std::move(val());
+    val().~T();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+    try {
+      ::new (errptr()) unexpected_type(std::move(rhs.err()));
+      rhs.err().~unexpected_type();
+      ::new (rhs.valptr()) T(std::move(temp));
+      std::swap(this->m_has_val, rhs.m_has_val);
+    } catch (...) {
+      val() = std::move(temp);
+      throw;
+    }
+#else
+    ::new (errptr()) unexpected_type(std::move(rhs.err()));
+    rhs.err().~unexpected_type();
+    ::new (rhs.valptr()) T(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+  }
+
+  void swap_where_only_one_has_value_and_t_is_not_void(
+      expected &rhs, move_constructing_t_can_throw,
+      e_is_nothrow_move_constructible) {
+    auto temp = std::move(rhs.err());
+    rhs.err().~unexpected_type();
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+    try {
+      ::new (rhs.valptr()) T(std::move(val()));
+      val().~T();
+      ::new (errptr()) unexpected_type(std::move(temp));
+      std::swap(this->m_has_val, rhs.m_has_val);
+    } catch (...) {
+      rhs.err() = std::move(temp);
+      throw;
+    }
+#else
+    ::new (rhs.valptr()) T(std::move(val()));
+    val().~T();
+    ::new (errptr()) unexpected_type(std::move(temp));
+    std::swap(this->m_has_val, rhs.m_has_val);
+#endif
+  }
+
+public:
+  template <class OT = T, class OE = E>
+  detail::enable_if_t<detail::is_swappable<OT>::value &&
+                      detail::is_swappable<OE>::value &&
+                      (std::is_nothrow_move_constructible<OT>::value ||
+                       std::is_nothrow_move_constructible<OE>::value)>
+  swap(expected &rhs) noexcept(
+      std::is_nothrow_move_constructible<T>::value
+          &&detail::is_nothrow_swappable<T>::value
+              &&std::is_nothrow_move_constructible<E>::value
+                  &&detail::is_nothrow_swappable<E>::value) {
+    if (has_value() && rhs.has_value()) {
+      swap_where_both_have_value(rhs, typename std::is_void<T>::type{});
+    } else if (!has_value() && rhs.has_value()) {
+      rhs.swap(*this);
+    } else if (has_value()) {
+      swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{});
+    } else {
+      using std::swap;
+      swap(err(), rhs.err());
+    }
+  }
+
+  constexpr const T *operator->() const {
+    TL_ASSERT(has_value());
+    return valptr();
+  }
+  TL_EXPECTED_11_CONSTEXPR T *operator->() {
+    TL_ASSERT(has_value());
+    return valptr();
+  }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &operator*() const & {
+    TL_ASSERT(has_value());
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &operator*() & {
+    TL_ASSERT(has_value());
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  constexpr const U &&operator*() const && {
+    TL_ASSERT(has_value());
+    return std::move(val());
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &&operator*() && {
+    TL_ASSERT(has_value());
+    return std::move(val());
+  }
+
+  constexpr bool has_value() const noexcept { return this->m_has_val; }
+  constexpr explicit operator bool() const noexcept { return this->m_has_val; }
+
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR const U &value() const & {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(err().value()));
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &value() & {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(err().value()));
+    return val();
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR const U &&value() const && {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+    return std::move(val());
+  }
+  template <class U = T,
+            detail::enable_if_t<!std::is_void<U>::value> * = nullptr>
+  TL_EXPECTED_11_CONSTEXPR U &&value() && {
+    if (!has_value())
+      detail::throw_exception(bad_expected_access<E>(std::move(err()).value()));
+    return std::move(val());
+  }
+
+  constexpr const E &error() const & {
+    TL_ASSERT(!has_value());
+    return err().value();
+  }
+  TL_EXPECTED_11_CONSTEXPR E &error() & {
+    TL_ASSERT(!has_value());
+    return err().value();
+  }
+  constexpr const E &&error() const && {
+    TL_ASSERT(!has_value());
+    return std::move(err().value());
+  }
+  TL_EXPECTED_11_CONSTEXPR E &&error() && {
+    TL_ASSERT(!has_value());
+    return std::move(err().value());
+  }
+
+  template <class U> constexpr T value_or(U &&v) const & {
+    static_assert(std::is_copy_constructible<T>::value &&
+                      std::is_convertible<U &&, T>::value,
+                  "T must be copy-constructible and convertible to from U&&");
+    return bool(*this) ? **this : static_cast<T>(std::forward<U>(v));
+  }
+  template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && {
+    static_assert(std::is_move_constructible<T>::value &&
+                      std::is_convertible<U &&, T>::value,
+                  "T must be move-constructible and convertible to from U&&");
+    return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v));
+  }
+};
+
+namespace detail {
+template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type;
+template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type;
+template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>;
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value()
+             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+             : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>()))>
+constexpr auto and_then_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value() ? detail::invoke(std::forward<F>(f))
+                         : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class> struct TC;
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr>
+auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value()
+             ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp))
+             : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr>
+constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+
+  return exp.has_value() ? detail::invoke(std::forward<F>(f))
+                         : Ret(unexpect, std::forward<Exp>(exp).error());
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+                                                 *std::forward<Exp>(exp)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = expected<void, err_t<Exp>>;
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+    return result();
+  }
+
+  return result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto expected_map_impl(Exp &&exp, F &&f) {
+  using result = expected<void, err_t<Exp>>;
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f));
+    return result();
+  }
+
+  return result(unexpect, std::forward<Exp>(exp).error());
+}
+#else
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+    -> ret_t<Exp, detail::decay_t<Ret>> {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f),
+                                                 *std::forward<Exp>(exp)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              *std::declval<Exp>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp));
+    return {};
+  }
+
+  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+
+constexpr auto expected_map_impl(Exp &&exp, F &&f)
+    -> ret_t<Exp, detail::decay_t<Ret>> {
+  using result = ret_t<Exp, detail::decay_t<Ret>>;
+
+  return exp.has_value() ? result(detail::invoke(std::forward<F>(f)))
+                         : result(unexpect, std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+
+auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> {
+  if (exp.has_value()) {
+    detail::invoke(std::forward<F>(f));
+    return {};
+  }
+
+  return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error());
+}
+#endif
+
+#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) &&               \
+    !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55)
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+  return exp.has_value()
+             ? result(*std::forward<Exp>(exp))
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result(*std::forward<Exp>(exp));
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+  return exp.has_value()
+             ? result()
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result();
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+#else
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+  return exp.has_value()
+             ? result(*std::forward<Exp>(exp))
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result(*std::forward<Exp>(exp));
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto map_error_impl(Exp &&exp, F &&f)
+    -> expected<exp_t<Exp>, detail::decay_t<Ret>> {
+  using result = expected<exp_t<Exp>, detail::decay_t<Ret>>;
+
+  return exp.has_value()
+             ? result()
+             : result(unexpect, detail::invoke(std::forward<F>(f),
+                                               std::forward<Exp>(exp).error()));
+}
+
+template <class Exp, class F,
+          detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> {
+  using result = expected<exp_t<Exp>, monostate>;
+  if (exp.has_value()) {
+    return result();
+  }
+
+  detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error());
+  return result(unexpect, monostate{});
+}
+#endif
+
+#ifdef TL_EXPECTED_CXX14
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+constexpr auto or_else_impl(Exp &&exp, F &&f) {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : detail::invoke(std::forward<F>(f),
+                                          std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : (detail::invoke(std::forward<F>(f),
+                                           std::forward<Exp>(exp).error()),
+                            std::forward<Exp>(exp));
+}
+#else
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr>
+auto or_else_impl(Exp &&exp, F &&f) -> Ret {
+  static_assert(detail::is_expected<Ret>::value, "F must return an expected");
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : detail::invoke(std::forward<F>(f),
+                                          std::forward<Exp>(exp).error());
+}
+
+template <class Exp, class F,
+          class Ret = decltype(detail::invoke(std::declval<F>(),
+                                              std::declval<Exp>().error())),
+          detail::enable_if_t<std::is_void<Ret>::value> * = nullptr>
+detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) {
+  return exp.has_value() ? std::forward<Exp>(exp)
+                         : (detail::invoke(std::forward<F>(f),
+                                           std::forward<Exp>(exp).error()),
+                            std::forward<Exp>(exp));
+}
+#endif
+} // namespace detail
+
+template <class T, class E, class U, class F>
+constexpr bool operator==(const expected<T, E> &lhs,
+                          const expected<U, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? false
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs);
+}
+template <class T, class E, class U, class F>
+constexpr bool operator!=(const expected<T, E> &lhs,
+                          const expected<U, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? true
+             : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs);
+}
+template <class E, class F>
+constexpr bool operator==(const expected<void, E> &lhs,
+                          const expected<void, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? false
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : true);
+}
+template <class E, class F>
+constexpr bool operator!=(const expected<void, E> &lhs,
+                          const expected<void, F> &rhs) {
+  return (lhs.has_value() != rhs.has_value())
+             ? true
+             : (!lhs.has_value() ? lhs.error() == rhs.error() : false);
+}
+
+template <class T, class E, class U>
+constexpr bool operator==(const expected<T, E> &x, const U &v) {
+  return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator==(const U &v, const expected<T, E> &x) {
+  return x.has_value() ? *x == v : false;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const expected<T, E> &x, const U &v) {
+  return x.has_value() ? *x != v : true;
+}
+template <class T, class E, class U>
+constexpr bool operator!=(const U &v, const expected<T, E> &x) {
+  return x.has_value() ? *x != v : true;
+}
+
+template <class T, class E>
+constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) {
+  return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) {
+  return x.has_value() ? false : x.error() == e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) {
+  return x.has_value() ? true : x.error() != e.value();
+}
+template <class T, class E>
+constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) {
+  return x.has_value() ? true : x.error() != e.value();
+}
+
+template <class T, class E,
+          detail::enable_if_t<(std::is_void<T>::value ||
+                               std::is_move_constructible<T>::value) &&
+                              detail::is_swappable<T>::value &&
+                              std::is_move_constructible<E>::value &&
+                              detail::is_swappable<E>::value> * = nullptr>
+void swap(expected<T, E> &lhs,
+          expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) {
+  lhs.swap(rhs);
+}
+} // namespace tl
+
+#endif
diff --git a/crates/cpp/test_headers/wasm_c_api.h b/crates/cpp/test_headers/wasm_c_api.h
new file mode 100644
index 000000000..6cb1a09bf
--- /dev/null
+++ b/crates/cpp/test_headers/wasm_c_api.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <stdint.h>
+
+typedef uint8_t wasm_valkind_t;
+enum wasm_valkind_enum {
+    WASM_I32,
+    WASM_I64,
+    WASM_F32,
+    WASM_F64,
+};
+typedef struct wasm_val_t {
+    wasm_valkind_t kind;
+    uint8_t __padding[7];
+    union {
+        int32_t i32;
+        int64_t i64;
+        float f32;
+        double f64;
+    } of;
+} wasm_val_t;
+
+#define WASM_INIT_VAL {.kind = WASM_I32, .of = {.i32 = 0}}
+#define WASM_I32_VAL(x) {.kind = WASM_I32, .of = {.i32 =(x)}}
+#define WASM_I64_VAL(x) {.kind = WASM_I64, .of = {.i64 =(x)}}
+#define WASM_F32_VAL(x) {.kind = WASM_F32, .of = {.f32 =(x)}}
+#define WASM_F64_VAL(x) {.kind = WASM_F64, .of = {.f64 =(x)}}
diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h
new file mode 100644
index 000000000..0b4b48aff
--- /dev/null
+++ b/crates/cpp/test_headers/wasm_export.h
@@ -0,0 +1,19 @@
+#pragma once
+// minimal WAMR header mock-up for compilation tests
+#include <stdint.h>
+struct WASMExecEnv;
+typedef WASMExecEnv* wasm_exec_env_t;
+struct WASMModuleInstanceCommon;
+typedef WASMModuleInstanceCommon* wasm_module_inst_t;
+typedef void* wasm_function_inst_t;
+wasm_module_inst_t wasm_runtime_get_module_inst(wasm_exec_env_t);
+void* wasm_runtime_addr_app_to_native(wasm_module_inst_t,int32_t);
+struct NativeSymbol {
+    const char* name;
+    void* func;
+    const char* signature;
+    void* env;
+};
+void wasm_runtime_register_natives(char const* module, NativeSymbol const*, unsigned);
+bool wasm_runtime_call_wasm_a(wasm_exec_env_t, wasm_function_inst_t, uint32_t, struct wasm_val_t*, uint32_t, struct wasm_val_t*);
+wasm_function_inst_t wasm_runtime_lookup_function(wasm_module_inst_t, const char*, const char*);
diff --git a/crates/cpp/test_headers/wit-common.h b/crates/cpp/test_headers/wit-common.h
new file mode 120000
index 000000000..b8079c722
--- /dev/null
+++ b/crates/cpp/test_headers/wit-common.h
@@ -0,0 +1 @@
+../helper-types/wit-common.h
\ No newline at end of file
diff --git a/crates/cpp/test_headers/wit-guest.h b/crates/cpp/test_headers/wit-guest.h
new file mode 120000
index 000000000..8b501851e
--- /dev/null
+++ b/crates/cpp/test_headers/wit-guest.h
@@ -0,0 +1 @@
+../helper-types/wit-guest.h
\ No newline at end of file
diff --git a/crates/cpp/test_headers/wit-host.h b/crates/cpp/test_headers/wit-host.h
new file mode 120000
index 000000000..89d7c0a62
--- /dev/null
+++ b/crates/cpp/test_headers/wit-host.h
@@ -0,0 +1 @@
+../helper-types/wit-host.h
\ No newline at end of file
diff --git a/crates/cpp/tests/.gitignore b/crates/cpp/tests/.gitignore
new file mode 100644
index 000000000..83a8c48a0
--- /dev/null
+++ b/crates/cpp/tests/.gitignore
@@ -0,0 +1,3 @@
+*.o
+*.template
+.vscode
diff --git a/crates/cpp/tests/README.md b/crates/cpp/tests/README.md
new file mode 100644
index 000000000..27ef59264
--- /dev/null
+++ b/crates/cpp/tests/README.md
@@ -0,0 +1,23 @@
+
+This folder contains examples on how to use the canonical ABI without
+a wasm32 target.
+
+The `native_strings` folder contains an example of passing strings, with
+the guest in C++ and Rust, the host in C++, and in the w2c folder an
+example of a wasm component transpiled to C and then executed natively.
+The wamr folder creates a fully binary compatible shared object linking to
+wasm-micro-runtime and interpreting the wasm binary.
+
+Please note that this demonstrates that native compilation, wasm2c and wamr are
+binary compatible and fully exchangeable.
+
+Sadly the [w2c2](https://github.com/turbolent/w2c2) bridge code generation isn't yet complete.
+
+The `native_resources` folder shows a more complex example using resources,
+both guest and host defined ones. This doesn't include a wasm2c deployment.
+
+The `native_mesh` folder shows an example with resources and more than one
+component. Optimizing this is work in progress.
+
+The `meshless_resources` and `meshless_strings` folders experiment
+with directly linking two components in a shared everything environment.
diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs
new file mode 100644
index 000000000..948d109e1
--- /dev/null
+++ b/crates/cpp/tests/codegen.rs
@@ -0,0 +1,160 @@
+use heck::*;
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+macro_rules! codegen_test {
+    ($id:ident $name:tt $test:tt) => {
+        #[test]
+        fn $id() {
+            if [
+                "go_params",
+                "guest-name",
+                "import-func",
+                "import-and-export-resource",
+                "import-and-export-resource-alias",
+                "interface-has-go-keyword",
+                "issue551",
+                "issue573",
+                "issue607",
+                "issue668",
+                "issue929",
+                "issue929-no-export",
+                "issue929-no-import",
+                "issue929-only-methods",
+                "small-anonymous",
+                "wasi-cli",
+                "wasi-clocks",
+                "wasi-filesystem",
+                "wasi-http",
+                "wasi-io",
+                "keywords",
+                "lift-lower-foreign",
+                "lists",
+                "multi-return",
+                "multiversion",
+                "option-result",
+                "record-has-go-keyword-and-used-in-fn",
+                "resource-alias",
+                "resource-borrow-in-record",
+                "resource-borrow-in-record-export",
+                "resource-local-alias",
+                "resource-local-alias-borrow",
+                "resource-local-alias-borrow-import",
+                "resource-own-in-other-interface",
+                "resources",
+                "resources-in-aggregates",
+                "resources-with-lists",
+                "result-empty",
+                "ret-areas",
+                "return-resource-from-export",
+                "same-names5",
+                "simple-http",
+                // "simple-lists",
+                "use-across-interfaces",
+                "variants",
+                "variants-unioning-types",
+                "worlds-with-types",
+                "zero-size-tuple",
+            ]
+            .contains(&$name)
+            {
+                let test_all_code = env::var_os("CPP_ALL_TESTS").is_some();
+                if !test_all_code {
+                    return;
+                }
+            }
+            test_helpers::run_world_codegen_test(
+                "cpp",
+                $test.as_ref(),
+                |resolve, world, files| {
+                    let mut opts = wit_bindgen_cpp::Opts::default();
+                    opts.new_api = true;
+                    opts.build().generate(resolve, world, files).unwrap()
+                },
+                verify,
+            );
+            let test_host_code = env::var_os("CPP_HOST_TESTS").is_some();
+            if test_host_code {
+                test_helpers::run_world_codegen_test(
+                    "cpp-host",
+                    $test.as_ref(),
+                    |resolve, world, files| {
+                        let mut opts = wit_bindgen_cpp::Opts::default();
+                        opts.host = true;
+                        opts.build().generate(resolve, world, files).unwrap()
+                    },
+                    verify_host,
+                );
+            }
+        }
+    };
+}
+
+test_helpers::codegen_tests!();
+
+fn verify(dir: &Path, name: &str) {
+    let name = name.to_snake_case();
+    let sdk_path = PathBuf::from(
+        env::var_os("WASI_SDK_PATH").expect("environment variable WASI_SDK_PATH should be set"),
+    );
+    let sysroot = sdk_path.join("share/wasi-sysroot");
+    let c_src = dir.join(format!("{name}.cpp"));
+    let additional_includes = PathBuf::from(
+        env::var_os("CARGO_MANIFEST_DIR")
+            .expect("environment variable CARGO_MANIFEST_DIR should get set by cargo"),
+    )
+    .join("test_headers");
+
+    let shared_args = vec![
+        "--sysroot",
+        sysroot.to_str().unwrap(),
+        "-I",
+        dir.to_str().unwrap(),
+        "-I",
+        additional_includes.to_str().unwrap(),
+        // "-Wall",
+        // "-Wextra",
+        // "-Werror",
+        // "-Wno-unused-parameter",
+        "-std=c++2b",
+        "-c",
+        "-o",
+    ];
+
+    let mut cmd = Command::new(sdk_path.join("bin/clang++"));
+    cmd.args(&shared_args);
+    cmd.arg(dir.join("obj.o"));
+    cmd.arg(&c_src);
+    test_helpers::run_command(&mut cmd);
+}
+
+fn verify_host(dir: &Path, name: &str) {
+    let name = name.to_snake_case();
+    let c_src = dir.join(format!("{name}_host.cpp"));
+    let additional_includes = PathBuf::from(
+        env::var_os("CARGO_MANIFEST_DIR")
+            .expect("environment variable CARGO_MANIFEST_DIR should get set by cargo"),
+    )
+    .join("test_headers");
+
+    let shared_args = vec![
+        "-I",
+        dir.to_str().unwrap(),
+        "-I",
+        additional_includes.to_str().unwrap(),
+        // "-Wall",
+        // "-Wextra",
+        // "-Werror",
+        // "-Wno-unused-parameter",
+        "-std=c++2b",
+        "-c",
+        "-o",
+    ];
+
+    let mut cmd = Command::new("clang++");
+    cmd.args(&shared_args);
+    cmd.arg(dir.join("obj.o"));
+    cmd.arg(&c_src);
+    test_helpers::run_command(&mut cmd);
+}
diff --git a/crates/cpp/tests/meshless_resources/.gitignore b/crates/cpp/tests/meshless_resources/.gitignore
new file mode 100644
index 000000000..aef20e490
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/.gitignore
@@ -0,0 +1,2 @@
+/component_a/component_a
+/component_b/libcomponent_b.a
diff --git a/crates/cpp/tests/meshless_resources/Makefile b/crates/cpp/tests/meshless_resources/Makefile
new file mode 100644
index 000000000..bb29df627
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/Makefile
@@ -0,0 +1,13 @@
+
+all:
+	make -C component_b $@
+	make -C component_a $@
+
+bindgen:
+	make -C component_b $@
+	make -C component_a $@
+
+clean:
+	make -C component_b $@
+	make -C component_a $@
+	
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_resources/component_a/Makefile b/crates/cpp/tests/meshless_resources/component_a/Makefile
new file mode 100644
index 000000000..bf15a3c65
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_a/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: component_a
+
+component_a: a.cpp main.cpp
+	$(CXX) $(CXXFLAGS) -o $@ $^ -L../component_b -lcomponent_b
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit -w a --symmetric --wasm64 --format
+
+clean:
+	-rm *~ component_a *.o
diff --git a/crates/cpp/tests/meshless_resources/component_a/a.cpp b/crates/cpp/tests/meshless_resources/component_a/a.cpp
new file mode 120000
index 000000000..7975d92df
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_a/a.cpp
@@ -0,0 +1 @@
+../../native_mesh/component_a/a.cpp
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_resources/component_a/a_cpp.h b/crates/cpp/tests/meshless_resources/component_a/a_cpp.h
new file mode 120000
index 000000000..6a689e8e5
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_a/a_cpp.h
@@ -0,0 +1 @@
+../../native_mesh/component_a/a_cpp.h
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_resources/component_a/main.cpp b/crates/cpp/tests/meshless_resources/component_a/main.cpp
new file mode 120000
index 000000000..c5bb33e5d
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_a/main.cpp
@@ -0,0 +1 @@
+../../native_mesh/component_a/main.cpp
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_resources/component_b/Makefile b/crates/cpp/tests/meshless_resources/component_b/Makefile
new file mode 100644
index 000000000..c871371a6
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_b/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: libcomponent_b.a
+
+libcomponent_b.a: b.o impl.o
+	ar rcvs $@ $^
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit -w b --symmetric --wasm64 --format
+
+clean:
+	-rm *~ *.a *.o
diff --git a/crates/cpp/tests/meshless_resources/component_b/b.cpp b/crates/cpp/tests/meshless_resources/component_b/b.cpp
new file mode 100644
index 000000000..501cd2ece
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_b/b.cpp
@@ -0,0 +1,69 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_b(void);
+void __component_type_object_force_link_b_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_b();
+}
+#endif
+#include "b_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[resource_drop]r"))) void
+    fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(uint8_t *arg0) {
+  exports::foo::foo::resources::R::ResourceDrop((uint8_t *)arg0);
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#[constructor]r")))
+uint8_t *
+fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) {
+  auto result0 = exports::foo::foo::resources::R::New((uint32_t(arg0)));
+  return result0.release()->handle;
+}
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void
+    fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(uint8_t *arg0, int32_t arg1) {
+  (std::ref(*(exports::foo::foo::resources::R *)arg0))
+      .get()
+      .Add((uint32_t(arg1)));
+}
+uint8_t *exports::foo::foo::resources::R::ResourceNew(R *self) {
+  return (uint8_t *)self;
+}
+exports::foo::foo::resources::R *
+exports::foo::foo::resources::R::ResourceRep(uint8_t *id) {
+  return (exports::foo::foo::resources::R *)id;
+}
+void exports::foo::foo::resources::R::ResourceDrop(uint8_t *id) {
+  exports::foo::foo::resources::R::Dtor((exports::foo::foo::resources::R *)id);
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#create")))
+uint8_t *
+fooX3AfooX2FresourcesX00create() {
+  auto result0 = exports::foo::foo::resources::Create();
+  return result0.release()->handle;
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void
+fooX3AfooX2FresourcesX00consume(uint8_t *arg0) {
+  auto obj0 = exports::foo::foo::resources::R::Owned(
+      exports::foo::foo::resources::R::ResourceRep(arg0));
+  exports::foo::foo::resources::Consume(std::move(obj0));
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/meshless_resources/component_b/b_cpp.h b/crates/cpp/tests/meshless_resources/component_b/b_cpp.h
new file mode 100644
index 000000000..39cfdad62
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_b/b_cpp.h
@@ -0,0 +1,20 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_B_H
+#define __CPP_GUEST_BINDINGS_B_H
+#define WIT_SYMMETRIC
+#include "exports-foo-foo-resources-R.h"
+#include <cstdint>
+#include <utility>
+// export_interface Interface(Id { idx: 0 })
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+R::Owned Create();
+void Consume(R::Owned o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h b/crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h
new file mode 100644
index 000000000..25796f33d
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h
@@ -0,0 +1,32 @@
+#pragma once
+#define WIT_SYMMETRIC
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <wit-guest.h>
+/* User class definition file, autogenerated once, then user modified
+ * Updated versions of this file are generated into R.template.
+ */
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceExportBase<R> {
+  uint32_t value;
+
+public:
+  static void Dtor(R *self) { delete self; }
+  R(uint32_t a) : value(a) {}
+  static Owned New(uint32_t a) { return Owned(new R(a)); }
+  void Add(uint32_t b) { value += b; }
+  static uint8_t* ResourceNew(R *self);
+  static R *ResourceRep(uint8_t* id);
+  static void ResourceDrop(uint8_t* id);
+
+  uint32_t get_value() const { return value; }
+};
+
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
diff --git a/crates/cpp/tests/meshless_resources/component_b/impl.cpp b/crates/cpp/tests/meshless_resources/component_b/impl.cpp
new file mode 120000
index 000000000..a95bdb47c
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/component_b/impl.cpp
@@ -0,0 +1 @@
+../../native_mesh/component_b/impl.cpp
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/.gitignore b/crates/cpp/tests/meshless_resources/rust_comp_a/.gitignore
new file mode 100644
index 000000000..ea8c4bf7f
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock
new file mode 100644
index 000000000..2578eb661
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock
@@ -0,0 +1,14 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "rust_comp_a"
+version = "0.1.0"
+dependencies = [
+ "rust_comp_b",
+]
+
+[[package]]
+name = "rust_comp_b"
+version = "0.1.0"
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml
new file mode 100644
index 000000000..c36e84d15
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+[package]
+name = "rust_comp_a"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+rust_comp_b = { path = "../rust_comp_b" }
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/Makefile b/crates/cpp/tests/meshless_resources/rust_comp_a/Makefile
new file mode 100644
index 000000000..2e7b4f365
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/Makefile
@@ -0,0 +1,3 @@
+
+bindgen:
+	(cd src; ../../../../../../target/debug/wit-bindgen rust ../../wit/resources_simple.wit -w a --with foo:foo/resources=generate)
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs b/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs
new file mode 100644
index 000000000..6f2a100df
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs
@@ -0,0 +1,7 @@
+use std::env;
+
+fn main() {
+    let source_dir = env::var("OUT_DIR").unwrap();
+    println!("cargo:rustc-link-search=native={}/deps", source_dir);
+    println!("cargo:rustc-link-lib=dylib=rust_comp_b");
+}
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs b/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs
new file mode 100644
index 000000000..d2dd5e071
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs
@@ -0,0 +1,303 @@
+// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code)]
+pub mod foo {
+  #[allow(dead_code)]
+  pub mod foo {
+    #[allow(dead_code, clippy::all)]
+    pub mod resources {
+      #[used]
+      #[doc(hidden)]
+      #[cfg(target_arch = "wasm32")]
+      static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports;
+      use super::super::super::_rt;
+
+      #[derive(Debug)]
+      #[repr(transparent)]
+      pub struct R{
+        handle: _rt::Resource<R>,
+      }
+
+      impl R{
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: usize) -> Self {
+          Self {
+            handle: _rt::Resource::from_handle(handle),
+          }
+        }
+
+        #[doc(hidden)]
+        pub fn take_handle(&self) -> usize{
+          _rt::Resource::take_handle(&self.handle)
+        }
+
+        #[doc(hidden)]
+        pub fn handle(&self) -> usize{
+          _rt::Resource::handle(&self.handle)
+        }
+      }
+
+
+      unsafe impl _rt::WasmResource for R{
+        #[inline]
+        unsafe fn drop(_handle: usize) {
+          {
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]r")]
+              fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_: usize);
+            }
+
+            fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_handle);
+          }
+        }
+      }
+
+      impl R {
+        #[allow(unused_unsafe, clippy::all)]
+        pub fn new(a: u32,) -> Self{
+          unsafe {
+
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]r")]
+              fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> *mut u8;
+            }
+            let ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_rt::as_i32(&a));
+            R::from_handle(ret as usize)
+          }
+        }
+      }
+      impl R {
+        #[allow(unused_unsafe, clippy::all)]
+        pub fn add(&self,b: u32,) -> (){
+          unsafe {
+
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[method]r.add")]
+              fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(_: *mut u8, _: i32, );
+            }
+            fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle() as *mut u8, _rt::as_i32(&b));
+          }
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn create() -> R{
+        unsafe {
+
+          #[link(wasm_import_module = "foo:foo/resources")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "create")]
+            fn fooX3AfooX2FresourcesX00create() -> *mut u8;
+          }
+          let ret = fooX3AfooX2FresourcesX00create();
+          R::from_handle(ret as usize)
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      /// borrows: func(o: borrow<r>);
+      pub fn consume(o: R,) -> (){
+        unsafe {
+
+          #[link(wasm_import_module = "foo:foo/resources")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "consume")]
+            fn fooX3AfooX2FresourcesX00consume(_: *mut u8, );
+          }
+          fooX3AfooX2FresourcesX00consume((&o).take_handle() as *mut u8);
+        }
+      }
+
+    }
+
+  }
+}
+mod _rt {
+
+
+  use core::fmt;
+  use core::marker;
+  use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+
+  /// A type which represents a component model resource, either imported or
+  /// exported into this component.
+  ///
+  /// This is a low-level wrapper which handles the lifetime of the resource
+  /// (namely this has a destructor). The `T` provided defines the component model
+  /// intrinsics that this wrapper uses.
+  ///
+  /// One of the chief purposes of this type is to provide `Deref` implementations
+  /// to access the underlying data when it is owned.
+  ///
+  /// This type is primarily used in generated code for exported and imported
+  /// resources.
+  #[repr(transparent)]
+  pub struct Resource<T: WasmResource> {
+    // NB: This would ideally be `usize` but it is not. The fact that this has
+    // interior mutability is not exposed in the API of this type except for the
+    // `take_handle` method which is supposed to in theory be private.
+    //
+    // This represents, almost all the time, a valid handle value. When it's
+    // invalid it's stored as `0`.
+    handle: AtomicUsize,
+    _marker: marker::PhantomData<T>,
+  }
+
+  /// A trait which all wasm resources implement, namely providing the ability to
+  /// drop a resource.
+  ///
+  /// This generally is implemented by generated code, not user-facing code.
+  #[allow(clippy::missing_safety_doc)]
+  pub unsafe trait WasmResource {
+    /// Invokes the `[resource-drop]...` intrinsic.
+    unsafe fn drop(handle: usize);
+  }
+
+  impl<T: WasmResource> Resource<T> {
+    #[doc(hidden)]
+    pub unsafe fn from_handle(handle: usize) -> Self {
+      debug_assert!(handle != 0);
+      Self {
+        handle: AtomicUsize::new(handle),
+        _marker: marker::PhantomData,
+      }
+    }
+
+    /// Takes ownership of the handle owned by `resource`.
+    ///
+    /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+    /// ownership. The code generator does not enable that in all situations,
+    /// unfortunately, so this is provided instead.
+    ///
+    /// Also note that `take_handle` is in theory only ever called on values
+    /// owned by a generated function. For example a generated function might
+    /// take `Resource<T>` as an argument but then call `take_handle` on a
+    /// reference to that argument. In that sense the dynamic nature of
+    /// `take_handle` should only be exposed internally to generated code, not
+    /// to user code.
+    #[doc(hidden)]
+    pub fn take_handle(resource: &Resource<T>) -> usize {
+      resource.handle.swap(0, Relaxed)
+    }
+
+    #[doc(hidden)]
+    pub fn handle(resource: &Resource<T>) -> usize {
+      resource.handle.load(Relaxed)
+    }
+  }
+
+  impl<T: WasmResource> fmt::Debug for Resource<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+      f.debug_struct("Resource")
+      .field("handle", &self.handle)
+      .finish()
+    }
+  }
+
+  impl<T: WasmResource> Drop for Resource<T> {
+    fn drop(&mut self) {
+      unsafe {
+        match self.handle.load(Relaxed) {
+          // If this handle was "taken" then don't do anything in the
+          // destructor.
+          0 => {}
+
+          // ... but otherwise do actually destroy it with the imported
+          // component model intrinsic as defined through `T`.
+          other => T::drop(other),
+        }
+      }
+    }
+  }
+  
+  pub fn as_i32<T: AsI32>(t: T) -> i32 {
+    t.as_i32()
+  }
+
+  pub trait AsI32 {
+    fn as_i32(self) -> i32;
+  }
+
+  impl<'a, T: Copy + AsI32> AsI32 for &'a T {
+    fn as_i32(self) -> i32 {
+      (*self).as_i32()
+    }
+  }
+  
+  impl AsI32 for i32 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u32 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for i16 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u16 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for i8 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u8 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for char {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for usize {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+}
+
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.28.0:a:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 272] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x98\x01\x01A\x02\x01\
+A\x02\x01B\x0b\x04\0\x01r\x03\x01\x01i\0\x01@\x01\x01ay\0\x01\x04\0\x0e[construc\
+tor]r\x01\x02\x01h\0\x01@\x02\x04self\x03\x01by\x01\0\x04\0\x0d[method]r.add\x01\
+\x04\x01@\0\0\x01\x04\0\x06create\x01\x05\x01@\x01\x01o\x01\x01\0\x04\0\x07consu\
+me\x01\x06\x03\x01\x11foo:foo/resources\x05\0\x04\x01\x09foo:foo/a\x04\0\x0b\x07\
+\x01\0\x01a\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\
+0.214.0\x10wit-bindgen-rust\x060.28.0";
+
+#[inline(never)]
+#[doc(hidden)]
+#[cfg(target_arch = "wasm32")]
+pub fn __link_custom_section_describing_imports() {
+  wit_bindgen::rt::maybe_link_cabi_realloc();
+}
+
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/src/main.rs b/crates/cpp/tests/meshless_resources/rust_comp_a/src/main.rs
new file mode 100644
index 000000000..9acc454d7
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_a/src/main.rs
@@ -0,0 +1,12 @@
+use a::foo::foo::resources;
+
+mod a;
+
+fn main() {
+    {
+        let obj = resources::R::new(5);
+        obj.add(2);
+    }
+    let obj2 = resources::create();
+    resources::consume(obj2);
+}
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/.gitignore b/crates/cpp/tests/meshless_resources/rust_comp_b/.gitignore
new file mode 100644
index 000000000..ea8c4bf7f
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.lock b/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.lock
new file mode 100644
index 000000000..1f246830b
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "rust_comp_b"
+version = "0.1.0"
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.toml b/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.toml
new file mode 100644
index 000000000..a6f9e5ff0
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.toml
@@ -0,0 +1,13 @@
+[workspace]
+
+[package]
+name = "rust_comp_b"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "rust_comp_b"
+crate-type = ["cdylib"]
+#crate-type = ["staticlib", "rlib"]
+
+[dependencies]
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/Makefile b/crates/cpp/tests/meshless_resources/rust_comp_b/Makefile
new file mode 100644
index 000000000..49ea14e22
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/Makefile
@@ -0,0 +1,3 @@
+
+bindgen:
+	(cd src; ../../../../../../target/debug/wit-bindgen rust ../../wit/resources_simple.wit -w b --with foo:foo/resources=generate --wasm64)
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs
new file mode 100644
index 000000000..77eb00084
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs
@@ -0,0 +1,355 @@
+#[allow(dead_code)]
+pub mod exports {
+    #[allow(dead_code)]
+    pub mod foo {
+        #[allow(dead_code)]
+        pub mod foo {
+            #[allow(dead_code, clippy::all)]
+            pub mod resources {
+                #[used]
+                #[doc(hidden)]
+                #[cfg(target_arch = "wasm32")]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+                use super::super::super::super::_rt;
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct R {
+                    handle: _rt::Resource<R>,
+                }
+                type _RRep<T> = Option<T>;
+                impl R {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `R`.
+                    pub fn new<T: GuestR>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _RRep<T> = Some(val);
+                        let ptr: *mut _RRep<T> = _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestR>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestR>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestR>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "threads"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => {
+                                    assert!(
+                                        ty == id,
+                                        "cannot use two types with this resource type"
+                                    )
+                                }
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _RRep<T>);
+                    }
+                    fn as_ptr<T: GuestR>(&self) -> *mut _RRep<T> {
+                        R::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+                /// A borrowed version of [`R`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct RBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a R>,
+                }
+                impl<'a> RBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestR>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+                    fn as_ptr<T: 'static>(&self) -> *mut _RRep<T> {
+                        R::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+                unsafe impl _rt::WasmResource for R {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(wasm_import_module = "foo:foo/resources")]
+                            extern "C" {
+                                #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]r")]
+                                fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_: usize);
+                            }
+                            fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_handle);
+                        }
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_constructor_r_cabi<T: GuestR>(arg0: i32) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = R::new(T::new(arg0 as u32));
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_r_add_cabi<T: GuestR>(arg0: *mut u8, arg1: i32) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::add(RBorrow::lift(arg0 as usize).get(), arg1 as u32);
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_create_cabi<T: Guest>() -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::create();
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_consume_cabi<T: Guest>(arg0: *mut u8) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::consume(R::from_handle(arg0 as usize));
+                }
+                pub trait Guest {
+                    type R: GuestR;
+                    fn create() -> R;
+                    /// borrows: func(o: borrow<r>);
+                    fn consume(o: R) -> ();
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_r_cabi<T: GuestR>(arg0: usize) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    R::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestR: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+                    fn new(a: u32) -> Self;
+                    fn add(&self, b: u32) -> ();
+                }
+                #[doc(hidden)]
+                macro_rules! __export_foo_foo_resources_cabi {
+                    ($ty:ident with_types_in $($path_to_types:tt)*) => {
+                        const _ : () = { #[cfg_attr(target_arch = "wasm32", export_name =
+                        "[constructor]r")] #[cfg_attr(not(target_arch = "wasm32"),
+                        no_mangle)] unsafe extern "C" fn
+                        fooX3AfooX2FresourcesX00X5BconstructorX5Dr(arg0 : i32,) -> * mut
+                        u8 { $($path_to_types)*:: _export_constructor_r_cabi::<<$ty as
+                        $($path_to_types)*:: Guest >::R > (arg0) } #[cfg_attr(target_arch
+                        = "wasm32", export_name = "[method]r.add")]
+                        #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern
+                        "C" fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(arg0 : * mut
+                        u8, arg1 : i32,) { $($path_to_types)*::
+                        _export_method_r_add_cabi::<<$ty as $($path_to_types)*:: Guest
+                        >::R > (arg0, arg1) } #[cfg_attr(target_arch = "wasm32",
+                        export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"),
+                        no_mangle)] unsafe extern "C" fn fooX3AfooX2FresourcesX00create()
+                        -> * mut u8 { $($path_to_types)*:: _export_create_cabi::<$ty > ()
+                        } #[cfg_attr(target_arch = "wasm32", export_name = "consume")]
+                        #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern
+                        "C" fn fooX3AfooX2FresourcesX00consume(arg0 : * mut u8,) {
+                        $($path_to_types)*:: _export_consume_cabi::<$ty > (arg0) }
+                        #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern
+                        "C" fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0 : usize)
+                        { $($path_to_types)*:: _export_drop_r_cabi::<<$ty as
+                        $($path_to_types)*:: Guest >::R > (arg0) } };
+                    };
+                }
+                #[doc(hidden)]
+                pub(crate) use __export_foo_foo_resources_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    use core::fmt;
+    use core::marker;
+    use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+    /// A type which represents a component model resource, either imported or
+    /// exported into this component.
+    ///
+    /// This is a low-level wrapper which handles the lifetime of the resource
+    /// (namely this has a destructor). The `T` provided defines the component model
+    /// intrinsics that this wrapper uses.
+    ///
+    /// One of the chief purposes of this type is to provide `Deref` implementations
+    /// to access the underlying data when it is owned.
+    ///
+    /// This type is primarily used in generated code for exported and imported
+    /// resources.
+    #[repr(transparent)]
+    pub struct Resource<T: WasmResource> {
+        handle: AtomicUsize,
+        _marker: marker::PhantomData<T>,
+    }
+    /// A trait which all wasm resources implement, namely providing the ability to
+    /// drop a resource.
+    ///
+    /// This generally is implemented by generated code, not user-facing code.
+    #[allow(clippy::missing_safety_doc)]
+    pub unsafe trait WasmResource {
+        /// Invokes the `[resource-drop]...` intrinsic.
+        unsafe fn drop(handle: usize);
+    }
+    impl<T: WasmResource> Resource<T> {
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: usize) -> Self {
+            debug_assert!(handle != 0);
+            Self {
+                handle: AtomicUsize::new(handle),
+                _marker: marker::PhantomData,
+            }
+        }
+        /// Takes ownership of the handle owned by `resource`.
+        ///
+        /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+        /// ownership. The code generator does not enable that in all situations,
+        /// unfortunately, so this is provided instead.
+        ///
+        /// Also note that `take_handle` is in theory only ever called on values
+        /// owned by a generated function. For example a generated function might
+        /// take `Resource<T>` as an argument but then call `take_handle` on a
+        /// reference to that argument. In that sense the dynamic nature of
+        /// `take_handle` should only be exposed internally to generated code, not
+        /// to user code.
+        #[doc(hidden)]
+        pub fn take_handle(resource: &Resource<T>) -> usize {
+            resource.handle.swap(0, Relaxed)
+        }
+        #[doc(hidden)]
+        pub fn handle(resource: &Resource<T>) -> usize {
+            resource.handle.load(Relaxed)
+        }
+    }
+    impl<T: WasmResource> fmt::Debug for Resource<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Resource")
+                .field("handle", &self.handle)
+                .finish()
+        }
+    }
+    impl<T: WasmResource> Drop for Resource<T> {
+        fn drop(&mut self) {
+            unsafe {
+                match self.handle.load(Relaxed) {
+                    0 => {}
+                    other => T::drop(other),
+                }
+            }
+        }
+    }
+    pub use alloc_crate::boxed::Box;
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    extern crate alloc as alloc_crate;
+}
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! __export_b_impl {
+    ($ty:ident) => {
+        self::export!($ty with_types_in self);
+    };
+    ($ty:ident with_types_in $($path_to_types_root:tt)*) => {
+        $($path_to_types_root)*::
+        exports::foo::foo::resources::__export_foo_foo_resources_cabi!($ty with_types_in
+        $($path_to_types_root)*:: exports::foo::foo::resources);
+    };
+}
+#[doc(inline)]
+pub(crate) use __export_b_impl as export;
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.28.0:b:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 272] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x98\x01\x01A\x02\x01\
+A\x02\x01B\x0b\x04\0\x01r\x03\x01\x01i\0\x01@\x01\x01ay\0\x01\x04\0\x0e[construc\
+tor]r\x01\x02\x01h\0\x01@\x02\x04self\x03\x01by\x01\0\x04\0\x0d[method]r.add\x01\
+\x04\x01@\0\0\x01\x04\0\x06create\x01\x05\x01@\x01\x01o\x01\x01\0\x04\0\x07consu\
+me\x01\x06\x04\x01\x11foo:foo/resources\x05\0\x04\x01\x09foo:foo/b\x04\0\x0b\x07\
+\x01\0\x01b\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\
+0.214.0\x10wit-bindgen-rust\x060.28.0";
+#[inline(never)]
+#[doc(hidden)]
+#[cfg(target_arch = "wasm32")]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs b/crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs
new file mode 100644
index 000000000..eb353f5ea
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs
@@ -0,0 +1,37 @@
+use std::sync::Mutex;
+
+use b::exports::foo::foo::resources::{self, Guest, GuestR};
+
+mod b;
+
+b::export!(MyWorld with_types_in b);
+
+#[derive(Debug)]
+struct MyResource(Mutex<u32>);
+
+impl GuestR for MyResource {
+    fn new(a: u32) -> Self {
+        MyResource(Mutex::new(a))
+    }
+
+    fn add(&self, b: u32) {
+        *self.0.lock().unwrap() += b;
+    }
+}
+
+struct MyWorld;
+
+impl Guest for MyWorld {
+    type R = MyResource;
+
+    fn create() -> resources::R {
+        resources::R::new(MyResource::new(17))
+    }
+
+    fn consume(o: resources::R) {
+        println!(
+            "resource consumed with {:?}",
+            o.get::<MyResource>().0.lock().unwrap()
+        );
+    }
+}
diff --git a/crates/cpp/tests/meshless_resources/wit/resources_simple.wit b/crates/cpp/tests/meshless_resources/wit/resources_simple.wit
new file mode 120000
index 000000000..b1b09c3ba
--- /dev/null
+++ b/crates/cpp/tests/meshless_resources/wit/resources_simple.wit
@@ -0,0 +1 @@
+../../native_mesh/wit/resources_simple.wit
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_strings/.gitignore b/crates/cpp/tests/meshless_strings/.gitignore
new file mode 100644
index 000000000..aef20e490
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/.gitignore
@@ -0,0 +1,2 @@
+/component_a/component_a
+/component_b/libcomponent_b.a
diff --git a/crates/cpp/tests/meshless_strings/Makefile b/crates/cpp/tests/meshless_strings/Makefile
new file mode 120000
index 000000000..7951ca18d
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/Makefile
@@ -0,0 +1 @@
+../meshless_resources/Makefile
\ No newline at end of file
diff --git a/crates/cpp/tests/meshless_strings/component_a/Makefile b/crates/cpp/tests/meshless_strings/component_a/Makefile
new file mode 100644
index 000000000..1b0496e00
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_a/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: component_a
+
+component_a: the_world.cpp main.cpp
+	$(CXX) $(CXXFLAGS) -o $@ $^ -L../component_b -lcomponent_b
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit --symmetric --internal-prefix=comp_a --new_api --format
+
+clean:
+	-rm *~ component_a *.o
diff --git a/crates/cpp/tests/meshless_strings/component_a/main.cpp b/crates/cpp/tests/meshless_strings/component_a/main.cpp
new file mode 100644
index 000000000..03e6d1448
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_a/main.cpp
@@ -0,0 +1,32 @@
+
+#include "the_world_cpp.h"
+#include <iostream>
+
+void comp_a::exports::foo::foo::strings::A(std::string_view x) {
+  std::cout << x << std::endl;
+}
+wit::string comp_a::exports::foo::foo::strings::B() {
+  wit::string b = wit::string::from_view(std::string_view("hello B"));
+  return b;
+}
+wit::string comp_a::exports::foo::foo::strings::C(std::string_view a,
+                                                  std::string_view b) {
+  std::cout << a << '|' << b << std::endl;
+  wit::string c = wit::string::from_view(std::string_view("hello C"));
+  return c;
+}
+
+int main() {
+  comp_a::foo::foo::strings::A(std::string_view("hello A"));
+
+  {
+    auto b = comp_a::foo::foo::strings::B();
+    std::cout << b.get_view() << std::endl;
+    // make sure that b's result is destructed before calling C
+  }
+
+  auto c = comp_a::foo::foo::strings::C(std::string_view("hello C1"),
+                                        std::string_view("hello C2"));
+  std::cout << c.get_view() << std::endl;
+  return 0;
+}
diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp
new file mode 100644
index 000000000..0ac6756d3
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp
@@ -0,0 +1,105 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_the_world(void);
+void __component_type_object_force_link_the_world_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_the_world();
+}
+#endif
+#include "the_world_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("a"))) void
+fooX3AfooX2FstringsX00a(uint8_t *, size_t);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("b"))) void
+fooX3AfooX2FstringsX00b(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("c"))) void
+fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+void comp_a::foo::foo::strings::A(std::string_view x) {
+  auto const &vec0 = x;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  fooX3AfooX2FstringsX00a(ptr0, len0);
+}
+wit::string comp_a::foo::foo::strings::B() {
+  uintptr_t ret_area[((2 * sizeof(void *)) + sizeof(uintptr_t) - 1) /
+                     sizeof(uintptr_t)];
+  uint8_t *ptr0 = (uint8_t *)(&ret_area);
+  fooX3AfooX2FstringsX00b(ptr0);
+  auto len1 = *((size_t *)(ptr0 + sizeof(void *)));
+
+  return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1);
+}
+wit::string comp_a::foo::foo::strings::C(std::string_view a,
+                                         std::string_view b) {
+  auto const &vec0 = a;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  auto const &vec1 = b;
+  auto ptr1 = (uint8_t *)(vec1.data());
+  auto len1 = (size_t)(vec1.size());
+  uintptr_t ret_area[((2 * sizeof(void *)) + sizeof(uintptr_t) - 1) /
+                     sizeof(uintptr_t)];
+  uint8_t *ptr2 = (uint8_t *)(&ret_area);
+  fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2);
+  auto len3 = *((size_t *)(ptr2 + sizeof(void *)));
+
+  return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3);
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void
+a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) {
+  auto len0 = arg1;
+
+  comp_a::exports::foo::foo::strings::A(
+      std::string_view((char const *)(arg0), len0));
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void
+a_fooX3AfooX2FstringsX00b(uint8_t *arg0) {
+  auto result0 = comp_a::exports::foo::foo::strings::B();
+  auto const &vec1 = result0;
+  auto ptr1 = (uint8_t *)(vec1.data());
+  auto len1 = (size_t)(vec1.size());
+  result0.leak();
+
+  *((size_t *)(arg0 + sizeof(void *))) = len1;
+  *((uint8_t **)(arg0 + 0)) = ptr1;
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void
+a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3,
+                        uint8_t *arg4) {
+  auto len0 = arg1;
+
+  auto len1 = arg3;
+
+  auto result2 = comp_a::exports::foo::foo::strings::C(
+      std::string_view((char const *)(arg0), len0),
+      std::string_view((char const *)(arg2), len1));
+  auto const &vec3 = result2;
+  auto ptr3 = (uint8_t *)(vec3.data());
+  auto len3 = (size_t)(vec3.size());
+  result2.leak();
+
+  *((size_t *)(arg4 + sizeof(void *))) = len3;
+  *((uint8_t **)(arg4 + 0)) = ptr3;
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h
new file mode 100644
index 000000000..019af892c
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h
@@ -0,0 +1,33 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define WIT_SYMMETRIC
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <wit-guest.h>
+namespace comp_a {
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+// export_interface Interface(Id { idx: 0 })
+} // namespace strings
+} // namespace foo
+} // namespace foo
+namespace exports {
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+} // namespace strings
+} // namespace foo
+} // namespace foo
+} // namespace exports
+} // namespace comp_a
+
+#endif
diff --git a/crates/cpp/tests/meshless_strings/component_b/Makefile b/crates/cpp/tests/meshless_strings/component_b/Makefile
new file mode 100644
index 000000000..917578e1e
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_b/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: libcomponent_b.a
+
+libcomponent_b.a: the_world.o guest.o
+	ar rcvs $@ $^
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit --symmetric --new-api --format
+
+clean:
+	-rm *~ *.a *.o
diff --git a/crates/cpp/tests/meshless_strings/component_b/guest.cpp b/crates/cpp/tests/meshless_strings/component_b/guest.cpp
new file mode 100644
index 000000000..e357dbbd3
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_b/guest.cpp
@@ -0,0 +1,13 @@
+#include "the_world_cpp.h"
+
+void exports::foo::foo::strings::A(std::string_view x) {
+    ::foo::foo::strings::A(x);
+}
+
+wit::string exports::foo::foo::strings::B() {
+    return ::foo::foo::strings::B();
+}
+
+wit::string exports::foo::foo::strings::C(std::string_view x, std::string_view b) {
+    return ::foo::foo::strings::C(x, b);
+}
diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp
new file mode 100644
index 000000000..713810a61
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp
@@ -0,0 +1,103 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_the_world(void);
+void __component_type_object_force_link_the_world_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_the_world();
+}
+#endif
+#include "the_world_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("a"))) void
+a_fooX3AfooX2FstringsX00a(uint8_t *, size_t);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("b"))) void
+a_fooX3AfooX2FstringsX00b(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("c"))) void
+a_fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+void foo::foo::strings::A(std::string_view x) {
+  auto const &vec0 = x;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  a_fooX3AfooX2FstringsX00a(ptr0, len0);
+}
+wit::string foo::foo::strings::B() {
+  uintptr_t ret_area[((2 * sizeof(void *)) + sizeof(uintptr_t) - 1) /
+                     sizeof(uintptr_t)];
+  uint8_t *ptr0 = (uint8_t *)(&ret_area);
+  a_fooX3AfooX2FstringsX00b(ptr0);
+  auto len1 = *((size_t *)(ptr0 + sizeof(void *)));
+
+  return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1);
+}
+wit::string foo::foo::strings::C(std::string_view a, std::string_view b) {
+  auto const &vec0 = a;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  auto const &vec1 = b;
+  auto ptr1 = (uint8_t *)(vec1.data());
+  auto len1 = (size_t)(vec1.size());
+  uintptr_t ret_area[((2 * sizeof(void *)) + sizeof(uintptr_t) - 1) /
+                     sizeof(uintptr_t)];
+  uint8_t *ptr2 = (uint8_t *)(&ret_area);
+  a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2);
+  auto len3 = *((size_t *)(ptr2 + sizeof(void *)));
+
+  return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3);
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void
+fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) {
+  auto len0 = arg1;
+
+  exports::foo::foo::strings::A(std::string_view((char const *)(arg0), len0));
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void
+fooX3AfooX2FstringsX00b(uint8_t *arg0) {
+  auto result0 = exports::foo::foo::strings::B();
+  auto const &vec1 = result0;
+  auto ptr1 = (uint8_t *)(vec1.data());
+  auto len1 = (size_t)(vec1.size());
+  result0.leak();
+
+  *((size_t *)(arg0 + sizeof(void *))) = len1;
+  *((uint8_t **)(arg0 + 0)) = ptr1;
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void
+fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3,
+                        uint8_t *arg4) {
+  auto len0 = arg1;
+
+  auto len1 = arg3;
+
+  auto result2 = exports::foo::foo::strings::C(
+      std::string_view((char const *)(arg0), len0),
+      std::string_view((char const *)(arg2), len1));
+  auto const &vec3 = result2;
+  auto ptr3 = (uint8_t *)(vec3.data());
+  auto len3 = (size_t)(vec3.size());
+  result2.leak();
+
+  *((size_t *)(arg4 + sizeof(void *))) = len3;
+  *((uint8_t **)(arg4 + 0)) = ptr3;
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h b/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h
new file mode 100644
index 000000000..718f3ddfd
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h
@@ -0,0 +1,31 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define WIT_SYMMETRIC
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <wit-guest.h>
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+// export_interface Interface(Id { idx: 0 })
+} // namespace strings
+} // namespace foo
+} // namespace foo
+namespace exports {
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+} // namespace strings
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/.gitignore b/crates/cpp/tests/meshless_strings/rust_comp_a/.gitignore
new file mode 100644
index 000000000..ea8c4bf7f
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.lock b/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.lock
new file mode 100644
index 000000000..b4bb8f1be
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "rust_comp_a"
+version = "0.1.0"
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.toml b/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.toml
new file mode 100644
index 000000000..dd669d8a6
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.toml
@@ -0,0 +1,8 @@
+[workspace]
+
+[package]
+name = "rust_comp_a"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs b/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs
new file mode 100644
index 000000000..f6026a6f7
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs
@@ -0,0 +1,11 @@
+use std::env;
+
+fn main() {
+    let source_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+    println!(
+        "cargo::rustc-link-search=native={}/../component_b",
+        source_dir
+    );
+    println!("cargo::rustc-link-lib=static=component_b");
+    println!("cargo::rustc-link-lib=static=stdc++");
+}
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs b/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs
new file mode 100644
index 000000000..c211223fe
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs
@@ -0,0 +1,33 @@
+use the_world::exports::foo::foo::strings::Guest;
+use the_world::foo::foo::strings;
+
+mod the_world;
+
+struct MyWorld;
+
+impl Guest for MyWorld {
+    fn a(x: String) {
+        println!("{x}");
+    }
+
+    fn b() -> String {
+        String::from("hello B")
+    }
+
+    fn c(a: String, b: String) -> String {
+        println!("{a}|{b}");
+        "hello C".into()
+    }
+}
+
+the_world::export!(MyWorld with_types_in the_world);
+
+fn main() {
+    strings::a("hello A");
+    {
+        let b = strings::b();
+        println!("{b}");
+    }
+    let c = strings::c("hello C1", "hello C2");
+    println!("{c}");
+}
diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/src/the_world.rs b/crates/cpp/tests/meshless_strings/rust_comp_a/src/the_world.rs
new file mode 100644
index 000000000..a2ff06f4f
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/rust_comp_a/src/the_world.rs
@@ -0,0 +1,263 @@
+#[allow(dead_code)]
+pub mod foo {
+    #[allow(dead_code)]
+    pub mod foo {
+        #[allow(dead_code, clippy::all)]
+        pub mod strings {
+            #[used]
+            #[doc(hidden)]
+            #[cfg(target_arch = "wasm32")]
+            static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports;
+            use super::super::super::_rt;
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn a(x: &str) -> () {
+                unsafe {
+                    let vec0 = x;
+                    let ptr0 = vec0.as_ptr().cast::<u8>();
+                    let len0 = vec0.len();
+                    #[link(wasm_import_module = "foo:foo/strings")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "a")]
+                        fn fooX3AfooX2FstringsX00a(_: *mut u8, _: usize);
+                    }
+                    fooX3AfooX2FstringsX00a(ptr0.cast_mut(), len0);
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn b() -> _rt::String {
+                unsafe {
+                    #[cfg_attr(target_pointer_width = "64", repr(align(8)))]
+                    #[cfg_attr(target_pointer_width = "32", repr(align(4)))]
+                    struct RetArea(
+                        [::core::mem::MaybeUninit<
+                            u8,
+                        >; (2 * core::mem::size_of::<*const u8>())],
+                    );
+                    let mut ret_area = RetArea(
+                        [::core::mem::MaybeUninit::uninit(); (2
+                            * core::mem::size_of::<*const u8>())],
+                    );
+                    let ptr0 = ret_area.0.as_mut_ptr().cast::<u8>();
+                    #[link(wasm_import_module = "foo:foo/strings")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "b")]
+                        fn fooX3AfooX2FstringsX00b(_: *mut u8);
+                    }
+                    fooX3AfooX2FstringsX00b(ptr0);
+                    let l1 = *ptr0.add(0).cast::<*mut u8>();
+                    let l2 = *ptr0
+                        .add(core::mem::size_of::<*const u8>())
+                        .cast::<usize>();
+                    let len3 = l2;
+                    let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3);
+                    _rt::string_lift(bytes3)
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn c(a: &str, b: &str) -> _rt::String {
+                unsafe {
+                    #[cfg_attr(target_pointer_width = "64", repr(align(8)))]
+                    #[cfg_attr(target_pointer_width = "32", repr(align(4)))]
+                    struct RetArea(
+                        [::core::mem::MaybeUninit<
+                            u8,
+                        >; (2 * core::mem::size_of::<*const u8>())],
+                    );
+                    let mut ret_area = RetArea(
+                        [::core::mem::MaybeUninit::uninit(); (2
+                            * core::mem::size_of::<*const u8>())],
+                    );
+                    let vec0 = a;
+                    let ptr0 = vec0.as_ptr().cast::<u8>();
+                    let len0 = vec0.len();
+                    let vec1 = b;
+                    let ptr1 = vec1.as_ptr().cast::<u8>();
+                    let len1 = vec1.len();
+                    let ptr2 = ret_area.0.as_mut_ptr().cast::<u8>();
+                    #[link(wasm_import_module = "foo:foo/strings")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "c")]
+                        fn fooX3AfooX2FstringsX00c(
+                            _: *mut u8,
+                            _: usize,
+                            _: *mut u8,
+                            _: usize,
+                            _: *mut u8,
+                        );
+                    }
+                    fooX3AfooX2FstringsX00c(
+                        ptr0.cast_mut(),
+                        len0,
+                        ptr1.cast_mut(),
+                        len1,
+                        ptr2,
+                    );
+                    let l3 = *ptr2.add(0).cast::<*mut u8>();
+                    let l4 = *ptr2
+                        .add(core::mem::size_of::<*const u8>())
+                        .cast::<usize>();
+                    let len5 = l4;
+                    let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5);
+                    _rt::string_lift(bytes5)
+                }
+            }
+        }
+    }
+}
+#[allow(dead_code)]
+pub mod exports {
+    #[allow(dead_code)]
+    pub mod foo {
+        #[allow(dead_code)]
+        pub mod foo {
+            #[allow(dead_code, clippy::all)]
+            pub mod strings {
+                #[used]
+                #[doc(hidden)]
+                #[cfg(target_arch = "wasm32")]
+                static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports;
+                use super::super::super::super::_rt;
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_a_cabi<T: Guest>(arg0: *mut u8, arg1: usize) {
+                    #[cfg(target_arch = "wasm32")] _rt::run_ctors_once();
+                    let len0 = arg1;
+                    let string0 = String::from(
+                        std::str::from_utf8(std::slice::from_raw_parts(arg0, len0))
+                            .unwrap(),
+                    );
+                    T::a(string0);
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_b_cabi<T: Guest>(arg0: *mut u8) {
+                    #[cfg(target_arch = "wasm32")] _rt::run_ctors_once();
+                    let result0 = T::b();
+                    let vec1 = (result0.into_bytes()).into_boxed_slice();
+                    let ptr1 = vec1.as_ptr().cast::<u8>();
+                    let len1 = vec1.len();
+                    ::core::mem::forget(vec1);
+                    *arg0.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len1;
+                    *arg0.add(0).cast::<*mut u8>() = ptr1.cast_mut();
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_c_cabi<T: Guest>(
+                    arg0: *mut u8,
+                    arg1: usize,
+                    arg2: *mut u8,
+                    arg3: usize,
+                    arg4: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")] _rt::run_ctors_once();
+                    let len0 = arg1;
+                    let string0 = String::from(
+                        std::str::from_utf8(std::slice::from_raw_parts(arg0, len0))
+                            .unwrap(),
+                    );
+                    let len1 = arg3;
+                    let string1 = String::from(
+                        std::str::from_utf8(std::slice::from_raw_parts(arg2, len1))
+                            .unwrap(),
+                    );
+                    let result2 = T::c(string0, string1);
+                    let vec3 = (result2.into_bytes()).into_boxed_slice();
+                    let ptr3 = vec3.as_ptr().cast::<u8>();
+                    let len3 = vec3.len();
+                    ::core::mem::forget(vec3);
+                    *arg4.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len3;
+                    *arg4.add(0).cast::<*mut u8>() = ptr3.cast_mut();
+                }
+                pub trait Guest {
+                    fn a(x: _rt::String) -> ();
+                    fn b() -> _rt::String;
+                    fn c(a: _rt::String, b: _rt::String) -> _rt::String;
+                }
+                #[doc(hidden)]
+                macro_rules! __export_foo_foo_strings_cabi {
+                    ($ty:ident with_types_in $($path_to_types:tt)*) => {
+                        const _ : () = { #[cfg_attr(target_arch = "wasm32", export_name =
+                        "a")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe
+                        extern "C" fn a_fooX3AfooX2FstringsX00a(arg0 : * mut u8, arg1 :
+                        usize,) { $($path_to_types)*:: _export_a_cabi::<$ty > (arg0,
+                        arg1) } #[cfg_attr(target_arch = "wasm32", export_name = "b")]
+                        #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern
+                        "C" fn a_fooX3AfooX2FstringsX00b(arg0 : * mut u8,) {
+                        $($path_to_types)*:: _export_b_cabi::<$ty > (arg0) }
+                        #[cfg_attr(target_arch = "wasm32", export_name = "c")]
+                        #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern
+                        "C" fn a_fooX3AfooX2FstringsX00c(arg0 : * mut u8, arg1 : usize,
+                        arg2 : * mut u8, arg3 : usize, arg4 : * mut u8,) {
+                        $($path_to_types)*:: _export_c_cabi::<$ty > (arg0, arg1, arg2,
+                        arg3, arg4) } };
+                    };
+                }
+                #[doc(hidden)]
+                pub(crate) use __export_foo_foo_strings_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    pub use alloc_crate::string::String;
+    pub use alloc_crate::vec::Vec;
+    pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
+        if cfg!(debug_assertions) {
+            String::from_utf8(bytes).unwrap()
+        } else {
+            String::from_utf8_unchecked(bytes)
+        }
+    }
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    extern crate alloc as alloc_crate;
+}
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! __export_the_world_impl {
+    ($ty:ident) => {
+        self::export!($ty with_types_in self);
+    };
+    ($ty:ident with_types_in $($path_to_types_root:tt)*) => {
+        $($path_to_types_root)*::
+        exports::foo::foo::strings::__export_foo_foo_strings_cabi!($ty with_types_in
+        $($path_to_types_root)*:: exports::foo::foo::strings);
+    };
+}
+#[doc(inline)]
+pub(crate) use __export_the_world_impl as export;
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.28.0:the-world:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 286] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x9e\x01\x01A\x02\x01\
+A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x01\
+\x01@\x02\x01as\x01bs\0s\x04\0\x01c\x01\x02\x03\x01\x0ffoo:foo/strings\x05\0\x01\
+B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x01\x01@\x02\
+\x01as\x01bs\0s\x04\0\x01c\x01\x02\x04\x01\x0ffoo:foo/strings\x05\x01\x04\x01\x11\
+foo:foo/the-world\x04\0\x0b\x0f\x01\0\x09the-world\x03\0\0\0G\x09producers\x01\x0c\
+processed-by\x02\x0dwit-component\x070.215.0\x10wit-bindgen-rust\x060.28.0";
+#[inline(never)]
+#[doc(hidden)]
+#[cfg(target_arch = "wasm32")]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/meshless_strings/wit b/crates/cpp/tests/meshless_strings/wit
new file mode 120000
index 000000000..feb12df31
--- /dev/null
+++ b/crates/cpp/tests/meshless_strings/wit
@@ -0,0 +1 @@
+../native_strings/wit
\ No newline at end of file
diff --git a/crates/cpp/tests/misc_wit/option_handle.wit b/crates/cpp/tests/misc_wit/option_handle.wit
new file mode 100644
index 000000000..38b19ec4e
--- /dev/null
+++ b/crates/cpp/tests/misc_wit/option_handle.wit
@@ -0,0 +1,11 @@
+package test:test;
+
+interface iface {
+    resource r;
+
+    myfunc: func() -> option<r>;
+}
+
+world myworld {
+    import iface;
+}
diff --git a/crates/cpp/tests/native_mesh/Makefile b/crates/cpp/tests/native_mesh/Makefile
new file mode 100644
index 000000000..497019c6d
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/Makefile
@@ -0,0 +1,16 @@
+
+all:
+	make -C component_b $@
+	make -C mesh $@
+	make -C component_a $@
+
+bindgen:
+	make -C component_b $@
+	make -C mesh $@
+	make -C component_a $@
+
+clean:
+	make -C component_b $@
+	make -C mesh $@
+	make -C component_a $@
+	
\ No newline at end of file
diff --git a/crates/cpp/tests/native_mesh/component_a/Makefile b/crates/cpp/tests/native_mesh/component_a/Makefile
new file mode 100644
index 000000000..829b6275e
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: component_a
+
+component_a: a.cpp main.cpp
+	$(CXX) $(CXXFLAGS) -o $@ $^ libmesh.so libcomponent_b.so
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit -w a --wasm64 --format
+
+clean:
+	-rm *~ component_a *.o
diff --git a/crates/cpp/tests/native_mesh/component_a/a.cpp b/crates/cpp/tests/native_mesh/component_a/a.cpp
new file mode 100644
index 000000000..2698cd8d9
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/a.cpp
@@ -0,0 +1,66 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_a(void);
+void __component_type_object_force_link_a_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_a();
+}
+#endif
+#include "a_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[resource-drop]r"))) void
+fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[constructor]r")))
+uint8_t *fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[method]r.add"))) void
+fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(uint8_t *, int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("create"))) uint8_t *
+fooX3AfooX2FresourcesX00create();
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("consume"))) void
+fooX3AfooX2FresourcesX00consume(uint8_t *);
+foo::foo::resources::R::~R() {
+  if (handle != nullptr) {
+    fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle);
+  }
+}
+foo::foo::resources::R::R(uint32_t a) {
+  auto ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr((int32_t(a)));
+  this->handle = wit::ResourceImportBase{ret}.into_handle();
+}
+void foo::foo::resources::R::Add(uint32_t b) const {
+  fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((*this).get_handle(),
+                                              (int32_t(b)));
+}
+foo::foo::resources::R::R(wit::ResourceImportBase &&b)
+    : wit::ResourceImportBase(std::move(b)) {}
+foo::foo::resources::R foo::foo::resources::Create() {
+  auto ret = fooX3AfooX2FresourcesX00create();
+  return wit::ResourceImportBase{ret};
+}
+void foo::foo::resources::Consume(R &&o) {
+  fooX3AfooX2FresourcesX00consume(o.into_handle());
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_mesh/component_a/a_cpp.h b/crates/cpp/tests/native_mesh/component_a/a_cpp.h
new file mode 100644
index 000000000..01899e67e
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/a_cpp.h
@@ -0,0 +1,29 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_A_H
+#define __CPP_GUEST_BINDINGS_A_H
+#define WIT_SYMMETRIC
+#include <cassert>
+#include <cstdint>
+#include <utility>
+#include <wit-guest.h>
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceImportBase {
+
+public:
+  ~R();
+  R(uint32_t a);
+  void Add(uint32_t b) const;
+  R(wit::ResourceImportBase &&);
+  R(R &&) = default;
+  R &operator=(R &&) = default;
+};
+
+R Create();
+void Consume(R &&o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+
+#endif
diff --git a/crates/cpp/tests/native_mesh/component_a/libcomponent_b.so b/crates/cpp/tests/native_mesh/component_a/libcomponent_b.so
new file mode 120000
index 000000000..4c8d224e8
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/libcomponent_b.so
@@ -0,0 +1 @@
+../component_b/libcomponent_b.so
\ No newline at end of file
diff --git a/crates/cpp/tests/native_mesh/component_a/libmesh.so b/crates/cpp/tests/native_mesh/component_a/libmesh.so
new file mode 120000
index 000000000..30d34c7ac
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/libmesh.so
@@ -0,0 +1 @@
+../mesh/libmesh.so
\ No newline at end of file
diff --git a/crates/cpp/tests/native_mesh/component_a/main.cpp b/crates/cpp/tests/native_mesh/component_a/main.cpp
new file mode 100644
index 000000000..51761d66e
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_a/main.cpp
@@ -0,0 +1,12 @@
+
+#include "a_cpp.h"
+
+int main() {
+    {
+        auto obj = foo::foo::resources::R(5);
+        obj.Add(2);
+    }
+    auto obj2 = foo::foo::resources::Create();
+    foo::foo::resources::Consume(std::move(obj2));
+    return 0;
+}
diff --git a/crates/cpp/tests/native_mesh/component_b/.gitignore b/crates/cpp/tests/native_mesh/component_b/.gitignore
new file mode 100644
index 000000000..eee26b6b8
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/.gitignore
@@ -0,0 +1 @@
+/libcomponent_b.so
diff --git a/crates/cpp/tests/native_mesh/component_b/Makefile b/crates/cpp/tests/native_mesh/component_b/Makefile
new file mode 100644
index 000000000..2fff54abd
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-fPIC -g -O0 -I../../../helper-types
+
+all: libcomponent_b.so
+
+libcomponent_b.so: b.cpp impl.cpp
+	$(CXX) -shared $(CXXFLAGS) -o $@ $^
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit -w b --wasm64 --format
+
+clean:
+	-rm *~ *.so *.o
diff --git a/crates/cpp/tests/native_mesh/component_b/b.cpp b/crates/cpp/tests/native_mesh/component_b/b.cpp
new file mode 100644
index 000000000..7627a7cc2
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/b.cpp
@@ -0,0 +1,81 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_b(void);
+void __component_type_object_force_link_b_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_b();
+}
+#endif
+#include "b_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-new]r"))) int32_t
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *);
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-rep]r")))
+uint8_t *X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t);
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-drop]r"))) void
+    X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t);
+extern "C" __attribute__((__export_name__("foo:foo/resources#[dtor]r"))) void
+fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *arg0) {
+  ((exports::foo::foo::resources::R *)arg0)->handle = -1;
+  exports::foo::foo::resources::R::Dtor(
+      (exports::foo::foo::resources::R *)arg0);
+}
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[constructor]r"))) int32_t
+    fooX3AfooX2FresourcesX23X5BconstructorX5Dr(int32_t arg0) {
+  auto result0 = exports::foo::foo::resources::R::New((uint32_t(arg0)));
+  return result0.release()->handle;
+}
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void
+    fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *arg0, int32_t arg1) {
+  (std::ref(*(exports::foo::foo::resources::R *)arg0))
+      .get()
+      .Add((uint32_t(arg1)));
+}
+int32_t exports::foo::foo::resources::R::ResourceNew(R *self) {
+  return X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(
+      (uint8_t *)self);
+}
+exports::foo::foo::resources::R *
+exports::foo::foo::resources::R::ResourceRep(int32_t id) {
+  return (exports::foo::foo::resources::R *)
+      X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(id);
+}
+void exports::foo::foo::resources::R::ResourceDrop(int32_t id) {
+  X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(id);
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#create"))) int32_t
+fooX3AfooX2FresourcesX23create() {
+  auto result0 = exports::foo::foo::resources::Create();
+  return result0.release()->handle;
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void
+fooX3AfooX2FresourcesX23consume(int32_t arg0) {
+  auto obj0 = exports::foo::foo::resources::R::Owned(
+      exports::foo::foo::resources::R::ResourceRep(arg0));
+  //obj0->into_handle();
+  exports::foo::foo::resources::Consume(std::move(obj0));
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_mesh/component_b/b_cpp.h b/crates/cpp/tests/native_mesh/component_b/b_cpp.h
new file mode 100644
index 000000000..8146a7098
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/b_cpp.h
@@ -0,0 +1,19 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_B_H
+#define __CPP_GUEST_BINDINGS_B_H
+#include "exports-foo-foo-resources-R.h"
+#include <cstdint>
+#include <utility>
+// export_interface Interface(Id { idx: 0 })
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+R::Owned Create();
+void Consume(R::Owned o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/native_mesh/component_b/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_mesh/component_b/exports-foo-foo-resources-R.h
new file mode 100644
index 000000000..b2c0ab0d6
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/exports-foo-foo-resources-R.h
@@ -0,0 +1,31 @@
+#pragma once
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <wit-guest.h>
+/* User class definition file, autogenerated once, then user modified
+ * Updated versions of this file are generated into R.template.
+ */
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceExportBase<R> {
+  uint32_t value;
+
+public:
+  static void Dtor(R *self) { delete self; }
+  R(uint32_t a) : value(a) {}
+  static Owned New(uint32_t a) { return Owned(new R(a)); }
+  void Add(uint32_t b) { value += b; }
+  static int32_t ResourceNew(R *self);
+  static R *ResourceRep(int32_t id);
+  static void ResourceDrop(int32_t id);
+
+  uint32_t get_value() const { return value; }
+};
+
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
diff --git a/crates/cpp/tests/native_mesh/component_b/impl.cpp b/crates/cpp/tests/native_mesh/component_b/impl.cpp
new file mode 100644
index 000000000..b16cdccb6
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/component_b/impl.cpp
@@ -0,0 +1,8 @@
+#include "b_cpp.h"
+
+exports::foo::foo::resources::R::Owned exports::foo::foo::resources::Create() {
+    return R::New(17);
+}
+void exports::foo::foo::resources::Consume(R::Owned o) {
+    printf("Consumed with %d\n", o->get_value());
+}
diff --git a/crates/cpp/tests/native_mesh/mesh/Makefile b/crates/cpp/tests/native_mesh/mesh/Makefile
new file mode 100644
index 000000000..523755e76
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS=-fPIC -g -O0 -I../../../helper-types
+
+all: libmesh.so
+
+libmesh.so: mesh_native.cpp impl.cpp
+	$(CXX) -shared $(CXXFLAGS) -o $@ $^ libcomponent_b.so
+
+bindgen:
+	../../../../../target/debug/wit-bindgen cpp ../wit -w mesh --wasm64 --format --direct --split-interfaces --internal-prefix mesh
+
+clean:
+	-rm *~ libmesh.so *.o
diff --git a/crates/cpp/tests/native_mesh/mesh/impl.cpp b/crates/cpp/tests/native_mesh/mesh/impl.cpp
new file mode 100644
index 000000000..f449e4bc0
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/impl.cpp
@@ -0,0 +1,22 @@
+
+#include "mesh_cpp_native.h"
+
+mesh::foo::foo::resources::R::R(uint32_t a) 
+: impl(exports::foo::foo::resources::R(a)) {}
+
+mesh::foo::foo::resources::R::R(exports::foo::foo::resources::R && a)
+: impl(std::move(a)) {}
+
+void mesh::foo::foo::resources::R::Add(uint32_t b) {
+    impl.Add(b);
+}
+
+mesh::foo::foo::resources::R::Owned 
+mesh::foo::foo::resources::Create() {
+    return mesh::foo::foo::resources::R::Owned(new mesh::foo::foo::resources::R
+        (exports::foo::foo::resources::Create()));
+}
+
+void mesh::foo::foo::resources::Consume(mesh::foo::foo::resources::R::Owned obj) {
+    exports::foo::foo::resources::Consume(obj->into_inner());
+}
diff --git a/crates/cpp/tests/native_mesh/mesh/libcomponent_b.so b/crates/cpp/tests/native_mesh/mesh/libcomponent_b.so
new file mode 120000
index 000000000..4c8d224e8
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/libcomponent_b.so
@@ -0,0 +1 @@
+../component_b/libcomponent_b.so
\ No newline at end of file
diff --git a/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h
new file mode 100644
index 000000000..790301f35
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <cassert>
+#include <cstdint>
+#include <utility>
+#include <wit-host.h>
+namespace mesh {
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceExportBase {
+
+public:
+  ~R();
+  R(uint32_t a);
+  void Add(uint32_t b) const;
+  R(wit::ResourceExportBase &&);
+  R(R &&) = default;
+  R &operator=(R &&) = default;
+};
+
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+} // namespace mesh
diff --git a/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h
new file mode 100644
index 000000000..4e4bc5e58
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h
@@ -0,0 +1,17 @@
+#pragma once
+#include "mesh-exports-foo-foo-resources-R.h"
+#include <cstdint>
+#include <utility>
+// export_interface Interface(Id { idx: 0 })
+namespace mesh {
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+R Create();
+void Consume(R &&o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+} // namespace mesh
diff --git a/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h
new file mode 100644
index 000000000..be64eda70
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h
@@ -0,0 +1,37 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_NATIVE_BINDINGS_MESH_H
+#define __CPP_NATIVE_BINDINGS_MESH_H
+#define WIT_HOST_DIRECT
+#include "mesh-exports-foo-foo-resources.h"
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <wit-host.h>
+/* User class definition file, autogenerated once, then user modified
+ * Updated versions of this file are generated into R.template.
+ */
+namespace mesh {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceImportBase<R> {
+  exports::foo::foo::resources::R impl;
+public:
+  static void Dtor(R *self) { delete self; }
+  R(uint32_t a);
+  R(exports::foo::foo::resources::R && a);
+  static Owned New(uint32_t a) { return Owned(new R(a)); }
+  void Add(uint32_t b);
+  exports::foo::foo::resources::R into_inner() {
+    return std::move(impl);
+  }
+};
+
+R::Owned Create();
+void Consume(R::Owned o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace mesh
+
+#endif
diff --git a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp
new file mode 100644
index 000000000..e399d8cd7
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp
@@ -0,0 +1,72 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#include "mesh_cpp_native.h"
+template <class R> std::map<int32_t, R> wit::ResourceTable<R>::resources;
+#include <assert.h>
+extern "C" void fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *);
+extern "C" int32_t fooX3AfooX2FresourcesX23X5BconstructorX5Dr(int32_t);
+extern "C" void fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *, int32_t);
+extern "C" int32_t fooX3AfooX2FresourcesX23create();
+extern "C" void fooX3AfooX2FresourcesX23consume(int32_t);
+extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) {
+  auto ptr = mesh::foo::foo::resources::R::remove_resource(arg0);
+  assert(ptr.has_value());
+  mesh::foo::foo::resources::R::Dtor(*ptr);
+}
+extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) {
+  auto result0 = mesh::foo::foo::resources::R::New((uint32_t(arg0)));
+  return result0.release()->get_handle();
+}
+extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0,
+                                                            int32_t arg1) {
+  (**mesh::foo::foo::resources::R::lookup_resource(arg0)).Add((uint32_t(arg1)));
+}
+extern "C" int32_t fooX3AfooX2FresourcesX00create() {
+  auto result0 = mesh::foo::foo::resources::Create();
+  return result0.release()->get_handle();
+}
+extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) {
+  auto obj0 = mesh::foo::foo::resources::R::remove_resource(arg0);
+  assert(obj0.has_value());
+  mesh::foo::foo::resources::Consume(
+      mesh::foo::foo::resources::R::Owned(*obj0));
+}
+mesh::exports::foo::foo::resources::R::~R() {
+  if (this->rep) {
+    fooX3AfooX2FresourcesX23X5BdtorX5Dr(this->rep);
+  }
+}
+mesh::exports::foo::foo::resources::R::R(uint32_t a) {
+  auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a)));
+  wit::ResourceExportBase retobj = wit::ResourceExportBase{ret};
+  this->index = retobj.get_handle();
+  this->rep = retobj.take_rep();
+}
+void mesh::exports::foo::foo::resources::R::Add(uint32_t b) const {
+  fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b)));
+}
+mesh::exports::foo::foo::resources::R::R(wit::ResourceExportBase &&b)
+    : wit::ResourceExportBase(std::move(b)) {}
+extern "C" int32_t
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *arg0) {
+  return mesh::exports::foo::foo::resources::R::store_resource(std::move(arg0));
+}
+extern "C" uint8_t *
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t arg0) {
+  return *mesh::exports::foo::foo::resources::R::lookup_resource(arg0);
+}
+extern "C" void
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) {
+  auto obj = mesh::exports::foo::foo::resources::R::remove_resource(arg0);
+  fooX3AfooX2FresourcesX23X5BdtorX5Dr(*obj);
+}
+mesh::exports::foo::foo::resources::R
+mesh::exports::foo::foo::resources::Create() {
+  auto ret = fooX3AfooX2FresourcesX23create();
+  return wit::ResourceExportBase{ret};
+}
+void mesh::exports::foo::foo::resources::Consume(R &&o) {
+  auto rep0 = o.take_rep();
+  fooX3AfooX2FresourcesX23consume(o.get_handle());
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_mesh/wit/resources_simple.wit b/crates/cpp/tests/native_mesh/wit/resources_simple.wit
new file mode 100644
index 000000000..79ff53ce1
--- /dev/null
+++ b/crates/cpp/tests/native_mesh/wit/resources_simple.wit
@@ -0,0 +1,22 @@
+package foo:foo;
+
+interface resources {
+    resource r {
+        constructor(a: u32);
+        add: func(b: u32);
+    }
+    create: func() -> r;
+    //borrows: func(o: borrow<r>);
+    consume: func(o: r);
+}
+
+world a {
+  import resources;
+}
+world b {
+  export resources;
+}
+world mesh {
+  import resources;
+  export resources;
+}
diff --git a/crates/cpp/tests/native_resources/.gitignore b/crates/cpp/tests/native_resources/.gitignore
new file mode 100644
index 000000000..4fc762d50
--- /dev/null
+++ b/crates/cpp/tests/native_resources/.gitignore
@@ -0,0 +1,4 @@
+/*.o
+/*.so
+/app-resources
+/*.template
diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile
new file mode 100644
index 000000000..32746aaa4
--- /dev/null
+++ b/crates/cpp/tests/native_resources/Makefile
@@ -0,0 +1,21 @@
+CXXFLAGS=-g -O0 -I../../helper-types
+WIT_BINDGEN=../../../../target/debug/wit-bindgen
+
+all: libresources.so app-resources
+
+app-resources: the_world_native.o main.o
+	$(CXX) $(CXXFLAGS) -o $@ $^ -L. -lresources
+
+bindgen: wit/resources_simple.wit
+	cd guest; ../$(WIT_BINDGEN) cpp ../wit --wasm64 --format
+	$(WIT_BINDGEN) cpp wit --wasm64 --format --direct
+	cd rust/src ; ../../$(WIT_BINDGEN) rust ../../wit --wasm64
+	
+clean:
+	-rm *.o app-resources
+
+run: app-resources
+	LD_LIBRARY_PATH=. ./app-resources
+
+valgrind: app-resources
+	LD_LIBRARY_PATH=. valgrind --leak-check=full ./app-resources
diff --git a/crates/cpp/tests/native_resources/foo-foo-resources-R.h b/crates/cpp/tests/native_resources/foo-foo-resources-R.h
new file mode 100644
index 000000000..e3591eafe
--- /dev/null
+++ b/crates/cpp/tests/native_resources/foo-foo-resources-R.h
@@ -0,0 +1,22 @@
+/* User class definition file, autogenerated once, then user modified
+ * Updated versions of this file are generated into
+ * foo-foo-resources-R.h.template.
+ */
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceImportBase<R> {
+  uint32_t value;
+
+public:
+  static void Dtor(R *self) { delete self; };
+  R(uint32_t a) : value(a) {}
+  static Owned New(uint32_t a) { return Owned(new R(a)); }
+  void Add(uint32_t b) { value += b; }
+
+  uint32_t GetValue() const { return value; }
+};
+
+} // namespace resources
+} // namespace foo
+} // namespace foo
diff --git a/crates/cpp/tests/native_resources/guest/.gitignore b/crates/cpp/tests/native_resources/guest/.gitignore
new file mode 100644
index 000000000..d6ec319d2
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/.gitignore
@@ -0,0 +1,3 @@
+/*.o
+/*.so
+/*.template
diff --git a/crates/cpp/tests/native_resources/guest/Makefile b/crates/cpp/tests/native_resources/guest/Makefile
new file mode 100644
index 000000000..6522a3264
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/Makefile
@@ -0,0 +1,15 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+
+all: libresources.so
+
+libresources.so: the_world.pie.o guest.pie.o
+	$(CXX) $(CXXFLAGS) -shared -o $@ $^ -Wl,--version-script=guest.verscr
+	
+%.pie.o: %.cpp
+	$(CXX) $(CXXFLAGS) -fPIE -o $@ -c $^
+
+guest.wasm: the_world.cpp guest.cpp
+	/opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) 
+	
+clean:
+	-rm *.o libresources.so
diff --git a/crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h
new file mode 100644
index 000000000..ce8d45fd0
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h
@@ -0,0 +1,28 @@
+/* User class definition file, autogenerated once, then user modified
+ * Updated versions of this file are generated into
+ * exports-foo-foo-resources-R.h.template.
+ */
+#include <memory>
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceExportBase<R> {
+  uint32_t value;
+
+public:
+  static void Dtor(R *self) { delete self; };
+  R(uint32_t a) : value(a) {}
+  static Owned New(uint32_t a) { return Owned(new R(a)); }
+  void Add(uint32_t b) { value += b; }
+  static int32_t ResourceNew(R *self);
+  static R* ResourceRep(int32_t id);
+  static void ResourceDrop(int32_t id);
+
+  uint32_t GetValue() const { return value; }
+};
+
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
diff --git a/crates/cpp/tests/native_resources/guest/guest.cpp b/crates/cpp/tests/native_resources/guest/guest.cpp
new file mode 100644
index 000000000..1cb4096cc
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/guest.cpp
@@ -0,0 +1,22 @@
+#include "the_world_cpp.h"
+#include <stdio.h>
+
+exports::foo::foo::resources::R::Owned exports::foo::foo::resources::Create() {
+    return R::Owned(new R(1));
+}
+
+void exports::foo::foo::resources::Borrows(std::reference_wrapper<const exports::foo::foo::resources::R> o) {
+    printf("resource borrowed with %d\n", o.get().GetValue());
+}
+
+void exports::foo::foo::resources::Consume(R::Owned o) {
+    printf("resource consumed with %d\n", o->GetValue());
+    o.reset();
+
+    printf("exercise the other direction\n");
+    auto obj = ::foo::foo::resources::Create();
+    obj.Add(12);
+    ::foo::foo::resources::Borrows(obj);
+    ::foo::foo::resources::Consume(std::move(obj));
+    auto obj2 = ::foo::foo::resources::R{42};
+}
diff --git a/crates/cpp/tests/native_resources/guest/guest.verscr b/crates/cpp/tests/native_resources/guest/guest.verscr
new file mode 100644
index 000000000..e6862b74e
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/guest.verscr
@@ -0,0 +1,5 @@
+{
+    global:
+        fooX3AfooX2FresourcesX23*;
+    local: *;
+};
diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp
new file mode 100644
index 000000000..d57fdd97b
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/the_world.cpp
@@ -0,0 +1,129 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_the_world(void);
+void __component_type_object_force_link_the_world_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_the_world();
+}
+#endif
+#include "the_world_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[resource-drop]r"))) void
+    fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[constructor]r")))
+int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[method]r.add"))) void
+    fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t, int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("create"))) int32_t
+fooX3AfooX2FresourcesX00create();
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("borrows"))) void
+    fooX3AfooX2FresourcesX00borrows(int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("consume"))) void
+    fooX3AfooX2FresourcesX00consume(int32_t);
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-new]r"))) int32_t
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *);
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-rep]r")))
+uint8_t *X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t);
+extern "C" __attribute__((import_module("[export]foo:foo/resources")))
+__attribute__((import_name("[resource-drop]r"))) void
+    X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t);
+foo::foo::resources::R::~R() {
+  if (handle >= 0) {
+    fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle);
+  }
+}
+foo::foo::resources::R::R(uint32_t a) {
+  auto ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr((int32_t(a)));
+  this->handle = wit::ResourceImportBase{ret}.into_handle();
+}
+void foo::foo::resources::R::Add(uint32_t b) const {
+  fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((*this).get_handle(),
+                                              (int32_t(b)));
+}
+foo::foo::resources::R::R(wit::ResourceImportBase &&b)
+    : wit::ResourceImportBase(std::move(b)) {}
+foo::foo::resources::R foo::foo::resources::Create() {
+  auto ret = fooX3AfooX2FresourcesX00create();
+  return wit::ResourceImportBase{ret};
+}
+void foo::foo::resources::Borrows(std::reference_wrapper<const R> o) {
+  fooX3AfooX2FresourcesX00borrows(o.get().get_handle());
+}
+void foo::foo::resources::Consume(R &&o) {
+  fooX3AfooX2FresourcesX00consume(o.into_handle());
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#[dtor]r"))) void
+fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *arg0) {
+  ((exports::foo::foo::resources::R *)arg0)->handle = -1;
+  exports::foo::foo::resources::R::Dtor(
+      (exports::foo::foo::resources::R *)arg0);
+}
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[constructor]r"))) int32_t
+    fooX3AfooX2FresourcesX23X5BconstructorX5Dr(int32_t arg0) {
+  auto result0 = exports::foo::foo::resources::R::New((uint32_t(arg0)));
+  return result0.release()->handle;
+}
+extern "C"
+    __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void
+    fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *arg0, int32_t arg1) {
+  (std::ref(*(exports::foo::foo::resources::R *)arg0))
+      .get()
+      .Add((uint32_t(arg1)));
+}
+int32_t exports::foo::foo::resources::R::ResourceNew(R *self) {
+  return X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(
+      (uint8_t *)self);
+}
+exports::foo::foo::resources::R *
+exports::foo::foo::resources::R::ResourceRep(int32_t id) {
+  return (exports::foo::foo::resources::R *)
+      X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(id);
+}
+void exports::foo::foo::resources::R::ResourceDrop(int32_t id) {
+  X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(id);
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#create"))) int32_t
+fooX3AfooX2FresourcesX23create() {
+  auto result0 = exports::foo::foo::resources::Create();
+  return result0.release()->handle;
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#borrows"))) void
+fooX3AfooX2FresourcesX23borrows(uint8_t* arg0) {
+  exports::foo::foo::resources::Borrows(
+      std::ref(*(exports::foo::foo::resources::R *)arg0));
+}
+extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void
+fooX3AfooX2FresourcesX23consume(int32_t arg0) {
+  auto obj0 = exports::foo::foo::resources::R::Owned(
+      exports::foo::foo::resources::R::ResourceRep(arg0));
+//  obj0->into_handle();
+  exports::foo::foo::resources::Consume(std::move(obj0));
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_resources/guest/the_world_cpp.h b/crates/cpp/tests/native_resources/guest/the_world_cpp.h
new file mode 100644
index 000000000..d7afa91a2
--- /dev/null
+++ b/crates/cpp/tests/native_resources/guest/the_world_cpp.h
@@ -0,0 +1,43 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define __CPP_GUEST_BINDINGS_THE_WORLD_H
+#include <cassert>
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <wit-guest.h>
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceImportBase {
+
+public:
+  ~R();
+  R(uint32_t a);
+  void Add(uint32_t b) const;
+  R(wit::ResourceImportBase &&);
+  R(R &&) = default;
+  R &operator=(R &&) = default;
+};
+
+R Create();
+void Borrows(std::reference_wrapper<const R> o);
+void Consume(R &&o);
+// export_interface Interface(Id { idx: 0 })
+} // namespace resources
+} // namespace foo
+} // namespace foo
+#include "exports-foo-foo-resources-R.h"
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+R::Owned Create();
+void Borrows(std::reference_wrapper<const R> o);
+void Consume(R::Owned o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/native_resources/libresources.so b/crates/cpp/tests/native_resources/libresources.so
new file mode 120000
index 000000000..8b53f48a4
--- /dev/null
+++ b/crates/cpp/tests/native_resources/libresources.so
@@ -0,0 +1 @@
+rust/target/debug/libresources.so
\ No newline at end of file
diff --git a/crates/cpp/tests/native_resources/main.cpp b/crates/cpp/tests/native_resources/main.cpp
new file mode 100644
index 000000000..3ce2ea1b2
--- /dev/null
+++ b/crates/cpp/tests/native_resources/main.cpp
@@ -0,0 +1,22 @@
+
+#include "the_world_cpp_native.h"
+#include <iostream>
+
+foo::foo::resources::R::Owned foo::foo::resources::Create() { 
+    return R::New(1);
+}
+void foo::foo::resources::Borrows(std::reference_wrapper<R const> o) {
+    printf("resource borrowed with %d\n", o.get().GetValue());
+}
+void foo::foo::resources::Consume(R::Owned o) { 
+    printf("resource consumed with %d\n", o->GetValue());
+}
+
+int main() {
+    auto obj = exports::foo::foo::resources::Create();
+    obj.Add(12);
+    exports::foo::foo::resources::Borrows(obj);
+    exports::foo::foo::resources::Consume(std::move(obj));
+    auto obj2 = exports::foo::foo::resources::R{42};
+    return 0;
+}
diff --git a/crates/cpp/tests/native_resources/rust/Cargo.lock b/crates/cpp/tests/native_resources/rust/Cargo.lock
new file mode 100644
index 000000000..5e8e19daf
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/Cargo.lock
@@ -0,0 +1,308 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "id-arena"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
+
+[[package]]
+name = "indexmap"
+version = "2.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+ "serde",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "leb128"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "resources"
+version = "0.1.0"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+
+[[package]]
+name = "spdx"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "wasm-encoder"
+version = "0.202.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.202.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "spdx",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.202.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413"
+dependencies = [
+ "bitflags",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.24.0"
+dependencies = [
+ "wit-bindgen-rt",
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.24.0"
+dependencies = [
+ "anyhow",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.24.0"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.24.0"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.24.0"
+dependencies = [
+ "anyhow",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.202.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.202.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[patch.unused]]
+name = "wit-parser"
+version = "0.201.0"
+source = "git+https://github.com/bytecodealliance/wasm-tools#9c01485c2713d926e8f5b67b3e6f62f792da8ba7"
diff --git a/crates/cpp/tests/native_resources/rust/Cargo.toml b/crates/cpp/tests/native_resources/rust/Cargo.toml
new file mode 100644
index 000000000..3117f59f0
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/Cargo.toml
@@ -0,0 +1,16 @@
+[workspace]
+
+[package]
+name = "resources"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+# wit-bindgen-rt = "0.21.0"
+wit-bindgen = { path = "../../../../guest-rust" }
+
+[patch.crates-io]
+wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" }
diff --git a/crates/cpp/tests/native_resources/rust/generate.sh b/crates/cpp/tests/native_resources/rust/generate.sh
new file mode 100755
index 000000000..03efe7aed
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/generate.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+cargo component build
+wasm-tools component new target/wasm32-wasi/debug/resources.wasm -o component.wasm --adapt ~/Downloads/wasi_snapshot_preview1.reactor\(1\).wasm 
+jco transpile component.wasm -o html --no-typescript --no-wasi-shim     --map wasi:filesystem/*=./bytecodealliance/preview2-shim/filesystem.js     --map wasi:cli/*=./bytecodealliance/preview2-shim/cli.js     --map wasi:cli-base/*=./bytecodealliance/preview2-shim/cli.js     --map wasi:io/*=./bytecodealliance/preview2-shim/io.js     --map test:example/my-interface=./test_example/my-interface.js     --map foo:foo/resources=./resources.js 
diff --git a/crates/cpp/tests/native_resources/rust/html/component.js b/crates/cpp/tests/native_resources/rust/html/component.js
new file mode 100644
index 000000000..1dde25d99
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/html/component.js
@@ -0,0 +1,1914 @@
+import { exit, getEnvironment, getStderr, getStdin, getStdout } from './bytecodealliance/preview2-shim/cli.js';
+import { Descriptor, filesystemErrorCode, getDirectories } from './bytecodealliance/preview2-shim/filesystem.js';
+import { Error as Error$1, InputStream, OutputStream } from './bytecodealliance/preview2-shim/io.js';
+import { R, borrows, consume, create } from './resources.js';
+
+const base64Compile = str => WebAssembly.compile(typeof Buffer !== 'undefined' ? Buffer.from(str, 'base64') : Uint8Array.from(atob(str), b => b.charCodeAt(0)));
+
+let dv = new DataView(new ArrayBuffer());
+const dataView = mem => dv.buffer === mem.buffer ? dv : dv = new DataView(mem.buffer);
+
+const emptyFunc = () => {};
+
+const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
+let _fs;
+async function fetchCompile (url) {
+  if (isNode) {
+    _fs = _fs || await import('fs/promises');
+    return WebAssembly.compile(await _fs.readFile(url));
+  }
+  return fetch(url).then(WebAssembly.compileStreaming);
+}
+
+function getErrorPayload(e) {
+  if (e && hasOwnProperty.call(e, 'payload')) return e.payload;
+  return e;
+}
+
+const handleTables = [];
+
+const hasOwnProperty = Object.prototype.hasOwnProperty;
+
+const instantiateCore = WebAssembly.instantiate;
+
+const T_FLAG = 1 << 30;
+
+function rscTableCreateOwn (table, rep) {
+  if (rep === 0) throw new Error('Invalid rep');
+  const free = table[0] & ~T_FLAG;
+  if (free === 0) {
+    table.push(0);
+    table.push(rep | T_FLAG);
+    return (table.length >> 1) - 1;
+  }
+  table[0] = table[free << 1];
+  table[free << 1] = 0;
+  table[(free << 1) + 1] = rep | T_FLAG;
+  return free;
+}
+
+function rscTableRemove (table, handle) {
+  const scope = table[handle << 1];
+  const val = table[(handle << 1) + 1];
+  const own = (val & T_FLAG) !== 0;
+  const rep = val & ~T_FLAG;
+  if (val === 0 || (scope & T_FLAG) !== 0) throw new Error('Invalid handle');
+  table[handle << 1] = table[0] | T_FLAG;
+  table[0] = handle | T_FLAG;
+  return { rep, scope, own };
+}
+
+const symbolCabiDispose = Symbol.for('cabiDispose');
+
+const symbolRscHandle = Symbol('handle');
+
+const symbolRscRep = Symbol.for('cabiRep');
+
+const symbolDispose = Symbol.dispose || Symbol.for('dispose');
+
+const toUint64 = val => BigInt.asUintN(64, BigInt(val));
+
+function toUint32(val) {
+  return val >>> 0;
+}
+
+const utf8Encoder = new TextEncoder();
+
+let utf8EncodedLen = 0;
+function utf8Encode(s, realloc, memory) {
+  if (typeof s !== 'string') throw new TypeError('expected a string');
+  if (s.length === 0) {
+    utf8EncodedLen = 0;
+    return 1;
+  }
+  let allocLen = 0;
+  let ptr = 0;
+  let writtenTotal = 0;
+  while (s.length > 0) {
+    ptr = realloc(ptr, allocLen, 1, allocLen += s.length * 2);
+    const { read, written } = utf8Encoder.encodeInto(
+    s,
+    new Uint8Array(memory.buffer, ptr + writtenTotal, allocLen - writtenTotal),
+    );
+    writtenTotal += written;
+    s = s.slice(read);
+  }
+  utf8EncodedLen = writtenTotal;
+  return ptr;
+}
+
+let exports0;
+const handleTable0 = [T_FLAG, 0];
+const captureTable0= new Map();
+let captureCnt0 = 0;
+handleTables[0] = handleTable0;
+
+function trampoline2(arg0) {
+  const ret = new R(arg0 >>> 0);
+  if (!(ret instanceof R)) {
+    throw new Error('Resource error: Not a valid "R" resource.');
+  }
+  var handle0 = ret[symbolRscHandle];
+  
+  if (!handle0) {
+    const rep = ret[symbolRscRep] || ++captureCnt0;
+    captureTable0.set(rep, ret);
+    handle0 = rscTableCreateOwn(handleTable0, rep);
+  }
+  return handle0;
+}
+
+function trampoline3(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable0[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable0.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(R.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  rsc0.add(arg1 >>> 0);
+  rsc0[symbolRscHandle] = null;
+}
+
+function trampoline4() {
+  const ret = create();
+  if (!(ret instanceof R)) {
+    throw new Error('Resource error: Not a valid "R" resource.');
+  }
+  var handle0 = ret[symbolRscHandle];
+  
+  if (!handle0) {
+    const rep = ret[symbolRscRep] || ++captureCnt0;
+    captureTable0.set(rep, ret);
+    handle0 = rscTableCreateOwn(handleTable0, rep);
+  }
+  return handle0;
+}
+
+function trampoline5(arg0) {
+  var handle1 = arg0;
+  var rep2 = handleTable0[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable0.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(R.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  borrows(rsc0);
+  rsc0[symbolRscHandle] = null;
+}
+
+function trampoline6(arg0) {
+  var handle1 = arg0;
+  var rep2 = handleTable0[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable0.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(R.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  else {
+    captureTable0.delete(rep2);
+  }
+  rscTableRemove(handleTable0, handle1);
+  consume(rsc0);
+}
+let exports1;
+const handleTable2 = [T_FLAG, 0];
+const captureTable2= new Map();
+let captureCnt2 = 0;
+handleTables[2] = handleTable2;
+
+function trampoline12() {
+  const ret = getStderr();
+  if (!(ret instanceof OutputStream)) {
+    throw new Error('Resource error: Not a valid "OutputStream" resource.');
+  }
+  var handle0 = ret[symbolRscHandle];
+  
+  if (!handle0) {
+    const rep = ret[symbolRscRep] || ++captureCnt2;
+    captureTable2.set(rep, ret);
+    handle0 = rscTableCreateOwn(handleTable2, rep);
+  }
+  return handle0;
+}
+
+function trampoline13(arg0) {
+  let variant0;
+  switch (arg0) {
+    case 0: {
+      variant0= {
+        tag: 'ok',
+        val: undefined
+      };
+      break;
+    }
+    case 1: {
+      variant0= {
+        tag: 'err',
+        val: undefined
+      };
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant discriminant for expected');
+    }
+  }
+  exit(variant0);
+}
+const handleTable3 = [T_FLAG, 0];
+const captureTable3= new Map();
+let captureCnt3 = 0;
+handleTables[3] = handleTable3;
+
+function trampoline14() {
+  const ret = getStdin();
+  if (!(ret instanceof InputStream)) {
+    throw new Error('Resource error: Not a valid "InputStream" resource.');
+  }
+  var handle0 = ret[symbolRscHandle];
+  
+  if (!handle0) {
+    const rep = ret[symbolRscRep] || ++captureCnt3;
+    captureTable3.set(rep, ret);
+    handle0 = rscTableCreateOwn(handleTable3, rep);
+  }
+  return handle0;
+}
+
+function trampoline15() {
+  const ret = getStdout();
+  if (!(ret instanceof OutputStream)) {
+    throw new Error('Resource error: Not a valid "OutputStream" resource.');
+  }
+  var handle0 = ret[symbolRscHandle];
+  
+  if (!handle0) {
+    const rep = ret[symbolRscRep] || ++captureCnt2;
+    captureTable2.set(rep, ret);
+    handle0 = rscTableCreateOwn(handleTable2, rep);
+  }
+  return handle0;
+}
+let exports2;
+let memory0;
+let realloc0;
+const handleTable4 = [T_FLAG, 0];
+const captureTable4= new Map();
+let captureCnt4 = 0;
+handleTables[4] = handleTable4;
+
+function trampoline16(arg0) {
+  const ret = getDirectories();
+  var vec3 = ret;
+  var len3 = vec3.length;
+  var result3 = realloc0(0, 0, 4, len3 * 12);
+  for (let i = 0; i < vec3.length; i++) {
+    const e = vec3[i];
+    const base = result3 + i * 12;var [tuple0_0, tuple0_1] = e;
+    if (!(tuple0_0 instanceof Descriptor)) {
+      throw new Error('Resource error: Not a valid "Descriptor" resource.');
+    }
+    var handle1 = tuple0_0[symbolRscHandle];
+    
+    if (!handle1) {
+      const rep = tuple0_0[symbolRscRep] || ++captureCnt4;
+      captureTable4.set(rep, tuple0_0);
+      handle1 = rscTableCreateOwn(handleTable4, rep);
+    }
+    dataView(memory0).setInt32(base + 0, handle1, true);
+    var ptr2 = utf8Encode(tuple0_1, realloc0, memory0);
+    var len2 = utf8EncodedLen;
+    dataView(memory0).setInt32(base + 8, len2, true);
+    dataView(memory0).setInt32(base + 4, ptr2, true);
+  }
+  dataView(memory0).setInt32(arg0 + 4, len3, true);
+  dataView(memory0).setInt32(arg0 + 0, result3, true);
+}
+
+function trampoline17(arg0, arg1, arg2) {
+  var handle1 = arg0;
+  var rep2 = handleTable4[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable4.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(Descriptor.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.writeViaStream(BigInt.asUintN(64, arg1))};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant5 = ret;
+  switch (variant5.tag) {
+    case 'ok': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg2 + 0, 0, true);
+      if (!(e instanceof OutputStream)) {
+        throw new Error('Resource error: Not a valid "OutputStream" resource.');
+      }
+      var handle3 = e[symbolRscHandle];
+      
+      if (!handle3) {
+        const rep = e[symbolRscRep] || ++captureCnt2;
+        captureTable2.set(rep, e);
+        handle3 = rscTableCreateOwn(handleTable2, rep);
+      }
+      dataView(memory0).setInt32(arg2 + 4, handle3, true);
+      break;
+    }
+    case 'err': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg2 + 0, 1, true);
+      var val4 = e;
+      let enum4;
+      switch (val4) {
+        case 'access': {
+          enum4 = 0;
+          break;
+        }
+        case 'would-block': {
+          enum4 = 1;
+          break;
+        }
+        case 'already': {
+          enum4 = 2;
+          break;
+        }
+        case 'bad-descriptor': {
+          enum4 = 3;
+          break;
+        }
+        case 'busy': {
+          enum4 = 4;
+          break;
+        }
+        case 'deadlock': {
+          enum4 = 5;
+          break;
+        }
+        case 'quota': {
+          enum4 = 6;
+          break;
+        }
+        case 'exist': {
+          enum4 = 7;
+          break;
+        }
+        case 'file-too-large': {
+          enum4 = 8;
+          break;
+        }
+        case 'illegal-byte-sequence': {
+          enum4 = 9;
+          break;
+        }
+        case 'in-progress': {
+          enum4 = 10;
+          break;
+        }
+        case 'interrupted': {
+          enum4 = 11;
+          break;
+        }
+        case 'invalid': {
+          enum4 = 12;
+          break;
+        }
+        case 'io': {
+          enum4 = 13;
+          break;
+        }
+        case 'is-directory': {
+          enum4 = 14;
+          break;
+        }
+        case 'loop': {
+          enum4 = 15;
+          break;
+        }
+        case 'too-many-links': {
+          enum4 = 16;
+          break;
+        }
+        case 'message-size': {
+          enum4 = 17;
+          break;
+        }
+        case 'name-too-long': {
+          enum4 = 18;
+          break;
+        }
+        case 'no-device': {
+          enum4 = 19;
+          break;
+        }
+        case 'no-entry': {
+          enum4 = 20;
+          break;
+        }
+        case 'no-lock': {
+          enum4 = 21;
+          break;
+        }
+        case 'insufficient-memory': {
+          enum4 = 22;
+          break;
+        }
+        case 'insufficient-space': {
+          enum4 = 23;
+          break;
+        }
+        case 'not-directory': {
+          enum4 = 24;
+          break;
+        }
+        case 'not-empty': {
+          enum4 = 25;
+          break;
+        }
+        case 'not-recoverable': {
+          enum4 = 26;
+          break;
+        }
+        case 'unsupported': {
+          enum4 = 27;
+          break;
+        }
+        case 'no-tty': {
+          enum4 = 28;
+          break;
+        }
+        case 'no-such-device': {
+          enum4 = 29;
+          break;
+        }
+        case 'overflow': {
+          enum4 = 30;
+          break;
+        }
+        case 'not-permitted': {
+          enum4 = 31;
+          break;
+        }
+        case 'pipe': {
+          enum4 = 32;
+          break;
+        }
+        case 'read-only': {
+          enum4 = 33;
+          break;
+        }
+        case 'invalid-seek': {
+          enum4 = 34;
+          break;
+        }
+        case 'text-file-busy': {
+          enum4 = 35;
+          break;
+        }
+        case 'cross-device': {
+          enum4 = 36;
+          break;
+        }
+        default: {
+          if ((e) instanceof Error) {
+            console.error(e);
+          }
+          
+          throw new TypeError(`"${val4}" is not one of the cases of error-code`);
+        }
+      }
+      dataView(memory0).setInt8(arg2 + 4, enum4, true);
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline18(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable4[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable4.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(Descriptor.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.appendViaStream()};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant5 = ret;
+  switch (variant5.tag) {
+    case 'ok': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 0, true);
+      if (!(e instanceof OutputStream)) {
+        throw new Error('Resource error: Not a valid "OutputStream" resource.');
+      }
+      var handle3 = e[symbolRscHandle];
+      
+      if (!handle3) {
+        const rep = e[symbolRscRep] || ++captureCnt2;
+        captureTable2.set(rep, e);
+        handle3 = rscTableCreateOwn(handleTable2, rep);
+      }
+      dataView(memory0).setInt32(arg1 + 4, handle3, true);
+      break;
+    }
+    case 'err': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 1, true);
+      var val4 = e;
+      let enum4;
+      switch (val4) {
+        case 'access': {
+          enum4 = 0;
+          break;
+        }
+        case 'would-block': {
+          enum4 = 1;
+          break;
+        }
+        case 'already': {
+          enum4 = 2;
+          break;
+        }
+        case 'bad-descriptor': {
+          enum4 = 3;
+          break;
+        }
+        case 'busy': {
+          enum4 = 4;
+          break;
+        }
+        case 'deadlock': {
+          enum4 = 5;
+          break;
+        }
+        case 'quota': {
+          enum4 = 6;
+          break;
+        }
+        case 'exist': {
+          enum4 = 7;
+          break;
+        }
+        case 'file-too-large': {
+          enum4 = 8;
+          break;
+        }
+        case 'illegal-byte-sequence': {
+          enum4 = 9;
+          break;
+        }
+        case 'in-progress': {
+          enum4 = 10;
+          break;
+        }
+        case 'interrupted': {
+          enum4 = 11;
+          break;
+        }
+        case 'invalid': {
+          enum4 = 12;
+          break;
+        }
+        case 'io': {
+          enum4 = 13;
+          break;
+        }
+        case 'is-directory': {
+          enum4 = 14;
+          break;
+        }
+        case 'loop': {
+          enum4 = 15;
+          break;
+        }
+        case 'too-many-links': {
+          enum4 = 16;
+          break;
+        }
+        case 'message-size': {
+          enum4 = 17;
+          break;
+        }
+        case 'name-too-long': {
+          enum4 = 18;
+          break;
+        }
+        case 'no-device': {
+          enum4 = 19;
+          break;
+        }
+        case 'no-entry': {
+          enum4 = 20;
+          break;
+        }
+        case 'no-lock': {
+          enum4 = 21;
+          break;
+        }
+        case 'insufficient-memory': {
+          enum4 = 22;
+          break;
+        }
+        case 'insufficient-space': {
+          enum4 = 23;
+          break;
+        }
+        case 'not-directory': {
+          enum4 = 24;
+          break;
+        }
+        case 'not-empty': {
+          enum4 = 25;
+          break;
+        }
+        case 'not-recoverable': {
+          enum4 = 26;
+          break;
+        }
+        case 'unsupported': {
+          enum4 = 27;
+          break;
+        }
+        case 'no-tty': {
+          enum4 = 28;
+          break;
+        }
+        case 'no-such-device': {
+          enum4 = 29;
+          break;
+        }
+        case 'overflow': {
+          enum4 = 30;
+          break;
+        }
+        case 'not-permitted': {
+          enum4 = 31;
+          break;
+        }
+        case 'pipe': {
+          enum4 = 32;
+          break;
+        }
+        case 'read-only': {
+          enum4 = 33;
+          break;
+        }
+        case 'invalid-seek': {
+          enum4 = 34;
+          break;
+        }
+        case 'text-file-busy': {
+          enum4 = 35;
+          break;
+        }
+        case 'cross-device': {
+          enum4 = 36;
+          break;
+        }
+        default: {
+          if ((e) instanceof Error) {
+            console.error(e);
+          }
+          
+          throw new TypeError(`"${val4}" is not one of the cases of error-code`);
+        }
+      }
+      dataView(memory0).setInt8(arg1 + 4, enum4, true);
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline19(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable4[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable4.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(Descriptor.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.getType()};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant5 = ret;
+  switch (variant5.tag) {
+    case 'ok': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 0, true);
+      var val3 = e;
+      let enum3;
+      switch (val3) {
+        case 'unknown': {
+          enum3 = 0;
+          break;
+        }
+        case 'block-device': {
+          enum3 = 1;
+          break;
+        }
+        case 'character-device': {
+          enum3 = 2;
+          break;
+        }
+        case 'directory': {
+          enum3 = 3;
+          break;
+        }
+        case 'fifo': {
+          enum3 = 4;
+          break;
+        }
+        case 'symbolic-link': {
+          enum3 = 5;
+          break;
+        }
+        case 'regular-file': {
+          enum3 = 6;
+          break;
+        }
+        case 'socket': {
+          enum3 = 7;
+          break;
+        }
+        default: {
+          if ((e) instanceof Error) {
+            console.error(e);
+          }
+          
+          throw new TypeError(`"${val3}" is not one of the cases of descriptor-type`);
+        }
+      }
+      dataView(memory0).setInt8(arg1 + 1, enum3, true);
+      break;
+    }
+    case 'err': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 1, true);
+      var val4 = e;
+      let enum4;
+      switch (val4) {
+        case 'access': {
+          enum4 = 0;
+          break;
+        }
+        case 'would-block': {
+          enum4 = 1;
+          break;
+        }
+        case 'already': {
+          enum4 = 2;
+          break;
+        }
+        case 'bad-descriptor': {
+          enum4 = 3;
+          break;
+        }
+        case 'busy': {
+          enum4 = 4;
+          break;
+        }
+        case 'deadlock': {
+          enum4 = 5;
+          break;
+        }
+        case 'quota': {
+          enum4 = 6;
+          break;
+        }
+        case 'exist': {
+          enum4 = 7;
+          break;
+        }
+        case 'file-too-large': {
+          enum4 = 8;
+          break;
+        }
+        case 'illegal-byte-sequence': {
+          enum4 = 9;
+          break;
+        }
+        case 'in-progress': {
+          enum4 = 10;
+          break;
+        }
+        case 'interrupted': {
+          enum4 = 11;
+          break;
+        }
+        case 'invalid': {
+          enum4 = 12;
+          break;
+        }
+        case 'io': {
+          enum4 = 13;
+          break;
+        }
+        case 'is-directory': {
+          enum4 = 14;
+          break;
+        }
+        case 'loop': {
+          enum4 = 15;
+          break;
+        }
+        case 'too-many-links': {
+          enum4 = 16;
+          break;
+        }
+        case 'message-size': {
+          enum4 = 17;
+          break;
+        }
+        case 'name-too-long': {
+          enum4 = 18;
+          break;
+        }
+        case 'no-device': {
+          enum4 = 19;
+          break;
+        }
+        case 'no-entry': {
+          enum4 = 20;
+          break;
+        }
+        case 'no-lock': {
+          enum4 = 21;
+          break;
+        }
+        case 'insufficient-memory': {
+          enum4 = 22;
+          break;
+        }
+        case 'insufficient-space': {
+          enum4 = 23;
+          break;
+        }
+        case 'not-directory': {
+          enum4 = 24;
+          break;
+        }
+        case 'not-empty': {
+          enum4 = 25;
+          break;
+        }
+        case 'not-recoverable': {
+          enum4 = 26;
+          break;
+        }
+        case 'unsupported': {
+          enum4 = 27;
+          break;
+        }
+        case 'no-tty': {
+          enum4 = 28;
+          break;
+        }
+        case 'no-such-device': {
+          enum4 = 29;
+          break;
+        }
+        case 'overflow': {
+          enum4 = 30;
+          break;
+        }
+        case 'not-permitted': {
+          enum4 = 31;
+          break;
+        }
+        case 'pipe': {
+          enum4 = 32;
+          break;
+        }
+        case 'read-only': {
+          enum4 = 33;
+          break;
+        }
+        case 'invalid-seek': {
+          enum4 = 34;
+          break;
+        }
+        case 'text-file-busy': {
+          enum4 = 35;
+          break;
+        }
+        case 'cross-device': {
+          enum4 = 36;
+          break;
+        }
+        default: {
+          if ((e) instanceof Error) {
+            console.error(e);
+          }
+          
+          throw new TypeError(`"${val4}" is not one of the cases of error-code`);
+        }
+      }
+      dataView(memory0).setInt8(arg1 + 1, enum4, true);
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline20(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable4[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable4.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(Descriptor.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.stat()};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant12 = ret;
+  switch (variant12.tag) {
+    case 'ok': {
+      const e = variant12.val;
+      dataView(memory0).setInt8(arg1 + 0, 0, true);
+      var {type: v3_0, linkCount: v3_1, size: v3_2, dataAccessTimestamp: v3_3, dataModificationTimestamp: v3_4, statusChangeTimestamp: v3_5 } = e;
+      var val4 = v3_0;
+      let enum4;
+      switch (val4) {
+        case 'unknown': {
+          enum4 = 0;
+          break;
+        }
+        case 'block-device': {
+          enum4 = 1;
+          break;
+        }
+        case 'character-device': {
+          enum4 = 2;
+          break;
+        }
+        case 'directory': {
+          enum4 = 3;
+          break;
+        }
+        case 'fifo': {
+          enum4 = 4;
+          break;
+        }
+        case 'symbolic-link': {
+          enum4 = 5;
+          break;
+        }
+        case 'regular-file': {
+          enum4 = 6;
+          break;
+        }
+        case 'socket': {
+          enum4 = 7;
+          break;
+        }
+        default: {
+          if ((v3_0) instanceof Error) {
+            console.error(v3_0);
+          }
+          
+          throw new TypeError(`"${val4}" is not one of the cases of descriptor-type`);
+        }
+      }
+      dataView(memory0).setInt8(arg1 + 8, enum4, true);
+      dataView(memory0).setBigInt64(arg1 + 16, toUint64(v3_1), true);
+      dataView(memory0).setBigInt64(arg1 + 24, toUint64(v3_2), true);
+      var variant6 = v3_3;
+      if (variant6 === null || variant6=== undefined) {
+        dataView(memory0).setInt8(arg1 + 32, 0, true);
+      } else {
+        const e = variant6;
+        dataView(memory0).setInt8(arg1 + 32, 1, true);
+        var {seconds: v5_0, nanoseconds: v5_1 } = e;
+        dataView(memory0).setBigInt64(arg1 + 40, toUint64(v5_0), true);
+        dataView(memory0).setInt32(arg1 + 48, toUint32(v5_1), true);
+      }
+      var variant8 = v3_4;
+      if (variant8 === null || variant8=== undefined) {
+        dataView(memory0).setInt8(arg1 + 56, 0, true);
+      } else {
+        const e = variant8;
+        dataView(memory0).setInt8(arg1 + 56, 1, true);
+        var {seconds: v7_0, nanoseconds: v7_1 } = e;
+        dataView(memory0).setBigInt64(arg1 + 64, toUint64(v7_0), true);
+        dataView(memory0).setInt32(arg1 + 72, toUint32(v7_1), true);
+      }
+      var variant10 = v3_5;
+      if (variant10 === null || variant10=== undefined) {
+        dataView(memory0).setInt8(arg1 + 80, 0, true);
+      } else {
+        const e = variant10;
+        dataView(memory0).setInt8(arg1 + 80, 1, true);
+        var {seconds: v9_0, nanoseconds: v9_1 } = e;
+        dataView(memory0).setBigInt64(arg1 + 88, toUint64(v9_0), true);
+        dataView(memory0).setInt32(arg1 + 96, toUint32(v9_1), true);
+      }
+      break;
+    }
+    case 'err': {
+      const e = variant12.val;
+      dataView(memory0).setInt8(arg1 + 0, 1, true);
+      var val11 = e;
+      let enum11;
+      switch (val11) {
+        case 'access': {
+          enum11 = 0;
+          break;
+        }
+        case 'would-block': {
+          enum11 = 1;
+          break;
+        }
+        case 'already': {
+          enum11 = 2;
+          break;
+        }
+        case 'bad-descriptor': {
+          enum11 = 3;
+          break;
+        }
+        case 'busy': {
+          enum11 = 4;
+          break;
+        }
+        case 'deadlock': {
+          enum11 = 5;
+          break;
+        }
+        case 'quota': {
+          enum11 = 6;
+          break;
+        }
+        case 'exist': {
+          enum11 = 7;
+          break;
+        }
+        case 'file-too-large': {
+          enum11 = 8;
+          break;
+        }
+        case 'illegal-byte-sequence': {
+          enum11 = 9;
+          break;
+        }
+        case 'in-progress': {
+          enum11 = 10;
+          break;
+        }
+        case 'interrupted': {
+          enum11 = 11;
+          break;
+        }
+        case 'invalid': {
+          enum11 = 12;
+          break;
+        }
+        case 'io': {
+          enum11 = 13;
+          break;
+        }
+        case 'is-directory': {
+          enum11 = 14;
+          break;
+        }
+        case 'loop': {
+          enum11 = 15;
+          break;
+        }
+        case 'too-many-links': {
+          enum11 = 16;
+          break;
+        }
+        case 'message-size': {
+          enum11 = 17;
+          break;
+        }
+        case 'name-too-long': {
+          enum11 = 18;
+          break;
+        }
+        case 'no-device': {
+          enum11 = 19;
+          break;
+        }
+        case 'no-entry': {
+          enum11 = 20;
+          break;
+        }
+        case 'no-lock': {
+          enum11 = 21;
+          break;
+        }
+        case 'insufficient-memory': {
+          enum11 = 22;
+          break;
+        }
+        case 'insufficient-space': {
+          enum11 = 23;
+          break;
+        }
+        case 'not-directory': {
+          enum11 = 24;
+          break;
+        }
+        case 'not-empty': {
+          enum11 = 25;
+          break;
+        }
+        case 'not-recoverable': {
+          enum11 = 26;
+          break;
+        }
+        case 'unsupported': {
+          enum11 = 27;
+          break;
+        }
+        case 'no-tty': {
+          enum11 = 28;
+          break;
+        }
+        case 'no-such-device': {
+          enum11 = 29;
+          break;
+        }
+        case 'overflow': {
+          enum11 = 30;
+          break;
+        }
+        case 'not-permitted': {
+          enum11 = 31;
+          break;
+        }
+        case 'pipe': {
+          enum11 = 32;
+          break;
+        }
+        case 'read-only': {
+          enum11 = 33;
+          break;
+        }
+        case 'invalid-seek': {
+          enum11 = 34;
+          break;
+        }
+        case 'text-file-busy': {
+          enum11 = 35;
+          break;
+        }
+        case 'cross-device': {
+          enum11 = 36;
+          break;
+        }
+        default: {
+          if ((e) instanceof Error) {
+            console.error(e);
+          }
+          
+          throw new TypeError(`"${val11}" is not one of the cases of error-code`);
+        }
+      }
+      dataView(memory0).setInt8(arg1 + 8, enum11, true);
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+const handleTable1 = [T_FLAG, 0];
+const captureTable1= new Map();
+let captureCnt1 = 0;
+handleTables[1] = handleTable1;
+
+function trampoline21(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable1[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable1.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(Error$1.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  const ret = filesystemErrorCode(rsc0);
+  rsc0[symbolRscHandle] = null;
+  var variant4 = ret;
+  if (variant4 === null || variant4=== undefined) {
+    dataView(memory0).setInt8(arg1 + 0, 0, true);
+  } else {
+    const e = variant4;
+    dataView(memory0).setInt8(arg1 + 0, 1, true);
+    var val3 = e;
+    let enum3;
+    switch (val3) {
+      case 'access': {
+        enum3 = 0;
+        break;
+      }
+      case 'would-block': {
+        enum3 = 1;
+        break;
+      }
+      case 'already': {
+        enum3 = 2;
+        break;
+      }
+      case 'bad-descriptor': {
+        enum3 = 3;
+        break;
+      }
+      case 'busy': {
+        enum3 = 4;
+        break;
+      }
+      case 'deadlock': {
+        enum3 = 5;
+        break;
+      }
+      case 'quota': {
+        enum3 = 6;
+        break;
+      }
+      case 'exist': {
+        enum3 = 7;
+        break;
+      }
+      case 'file-too-large': {
+        enum3 = 8;
+        break;
+      }
+      case 'illegal-byte-sequence': {
+        enum3 = 9;
+        break;
+      }
+      case 'in-progress': {
+        enum3 = 10;
+        break;
+      }
+      case 'interrupted': {
+        enum3 = 11;
+        break;
+      }
+      case 'invalid': {
+        enum3 = 12;
+        break;
+      }
+      case 'io': {
+        enum3 = 13;
+        break;
+      }
+      case 'is-directory': {
+        enum3 = 14;
+        break;
+      }
+      case 'loop': {
+        enum3 = 15;
+        break;
+      }
+      case 'too-many-links': {
+        enum3 = 16;
+        break;
+      }
+      case 'message-size': {
+        enum3 = 17;
+        break;
+      }
+      case 'name-too-long': {
+        enum3 = 18;
+        break;
+      }
+      case 'no-device': {
+        enum3 = 19;
+        break;
+      }
+      case 'no-entry': {
+        enum3 = 20;
+        break;
+      }
+      case 'no-lock': {
+        enum3 = 21;
+        break;
+      }
+      case 'insufficient-memory': {
+        enum3 = 22;
+        break;
+      }
+      case 'insufficient-space': {
+        enum3 = 23;
+        break;
+      }
+      case 'not-directory': {
+        enum3 = 24;
+        break;
+      }
+      case 'not-empty': {
+        enum3 = 25;
+        break;
+      }
+      case 'not-recoverable': {
+        enum3 = 26;
+        break;
+      }
+      case 'unsupported': {
+        enum3 = 27;
+        break;
+      }
+      case 'no-tty': {
+        enum3 = 28;
+        break;
+      }
+      case 'no-such-device': {
+        enum3 = 29;
+        break;
+      }
+      case 'overflow': {
+        enum3 = 30;
+        break;
+      }
+      case 'not-permitted': {
+        enum3 = 31;
+        break;
+      }
+      case 'pipe': {
+        enum3 = 32;
+        break;
+      }
+      case 'read-only': {
+        enum3 = 33;
+        break;
+      }
+      case 'invalid-seek': {
+        enum3 = 34;
+        break;
+      }
+      case 'text-file-busy': {
+        enum3 = 35;
+        break;
+      }
+      case 'cross-device': {
+        enum3 = 36;
+        break;
+      }
+      default: {
+        if ((e) instanceof Error) {
+          console.error(e);
+        }
+        
+        throw new TypeError(`"${val3}" is not one of the cases of error-code`);
+      }
+    }
+    dataView(memory0).setInt8(arg1 + 1, enum3, true);
+  }
+}
+
+function trampoline22(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable2[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable2.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(OutputStream.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.checkWrite()};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant5 = ret;
+  switch (variant5.tag) {
+    case 'ok': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 0, true);
+      dataView(memory0).setBigInt64(arg1 + 8, toUint64(e), true);
+      break;
+    }
+    case 'err': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 1, true);
+      var variant4 = e;
+      switch (variant4.tag) {
+        case 'last-operation-failed': {
+          const e = variant4.val;
+          dataView(memory0).setInt8(arg1 + 8, 0, true);
+          if (!(e instanceof Error$1)) {
+            throw new Error('Resource error: Not a valid "Error" resource.');
+          }
+          var handle3 = e[symbolRscHandle];
+          
+          if (!handle3) {
+            const rep = e[symbolRscRep] || ++captureCnt1;
+            captureTable1.set(rep, e);
+            handle3 = rscTableCreateOwn(handleTable1, rep);
+          }
+          dataView(memory0).setInt32(arg1 + 12, handle3, true);
+          break;
+        }
+        case 'closed': {
+          dataView(memory0).setInt8(arg1 + 8, 1, true);
+          break;
+        }
+        default: {
+          throw new TypeError(`invalid variant tag value \`${JSON.stringify(variant4.tag)}\` (received \`${variant4}\`) specified for \`StreamError\``);
+        }
+      }
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline23(arg0, arg1, arg2, arg3) {
+  var handle1 = arg0;
+  var rep2 = handleTable2[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable2.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(OutputStream.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  var ptr3 = arg1;
+  var len3 = arg2;
+  var result3 = new Uint8Array(memory0.buffer.slice(ptr3, ptr3 + len3 * 1));
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.write(result3)};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant6 = ret;
+  switch (variant6.tag) {
+    case 'ok': {
+      const e = variant6.val;
+      dataView(memory0).setInt8(arg3 + 0, 0, true);
+      break;
+    }
+    case 'err': {
+      const e = variant6.val;
+      dataView(memory0).setInt8(arg3 + 0, 1, true);
+      var variant5 = e;
+      switch (variant5.tag) {
+        case 'last-operation-failed': {
+          const e = variant5.val;
+          dataView(memory0).setInt8(arg3 + 4, 0, true);
+          if (!(e instanceof Error$1)) {
+            throw new Error('Resource error: Not a valid "Error" resource.');
+          }
+          var handle4 = e[symbolRscHandle];
+          
+          if (!handle4) {
+            const rep = e[symbolRscRep] || ++captureCnt1;
+            captureTable1.set(rep, e);
+            handle4 = rscTableCreateOwn(handleTable1, rep);
+          }
+          dataView(memory0).setInt32(arg3 + 8, handle4, true);
+          break;
+        }
+        case 'closed': {
+          dataView(memory0).setInt8(arg3 + 4, 1, true);
+          break;
+        }
+        default: {
+          throw new TypeError(`invalid variant tag value \`${JSON.stringify(variant5.tag)}\` (received \`${variant5}\`) specified for \`StreamError\``);
+        }
+      }
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline24(arg0, arg1, arg2, arg3) {
+  var handle1 = arg0;
+  var rep2 = handleTable2[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable2.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(OutputStream.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  var ptr3 = arg1;
+  var len3 = arg2;
+  var result3 = new Uint8Array(memory0.buffer.slice(ptr3, ptr3 + len3 * 1));
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.blockingWriteAndFlush(result3)};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant6 = ret;
+  switch (variant6.tag) {
+    case 'ok': {
+      const e = variant6.val;
+      dataView(memory0).setInt8(arg3 + 0, 0, true);
+      break;
+    }
+    case 'err': {
+      const e = variant6.val;
+      dataView(memory0).setInt8(arg3 + 0, 1, true);
+      var variant5 = e;
+      switch (variant5.tag) {
+        case 'last-operation-failed': {
+          const e = variant5.val;
+          dataView(memory0).setInt8(arg3 + 4, 0, true);
+          if (!(e instanceof Error$1)) {
+            throw new Error('Resource error: Not a valid "Error" resource.');
+          }
+          var handle4 = e[symbolRscHandle];
+          
+          if (!handle4) {
+            const rep = e[symbolRscRep] || ++captureCnt1;
+            captureTable1.set(rep, e);
+            handle4 = rscTableCreateOwn(handleTable1, rep);
+          }
+          dataView(memory0).setInt32(arg3 + 8, handle4, true);
+          break;
+        }
+        case 'closed': {
+          dataView(memory0).setInt8(arg3 + 4, 1, true);
+          break;
+        }
+        default: {
+          throw new TypeError(`invalid variant tag value \`${JSON.stringify(variant5.tag)}\` (received \`${variant5}\`) specified for \`StreamError\``);
+        }
+      }
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline25(arg0, arg1) {
+  var handle1 = arg0;
+  var rep2 = handleTable2[(handle1 << 1) + 1] & ~T_FLAG;
+  var rsc0 = captureTable2.get(rep2);
+  if (!rsc0) {
+    rsc0 = Object.create(OutputStream.prototype);
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: handle1});
+    Object.defineProperty(rsc0, symbolRscRep, { writable: true, value: rep2});
+  }
+  let ret;
+  try {
+    ret = { tag: 'ok', val: rsc0.blockingFlush()};
+  } catch (e) {
+    ret = { tag: 'err', val: getErrorPayload(e) };
+  }
+  rsc0[symbolRscHandle] = null;
+  var variant5 = ret;
+  switch (variant5.tag) {
+    case 'ok': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 0, true);
+      break;
+    }
+    case 'err': {
+      const e = variant5.val;
+      dataView(memory0).setInt8(arg1 + 0, 1, true);
+      var variant4 = e;
+      switch (variant4.tag) {
+        case 'last-operation-failed': {
+          const e = variant4.val;
+          dataView(memory0).setInt8(arg1 + 4, 0, true);
+          if (!(e instanceof Error$1)) {
+            throw new Error('Resource error: Not a valid "Error" resource.');
+          }
+          var handle3 = e[symbolRscHandle];
+          
+          if (!handle3) {
+            const rep = e[symbolRscRep] || ++captureCnt1;
+            captureTable1.set(rep, e);
+            handle3 = rscTableCreateOwn(handleTable1, rep);
+          }
+          dataView(memory0).setInt32(arg1 + 8, handle3, true);
+          break;
+        }
+        case 'closed': {
+          dataView(memory0).setInt8(arg1 + 4, 1, true);
+          break;
+        }
+        default: {
+          throw new TypeError(`invalid variant tag value \`${JSON.stringify(variant4.tag)}\` (received \`${variant4}\`) specified for \`StreamError\``);
+        }
+      }
+      break;
+    }
+    default: {
+      throw new TypeError('invalid variant specified for result');
+    }
+  }
+}
+
+function trampoline26(arg0) {
+  const ret = getEnvironment();
+  var vec3 = ret;
+  var len3 = vec3.length;
+  var result3 = realloc0(0, 0, 4, len3 * 16);
+  for (let i = 0; i < vec3.length; i++) {
+    const e = vec3[i];
+    const base = result3 + i * 16;var [tuple0_0, tuple0_1] = e;
+    var ptr1 = utf8Encode(tuple0_0, realloc0, memory0);
+    var len1 = utf8EncodedLen;
+    dataView(memory0).setInt32(base + 4, len1, true);
+    dataView(memory0).setInt32(base + 0, ptr1, true);
+    var ptr2 = utf8Encode(tuple0_1, realloc0, memory0);
+    var len2 = utf8EncodedLen;
+    dataView(memory0).setInt32(base + 12, len2, true);
+    dataView(memory0).setInt32(base + 8, ptr2, true);
+  }
+  dataView(memory0).setInt32(arg0 + 4, len3, true);
+  dataView(memory0).setInt32(arg0 + 0, result3, true);
+}
+let exports3;
+const handleTable5 = [T_FLAG, 0];
+const finalizationRegistry5= new FinalizationRegistry((handle) => {
+  const { rep } = rscTableRemove(handleTable5, handle);
+  exports0['15'](rep);
+});
+
+handleTables[5] = handleTable5;
+const trampoline0 = rscTableCreateOwn.bind(null, handleTable5);
+function trampoline1(handle) {
+  const handleEntry = rscTableRemove(handleTable0, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  const rsc = captureTable0.get(handleEntry.rep);
+  if (rsc) {
+    if (rsc[symbolDispose]) rsc[symbolDispose]();
+    captureTable0.delete(handleEntry.rep);
+  } else if (R[symbolCabiDispose]) {
+    R[symbolCabiDispose](handleEntry.rep);
+  }
+}
+function trampoline7(handle) {
+  const handleEntry = rscTableRemove(handleTable5, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  exports0['15'](handleEntry.rep);
+}
+function trampoline8(handle) {
+  const handleEntry = rscTableRemove(handleTable1, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  const rsc = captureTable1.get(handleEntry.rep);
+  if (rsc) {
+    if (rsc[symbolDispose]) rsc[symbolDispose]();
+    captureTable1.delete(handleEntry.rep);
+  } else if (Error$1[symbolCabiDispose]) {
+    Error$1[symbolCabiDispose](handleEntry.rep);
+  }
+}
+function trampoline9(handle) {
+  const handleEntry = rscTableRemove(handleTable3, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  const rsc = captureTable3.get(handleEntry.rep);
+  if (rsc) {
+    if (rsc[symbolDispose]) rsc[symbolDispose]();
+    captureTable3.delete(handleEntry.rep);
+  } else if (InputStream[symbolCabiDispose]) {
+    InputStream[symbolCabiDispose](handleEntry.rep);
+  }
+}
+function trampoline10(handle) {
+  const handleEntry = rscTableRemove(handleTable2, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  const rsc = captureTable2.get(handleEntry.rep);
+  if (rsc) {
+    if (rsc[symbolDispose]) rsc[symbolDispose]();
+    captureTable2.delete(handleEntry.rep);
+  } else if (OutputStream[symbolCabiDispose]) {
+    OutputStream[symbolCabiDispose](handleEntry.rep);
+  }
+}
+function trampoline11(handle) {
+  const handleEntry = rscTableRemove(handleTable4, handle);
+  if (!handleEntry.own) throw new Error('Internal error: Unexpected borrow handle');
+  const rsc = captureTable4.get(handleEntry.rep);
+  if (rsc) {
+    if (rsc[symbolDispose]) rsc[symbolDispose]();
+    captureTable4.delete(handleEntry.rep);
+  } else if (Descriptor[symbolCabiDispose]) {
+    Descriptor[symbolCabiDispose](handleEntry.rep);
+  }
+}
+
+class R$1{
+  constructor(arg0) {
+    const ret = exports1['foo:foo/resources#[constructor]r'](toUint32(arg0));
+    var handle1 = ret;
+    var rsc0 = new.target === R$1 ? this : Object.create(R$1.prototype);
+    var rep2 = handleTable5[(handle1 << 1) + 1] & ~T_FLAG;
+    Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: rep2});
+    finalizationRegistry5.register(rsc0, handle1, rsc0);
+    Object.defineProperty(rsc0, symbolDispose, { writable: true, value: function () {
+      finalizationRegistry5.unregister(rsc0);
+      rscTableRemove(handleTable5, handle1);
+      rsc0[symbolDispose] = emptyFunc;
+      rsc0[symbolRscHandle] = null;
+      exports0['15'](rep2);
+    } });
+    rscTableRemove(handleTable5, handle1);
+    
+    return rsc0;
+  }
+}
+
+R$1.prototype.add = function add(arg1) {
+  var handle0 = this[symbolRscHandle];
+  if (!handle0) {
+    throw new Error('Resource error: Not a valid "R" resource.');
+  }
+  
+  exports1['foo:foo/resources#[method]r.add'](handle0, toUint32(arg1));
+};
+
+function create$1() {
+  const ret = exports1['foo:foo/resources#create']();
+  var handle1 = ret;
+  var rsc0 = new.target === R$1 ? this : Object.create(R$1.prototype);
+  var rep2 = handleTable5[(handle1 << 1) + 1] & ~T_FLAG;
+  Object.defineProperty(rsc0, symbolRscHandle, { writable: true, value: rep2});
+  finalizationRegistry5.register(rsc0, handle1, rsc0);
+  Object.defineProperty(rsc0, symbolDispose, { writable: true, value: function () {
+    finalizationRegistry5.unregister(rsc0);
+    rscTableRemove(handleTable5, handle1);
+    rsc0[symbolDispose] = emptyFunc;
+    rsc0[symbolRscHandle] = null;
+    exports0['15'](rep2);
+  } });
+  rscTableRemove(handleTable5, handle1);
+  
+  return rsc0;
+}
+
+function borrows$1(arg0) {
+  var handle0 = arg0[symbolRscHandle];
+  if (!handle0) {
+    throw new Error('Resource error: Not a valid "R" resource.');
+  }
+  
+  exports1['foo:foo/resources#borrows'](handle0);
+}
+
+function consume$1(arg0) {
+  var handle0 = arg0[symbolRscHandle];
+  if (!handle0) {
+    throw new Error('Resource error: Not a valid "R" resource.');
+  }
+  
+  finalizationRegistry5.unregister(arg0);
+  arg0[symbolDispose] = emptyFunc;
+  arg0[symbolRscHandle] = null;
+  exports1['foo:foo/resources#consume'](handle0);
+}
+
+function unimplemented() {todo();}
+function fd_stat_error() { return -1; }
+
+const $init = (async() => {
+  const module0 = fetchCompile(new URL('./component.core.wasm', import.meta.url));
+  const module1 = fetchCompile(new URL('./component.core2.wasm', import.meta.url));
+  const module2 = base64Compile('AGFzbQEAAAABKQdgAX8AYAN/fn8AYAJ/fwBgBH9/f38AYAR/f39/AX9gAn9/AX9gAX8AAxEQAAECAgICAgMDAgAEBQUGBgQFAXABEBAHUhEBMAAAATEAAQEyAAIBMwADATQABAE1AAUBNgAGATcABwE4AAgBOQAJAjEwAAoCMTEACwIxMgAMAjEzAA0CMTQADgIxNQAPCCRpbXBvcnRzAQAKxwEQCQAgAEEAEQAACw0AIAAgASACQQERAQALCwAgACABQQIRAgALCwAgACABQQMRAgALCwAgACABQQQRAgALCwAgACABQQURAgALCwAgACABQQYRAgALDwAgACABIAIgA0EHEQMACw8AIAAgASACIANBCBEDAAsLACAAIAFBCRECAAsJACAAQQoRAAALDwAgACABIAIgA0ELEQQACwsAIAAgAUEMEQUACwsAIAAgAUENEQUACwkAIABBDhEGAAsJACAAQQ8RBgALAC8JcHJvZHVjZXJzAQxwcm9jZXNzZWQtYnkBDXdpdC1jb21wb25lbnQHMC4yMDAuMAC6BwRuYW1lABMSd2l0LWNvbXBvbmVudDpzaGltAZ0HEAA3aW5kaXJlY3Qtd2FzaTpmaWxlc3lzdGVtL3ByZW9wZW5zQDAuMi4wLWdldC1kaXJlY3RvcmllcwFIaW5kaXJlY3Qtd2FzaTpmaWxlc3lzdGVtL3R5cGVzQDAuMi4wLVttZXRob2RdZGVzY3JpcHRvci53cml0ZS12aWEtc3RyZWFtAklpbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtW21ldGhvZF1kZXNjcmlwdG9yLmFwcGVuZC12aWEtc3RyZWFtA0BpbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtW21ldGhvZF1kZXNjcmlwdG9yLmdldC10eXBlBDxpbmRpcmVjdC13YXNpOmZpbGVzeXN0ZW0vdHlwZXNAMC4yLjAtW21ldGhvZF1kZXNjcmlwdG9yLnN0YXQFOmluZGlyZWN0LXdhc2k6ZmlsZXN5c3RlbS90eXBlc0AwLjIuMC1maWxlc3lzdGVtLWVycm9yLWNvZGUGQGluZGlyZWN0LXdhc2k6aW8vc3RyZWFtc0AwLjIuMC1bbWV0aG9kXW91dHB1dC1zdHJlYW0uY2hlY2std3JpdGUHOmluZGlyZWN0LXdhc2k6aW8vc3RyZWFtc0AwLjIuMC1bbWV0aG9kXW91dHB1dC1zdHJlYW0ud3JpdGUITWluZGlyZWN0LXdhc2k6aW8vc3RyZWFtc0AwLjIuMC1bbWV0aG9kXW91dHB1dC1zdHJlYW0uYmxvY2tpbmctd3JpdGUtYW5kLWZsdXNoCUNpbmRpcmVjdC13YXNpOmlvL3N0cmVhbXNAMC4yLjAtW21ldGhvZF1vdXRwdXQtc3RyZWFtLmJsb2NraW5nLWZsdXNoCjNpbmRpcmVjdC13YXNpOmNsaS9lbnZpcm9ubWVudEAwLjIuMC1nZXQtZW52aXJvbm1lbnQLJWFkYXB0LXdhc2lfc25hcHNob3RfcHJldmlldzEtZmRfd3JpdGUMKGFkYXB0LXdhc2lfc25hcHNob3RfcHJldmlldzEtZW52aXJvbl9nZXQNLmFkYXB0LXdhc2lfc25hcHNob3RfcHJldmlldzEtZW52aXJvbl9zaXplc19nZXQOJmFkYXB0LXdhc2lfc25hcHNob3RfcHJldmlldzEtcHJvY19leGl0DyBkdG9yLVtleHBvcnRdZm9vOmZvby9yZXNvdXJjZXMtcg');
+  const module3 = base64Compile('AGFzbQEAAAABKQdgAX8AYAN/fn8AYAJ/fwBgBH9/f38AYAR/f39/AX9gAn9/AX9gAX8AAmYRAAEwAAAAATEAAQABMgACAAEzAAIAATQAAgABNQACAAE2AAIAATcAAwABOAADAAE5AAIAAjEwAAAAAjExAAQAAjEyAAUAAjEzAAUAAjE0AAYAAjE1AAYACCRpbXBvcnRzAXABEBAJFgEAQQALEAABAgMEBQYHCAkKCwwNDg8ALwlwcm9kdWNlcnMBDHByb2Nlc3NlZC1ieQENd2l0LWNvbXBvbmVudAcwLjIwMC4wABwEbmFtZQAVFHdpdC1jb21wb25lbnQ6Zml4dXBz');
+  ({ exports: exports0 } = await instantiateCore(await module2));
+  ({ exports: exports1 } = await instantiateCore(await module0, {
+    '[export]foo:foo/resources': {
+      '[resource-drop]r': trampoline7,
+      '[resource-new]r': trampoline0,
+    },
+    'foo:foo/resources': {
+      '[constructor]r': trampoline2,
+      '[method]r.add': trampoline3,
+      '[resource-drop]r': trampoline1,
+      borrows: trampoline5,
+      consume: trampoline6,
+      create: trampoline4,
+    },
+    wasi_snapshot_preview1: {
+      environ_get: exports0['12'],
+      environ_sizes_get: exports0['13'],
+      fd_write: exports0['11'],
+      proc_exit: exports0['14'],
+      args_get: unimplemented,
+      args_sizes_get: unimplemented,
+      fd_close: unimplemented,
+      fd_fdstat_get: fd_stat_error,
+      fd_seek: unimplemented,
+    },
+  }));
+  ({ exports: exports2 } = await instantiateCore(await module1, {
+    __main_module__: {
+      cabi_realloc: exports1.cabi_realloc,
+    },
+    env: {
+      memory: exports1.memory,
+    },
+    'wasi:cli/environment@0.2.0': {
+      'get-environment': exports0['10'],
+    },
+    'wasi:cli/exit@0.2.0': {
+      exit: trampoline13,
+    },
+    'wasi:cli/stderr@0.2.0': {
+      'get-stderr': trampoline12,
+    },
+    'wasi:cli/stdin@0.2.0': {
+      'get-stdin': trampoline14,
+    },
+    'wasi:cli/stdout@0.2.0': {
+      'get-stdout': trampoline15,
+    },
+    'wasi:filesystem/preopens@0.2.0': {
+      'get-directories': exports0['0'],
+    },
+    'wasi:filesystem/types@0.2.0': {
+      '[method]descriptor.append-via-stream': exports0['2'],
+      '[method]descriptor.get-type': exports0['3'],
+      '[method]descriptor.stat': exports0['4'],
+      '[method]descriptor.write-via-stream': exports0['1'],
+      '[resource-drop]descriptor': trampoline11,
+      'filesystem-error-code': exports0['5'],
+    },
+    'wasi:io/error@0.2.0': {
+      '[resource-drop]error': trampoline8,
+    },
+    'wasi:io/streams@0.2.0': {
+      '[method]output-stream.blocking-flush': exports0['9'],
+      '[method]output-stream.blocking-write-and-flush': exports0['8'],
+      '[method]output-stream.check-write': exports0['6'],
+      '[method]output-stream.write': exports0['7'],
+      '[resource-drop]input-stream': trampoline9,
+      '[resource-drop]output-stream': trampoline10,
+    },
+  }));
+  memory0 = exports1.memory;
+  realloc0 = exports2.cabi_import_realloc;
+  ({ exports: exports3 } = await instantiateCore(await module3, {
+    '': {
+      $imports: exports0.$imports,
+      '0': trampoline16,
+      '1': trampoline17,
+      '10': trampoline26,
+      '11': exports2.fd_write,
+      '12': exports2.environ_get,
+      '13': exports2.environ_sizes_get,
+      '14': exports2.proc_exit,
+      '15': exports1['foo:foo/resources#[dtor]r'],
+      '2': trampoline18,
+      '3': trampoline19,
+      '4': trampoline20,
+      '5': trampoline21,
+      '6': trampoline22,
+      '7': trampoline23,
+      '8': trampoline24,
+      '9': trampoline25,
+    },
+  }));
+})();
+
+await $init;
+const resources = {
+  R: R$1,
+  borrows: borrows$1,
+  consume: consume$1,
+  create: create$1,
+  
+};
+
+export { resources, resources as 'foo:foo/resources',  }
\ No newline at end of file
diff --git a/crates/cpp/tests/native_resources/rust/html/index.html b/crates/cpp/tests/native_resources/rust/html/index.html
new file mode 100644
index 000000000..ff6ecda74
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/html/index.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="en-US">
+
+<head>
+    <meta charset="UTF-8" />
+    <title></title>
+    <link rel="stylesheet" href="" />
+</head>
+
+<body>
+    <div id="main">Look into console</div>
+    <script type="module" src="main.js"></script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/crates/cpp/tests/native_resources/rust/html/main.js b/crates/cpp/tests/native_resources/rust/html/main.js
new file mode 100644
index 000000000..9d7f4b003
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/html/main.js
@@ -0,0 +1,8 @@
+import {resources} from './component.js'
+
+var r = resources.create();
+r.add(12);
+resources.borrows(r);
+resources.consume(r);
+let s = new resources.R(42);
+s = null;
diff --git a/crates/cpp/tests/native_resources/rust/html/resources.js b/crates/cpp/tests/native_resources/rust/html/resources.js
new file mode 100644
index 000000000..a3be53446
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/html/resources.js
@@ -0,0 +1,21 @@
+
+export class R {
+    constructor(value) {
+        this.value = value;
+    }
+    add(b) {
+        this.value += b;
+    }
+}
+
+export function borrows(obj) {
+    console.log('borrows', obj.value);
+}
+
+export function consume(obj) {
+    console.log('consume', obj.value);
+}
+
+export function create() {
+    return new R(1);
+}
diff --git a/crates/cpp/tests/native_resources/rust/src/lib.rs b/crates/cpp/tests/native_resources/rust/src/lib.rs
new file mode 100644
index 000000000..754aa6986
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/src/lib.rs
@@ -0,0 +1,44 @@
+use the_world::exports::foo::foo::resources::{self, Guest, GuestR, RBorrow};
+use core::alloc::Layout;
+use std::sync::Mutex;
+
+mod the_world;
+
+#[derive(Debug)]
+struct MyResource(Mutex<u32>);
+
+impl GuestR for MyResource {
+    fn new(a: u32) -> Self {
+        MyResource(Mutex::new(a))
+    }
+
+    fn add(&self, b: u32) {
+        *self.0.lock().unwrap() += b;
+    }
+}
+
+struct MyWorld;
+
+impl Guest for MyWorld {
+    type R = MyResource;
+
+    fn create() -> resources::R { 
+        resources::R::new(MyResource::new(1))
+    }
+    fn borrows(o: RBorrow<'_>) {
+        println!("resource borrowed with {:?}", o.get::<MyResource>().0.lock().unwrap());
+    }
+    fn consume(o: resources::R) {
+        println!("resource consumed with {:?}", o.get::<MyResource>().0.lock().unwrap());
+
+        println!("exercise the other direction");
+        let obj = the_world::foo::foo::resources::create();
+        obj.add(12);
+        the_world::foo::foo::resources::borrows(&obj);
+        the_world::foo::foo::resources::consume(obj);
+        let obj2 = the_world::foo::foo::resources::R::new(42);
+        drop(obj2);
+    }
+}
+
+the_world::export!(MyWorld with_types_in the_world);
diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs
new file mode 100644
index 000000000..c80b4a3a4
--- /dev/null
+++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs
@@ -0,0 +1,622 @@
+// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code)]
+pub mod foo {
+  #[allow(dead_code)]
+  pub mod foo {
+    #[allow(dead_code, clippy::all)]
+    pub mod resources {
+      #[used]
+      #[doc(hidden)]
+      #[cfg(target_arch = "wasm32")]
+      static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports;
+      use super::super::super::_rt;
+
+      #[derive(Debug)]
+      #[repr(transparent)]
+      pub struct R{
+        handle: _rt::Resource<R>,
+      }
+
+      impl R{
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: u32) -> Self {
+          Self {
+            handle: _rt::Resource::from_handle(handle),
+          }
+        }
+
+        #[doc(hidden)]
+        pub fn take_handle(&self) -> u32 {
+          _rt::Resource::take_handle(&self.handle)
+        }
+
+        #[doc(hidden)]
+        pub fn handle(&self) -> u32 {
+          _rt::Resource::handle(&self.handle)
+        }
+      }
+
+
+      unsafe impl _rt::WasmResource for R{
+        #[inline]
+        unsafe fn drop(_handle: u32) {
+          {
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]r")]
+              fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_: u32);
+            }
+
+            fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_handle);
+          }
+        }
+      }
+
+      impl R {
+        #[allow(unused_unsafe, clippy::all)]
+        pub fn new(a: u32,) -> Self{
+          unsafe {
+
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]r")]
+              fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> i32;
+            }
+            let ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_rt::as_i32(&a));
+            R::from_handle(ret as u32)
+          }
+        }
+      }
+      impl R {
+        #[allow(unused_unsafe, clippy::all)]
+        pub fn add(&self,b: u32,){
+          unsafe {
+
+            #[link(wasm_import_module = "foo:foo/resources")]
+            extern "C" {
+              #[cfg_attr(target_arch = "wasm32", link_name = "[method]r.add")]
+              fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(_: i32, _: i32, );
+            }
+            fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle() as i32, _rt::as_i32(&b));
+          }
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn create() -> R{
+        unsafe {
+
+          #[link(wasm_import_module = "foo:foo/resources")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "create")]
+            fn fooX3AfooX2FresourcesX00create() -> i32;
+          }
+          let ret = fooX3AfooX2FresourcesX00create();
+          R::from_handle(ret as u32)
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn borrows(o: &R,){
+        unsafe {
+
+          #[link(wasm_import_module = "foo:foo/resources")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "borrows")]
+            fn fooX3AfooX2FresourcesX00borrows(_: i32, );
+          }
+          fooX3AfooX2FresourcesX00borrows((o).handle() as i32);
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn consume(o: R,){
+        unsafe {
+
+          #[link(wasm_import_module = "foo:foo/resources")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "consume")]
+            fn fooX3AfooX2FresourcesX00consume(_: i32, );
+          }
+          fooX3AfooX2FresourcesX00consume((&o).take_handle() as i32);
+        }
+      }
+
+    }
+
+  }
+}
+#[allow(dead_code)]
+pub mod exports {
+  #[allow(dead_code)]
+  pub mod foo {
+    #[allow(dead_code)]
+    pub mod foo {
+      #[allow(dead_code, clippy::all)]
+      pub mod resources {
+        #[used]
+        #[doc(hidden)]
+        #[cfg(target_arch = "wasm32")]
+        static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports;
+        use super::super::super::super::_rt;
+
+        #[derive(Debug)]
+        #[repr(transparent)]
+        pub struct R{
+          handle: _rt::Resource<R>,
+        }
+
+        type _RRep<T> = Option<T>;
+
+        impl R{
+          /// Creates a new resource from the specified representation.
+          ///
+          /// This function will create a new resource handle by moving `val` onto
+          /// the heap and then passing that heap pointer to the component model to
+          /// create a handle. The owned handle is then returned as `R`.
+          pub fn new<T: GuestR>(val: T) -> Self {
+            Self::type_guard::<T>();
+            let val: _RRep<T> = Some(val);
+            let ptr: *mut _RRep<T> =
+            _rt::Box::into_raw(_rt::Box::new(val));
+            unsafe {
+              Self::from_handle(T::_resource_new(ptr.cast()))
+            }
+          }
+
+          /// Gets access to the underlying `T` which represents this resource.
+          pub fn get<T: GuestR>(&self) -> &T {
+            let ptr = unsafe { &*self.as_ptr::<T>() };
+            ptr.as_ref().unwrap()
+          }
+
+          /// Gets mutable access to the underlying `T` which represents this
+          /// resource.
+          pub fn get_mut<T: GuestR>(&mut self) -> &mut T {
+            let ptr = unsafe { &mut *self.as_ptr::<T>() };
+            ptr.as_mut().unwrap()
+          }
+
+          /// Consumes this resource and returns the underlying `T`.
+          pub fn into_inner<T: GuestR>(self) -> T {
+            let ptr = unsafe { &mut *self.as_ptr::<T>() };
+            ptr.take().unwrap()
+          }
+
+          #[doc(hidden)]
+          pub unsafe fn from_handle(handle: u32) -> Self {
+            Self {
+              handle: _rt::Resource::from_handle(handle),
+            }
+          }
+
+          #[doc(hidden)]
+          pub fn take_handle(&self) -> u32 {
+            _rt::Resource::take_handle(&self.handle)
+          }
+
+          #[doc(hidden)]
+          pub fn handle(&self) -> u32 {
+            _rt::Resource::handle(&self.handle)
+          }
+
+          // It's theoretically possible to implement the `GuestR` trait twice
+          // so guard against using it with two different types here.
+          #[doc(hidden)]
+          fn type_guard<T: 'static>() {
+            use core::any::TypeId;
+            static mut LAST_TYPE: Option<TypeId> = None;
+            unsafe {
+              assert!(!cfg!(target_feature = "threads"));
+              let id = TypeId::of::<T>();
+              match LAST_TYPE {
+                Some(ty) => assert!(ty == id, "cannot use two types with this resource type"),
+                None => LAST_TYPE = Some(id),
+              }
+            }
+          }
+
+          #[doc(hidden)]
+          pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+            Self::type_guard::<T>();
+            let _ = _rt::Box::from_raw(handle as *mut _RRep<T>);
+          }
+
+          fn as_ptr<T: GuestR>(&self) -> *mut _RRep<T> {
+            R::type_guard::<T>();
+            T::_resource_rep(self.handle()).cast()
+          }
+        }
+
+        /// A borrowed version of [`R`] which represents a borrowed value
+        /// with the lifetime `'a`.
+        #[derive(Debug)]
+        #[repr(transparent)]
+        pub struct RBorrow<'a> {
+          rep: *mut u8,
+          _marker: core::marker::PhantomData<&'a R>,
+        }
+
+        impl<'a> RBorrow<'a>{
+          #[doc(hidden)]
+          pub unsafe fn lift(rep: usize) -> Self {
+            Self {
+              rep: rep as *mut u8,
+              _marker: core::marker::PhantomData,
+            }
+          }
+
+          /// Gets access to the underlying `T` in this resource.
+          pub fn get<T: GuestR>(&self) -> &T {
+            let ptr = unsafe { &mut *self.as_ptr::<T>() };
+            ptr.as_ref().unwrap()
+          }
+
+          // NB: mutable access is not allowed due to the component model allowing
+          // multiple borrows of the same resource.
+
+          fn as_ptr<T: 'static>(&self) -> *mut _RRep<T> {
+            R::type_guard::<T>();
+            self.rep.cast()
+          }
+        }
+
+
+        unsafe impl _rt::WasmResource for R{
+          #[inline]
+          unsafe fn drop(_handle: u32) {
+            {
+              #[link(wasm_import_module = "[export]foo:foo/resources")]
+              extern "C" {
+                #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]r")]
+                fn X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_: u32);
+              }
+
+              X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_handle);
+            }
+          }
+        }
+
+        #[doc(hidden)]
+        #[allow(non_snake_case)]
+        pub unsafe fn _export_constructor_r_cabi<T: GuestR>(arg0: i32,) -> i32 {#[cfg(target_arch="wasm32")]
+        _rt::run_ctors_once();let result0 = R::new(T::new(arg0 as u32));
+        (result0).take_handle() as i32
+      }
+      #[doc(hidden)]
+      #[allow(non_snake_case)]
+      pub unsafe fn _export_method_r_add_cabi<T: GuestR>(arg0: *mut u8,arg1: i32,) {#[cfg(target_arch="wasm32")]
+      _rt::run_ctors_once();T::add(RBorrow::lift(arg0 as usize).get(), arg1 as u32);
+    }
+    #[doc(hidden)]
+    #[allow(non_snake_case)]
+    pub unsafe fn _export_create_cabi<T: Guest>() -> i32 {#[cfg(target_arch="wasm32")]
+    _rt::run_ctors_once();let result0 = T::create();
+    (result0).take_handle() as i32
+  }
+  #[doc(hidden)]
+  #[allow(non_snake_case)]
+  pub unsafe fn _export_borrows_cabi<T: Guest>(arg0: *const u8,) {#[cfg(target_arch="wasm32")]
+  _rt::run_ctors_once();T::borrows(RBorrow::lift(arg0 as usize));
+}
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub unsafe fn _export_consume_cabi<T: Guest>(arg0: i32,) {#[cfg(target_arch="wasm32")]
+_rt::run_ctors_once();T::consume(R::from_handle(arg0 as u32));
+}
+pub trait Guest {
+  type R: GuestR;
+  fn create() -> R;
+  fn borrows(o: RBorrow<'_>,);
+  fn consume(o: R,);
+}
+pub trait GuestR: 'static {
+
+  #[doc(hidden)]
+  unsafe fn _resource_new(val: *mut u8) -> u32
+  where Self: Sized
+  {
+    #[link(wasm_import_module = "[export]foo:foo/resources")]
+    extern "C" {
+      #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]r")]
+      fn X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(_: *mut u8) -> u32;
+    }
+    X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(val)
+  }
+
+  #[doc(hidden)]
+  fn _resource_rep(handle: u32) -> *mut u8
+  where Self: Sized
+  {
+    #[link(wasm_import_module = "[export]foo:foo/resources")]
+    extern "C" {
+      #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]r")]
+      fn X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(_: u32) -> *mut u8;
+    }
+    unsafe {
+      X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(handle)
+    }
+  }
+
+  
+  fn new(a: u32,) -> Self;
+  fn add(&self,b: u32,);
+}
+#[doc(hidden)]
+
+macro_rules! __export_foo_foo_resources_cabi{
+  ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+    #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#[constructor]r")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn fooX3AfooX2FresourcesX23X5BconstructorX5Dr(arg0: i32,) -> i32 {
+      $($path_to_types)*::_export_constructor_r_cabi::<<$ty as $($path_to_types)*::Guest>::R>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#[method]r.add")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(arg0: *mut u8,arg1: i32,) {
+      $($path_to_types)*::_export_method_r_add_cabi::<<$ty as $($path_to_types)*::Guest>::R>(arg0, arg1)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#create")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn fooX3AfooX2FresourcesX23create() -> i32 {
+      $($path_to_types)*::_export_create_cabi::<$ty>()
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#borrows")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn fooX3AfooX2FresourcesX23borrows(arg0: *const u8,) {
+      $($path_to_types)*::_export_borrows_cabi::<$ty>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#consume")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn fooX3AfooX2FresourcesX23consume(arg0: i32,) {
+      $($path_to_types)*::_export_consume_cabi::<$ty>(arg0)
+    }
+
+    const _: () = {
+      #[doc(hidden)]
+      #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#[dtor]r")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      #[allow(non_snake_case)]
+      unsafe extern "C" fn fooX3AfooX2FresourcesX23X5BdtorX5Dr(rep: *mut u8) {
+        $($path_to_types)*::R::dtor::<
+        <$ty as $($path_to_types)*::Guest>::R
+        >(rep)
+      }
+    };
+    
+  };);
+}
+#[doc(hidden)]
+pub(crate) use __export_foo_foo_resources_cabi;
+
+}
+
+}
+}
+}
+mod _rt {
+
+
+  use core::fmt;
+  use core::marker;
+  use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
+
+  /// A type which represents a component model resource, either imported or
+  /// exported into this component.
+  ///
+  /// This is a low-level wrapper which handles the lifetime of the resource
+  /// (namely this has a destructor). The `T` provided defines the component model
+  /// intrinsics that this wrapper uses.
+  ///
+  /// One of the chief purposes of this type is to provide `Deref` implementations
+  /// to access the underlying data when it is owned.
+  ///
+  /// This type is primarily used in generated code for exported and imported
+  /// resources.
+  #[repr(transparent)]
+  pub struct Resource<T: WasmResource> {
+    // NB: This would ideally be `u32` but it is not. The fact that this has
+    // interior mutability is not exposed in the API of this type except for the
+    // `take_handle` method which is supposed to in theory be private.
+    //
+    // This represents, almost all the time, a valid handle value. When it's
+    // invalid it's stored as `u32::MAX`.
+    handle: AtomicU32,
+    _marker: marker::PhantomData<T>,
+  }
+
+  /// A trait which all wasm resources implement, namely providing the ability to
+  /// drop a resource.
+  ///
+  /// This generally is implemented by generated code, not user-facing code.
+  pub unsafe trait WasmResource {
+    /// Invokes the `[resource-drop]...` intrinsic.
+    unsafe fn drop(handle: u32);
+  }
+
+  impl<T: WasmResource> Resource<T> {
+    #[doc(hidden)]
+    pub unsafe fn from_handle(handle: u32) -> Self {
+      debug_assert!(handle != u32::MAX);
+      Self {
+        handle: AtomicU32::new(handle),
+        _marker: marker::PhantomData,
+      }
+    }
+
+    /// Takes ownership of the handle owned by `resource`.
+    ///
+    /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+    /// ownership. The code generator does not enable that in all situations,
+    /// unfortunately, so this is provided instead.
+    ///
+    /// Also note that `take_handle` is in theory only ever called on values
+    /// owned by a generated function. For example a generated function might
+    /// take `Resource<T>` as an argument but then call `take_handle` on a
+    /// reference to that argument. In that sense the dynamic nature of
+    /// `take_handle` should only be exposed internally to generated code, not
+    /// to user code.
+    #[doc(hidden)]
+    pub fn take_handle(resource: &Resource<T>) -> u32 {
+      resource.handle.swap(u32::MAX, Relaxed)
+    }
+
+    #[doc(hidden)]
+    pub fn handle(resource: &Resource<T>) -> u32 {
+      resource.handle.load(Relaxed)
+    }
+  }
+
+  impl<T: WasmResource> fmt::Debug for Resource<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+      f.debug_struct("Resource")
+      .field("handle", &self.handle)
+      .finish()
+    }
+  }
+
+  impl<T: WasmResource> Drop for Resource<T> {
+    fn drop(&mut self) {
+      unsafe {
+        match self.handle.load(Relaxed) {
+          // If this handle was "taken" then don't do anything in the
+          // destructor.
+          u32::MAX => {}
+
+          // ... but otherwise do actually destroy it with the imported
+          // component model intrinsic as defined through `T`.
+          other => T::drop(other),
+        }
+      }
+    }
+  }
+  
+  pub fn as_i32<T: AsI32>(t: T) -> i32 {
+    t.as_i32()
+  }
+
+  pub trait AsI32 {
+    fn as_i32(self) -> i32;
+  }
+
+  impl<'a, T: Copy + AsI32> AsI32 for &'a T {
+    fn as_i32(self) -> i32 {
+      (*self).as_i32()
+    }
+  }
+  
+  impl AsI32 for i32 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u32 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for i16 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u16 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for i8 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for u8 {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for char {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  
+  impl AsI32 for usize {
+    #[inline]
+    fn as_i32(self) -> i32 {
+      self as i32
+    }
+  }
+  pub use alloc_crate::boxed::Box;
+
+  #[cfg(target_arch = "wasm32")]
+  pub fn run_ctors_once() {
+    wit_bindgen::rt::run_ctors_once();
+  }
+  extern crate alloc as alloc_crate;
+}
+
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_the_world_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::foo::foo::resources::__export_foo_foo_resources_cabi!($ty with_types_in $($path_to_types_root)*::exports::foo::foo::resources);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_the_world_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.24.0:the-world:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 460] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xcc\x02\x01A\x02\x01\
+A\x04\x01B\x0d\x04\0\x01r\x03\x01\x01i\0\x01@\x01\x01ay\0\x01\x04\0\x0e[construc\
+tor]r\x01\x02\x01h\0\x01@\x02\x04self\x03\x01by\x01\0\x04\0\x0d[method]r.add\x01\
+\x04\x01@\0\0\x01\x04\0\x06create\x01\x05\x01@\x01\x01o\x03\x01\0\x04\0\x07borro\
+ws\x01\x06\x01@\x01\x01o\x01\x01\0\x04\0\x07consume\x01\x07\x03\x01\x11foo:foo/r\
+esources\x05\0\x01B\x0d\x04\0\x01r\x03\x01\x01i\0\x01@\x01\x01ay\0\x01\x04\0\x0e\
+[constructor]r\x01\x02\x01h\0\x01@\x02\x04self\x03\x01by\x01\0\x04\0\x0d[method]\
+r.add\x01\x04\x01@\0\0\x01\x04\0\x06create\x01\x05\x01@\x01\x01o\x03\x01\0\x04\0\
+\x07borrows\x01\x06\x01@\x01\x01o\x01\x01\0\x04\0\x07consume\x01\x07\x04\x01\x11\
+foo:foo/resources\x05\x01\x04\x01\x11foo:foo/the-world\x04\0\x0b\x0f\x01\0\x09th\
+e-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.20\
+2.0\x10wit-bindgen-rust\x060.24.0";
+
+#[inline(never)]
+#[doc(hidden)]
+#[cfg(target_arch = "wasm32")]
+pub fn __link_custom_section_describing_imports() {
+  wit_bindgen::rt::maybe_link_cabi_realloc();
+}
+
diff --git a/crates/cpp/tests/native_resources/the_world_cpp_native.h b/crates/cpp/tests/native_resources/the_world_cpp_native.h
new file mode 100644
index 000000000..dbe1400c8
--- /dev/null
+++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h
@@ -0,0 +1,46 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_NATIVE_BINDINGS_THE_WORLD_H
+#define __CPP_NATIVE_BINDINGS_THE_WORLD_H
+#define WIT_HOST_DIRECT
+#include <cassert>
+#include <cstdint>
+#include <map>
+#include <utility>
+#include <wit-host.h>
+
+// guest imports (implemented here)
+#include "foo-foo-resources-R.h"
+namespace foo {
+namespace foo {
+namespace resources {
+R::Owned Create();
+void Borrows(std::reference_wrapper<const R> o);
+void Consume(R::Owned o);
+// export_interface Interface(Id { idx: 0 })
+} // namespace resources
+} // namespace foo
+} // namespace foo
+namespace exports {
+namespace foo {
+namespace foo {
+namespace resources {
+class R : public wit::ResourceExportBase {
+
+public:
+  ~R();
+  R(uint32_t a);
+  void Add(uint32_t b) const;
+  R(wit::ResourceExportBase &&);
+  R(R &&) = default;
+  R &operator=(R &&) = default;
+};
+
+R Create();
+void Borrows(std::reference_wrapper<const R> o);
+void Consume(R &&o);
+} // namespace resources
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp
new file mode 100644
index 000000000..eb9522ded
--- /dev/null
+++ b/crates/cpp/tests/native_resources/the_world_native.cpp
@@ -0,0 +1,89 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#include "the_world_cpp_native.h"
+template <class R> std::map<int32_t, R> wit::ResourceTable<R>::resources;
+#include <assert.h>
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[dtor]r"))) void
+fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[constructor]r")))
+int32_t fooX3AfooX2FresourcesX23X5BconstructorX5Dr(int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("[method]r.add"))) void
+fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *, int32_t);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("create"))) int32_t
+fooX3AfooX2FresourcesX23create();
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("borrows"))) void
+    fooX3AfooX2FresourcesX23borrows(uint8_t*);
+extern "C" __attribute__((import_module("foo:foo/resources")))
+__attribute__((import_name("consume"))) void
+    fooX3AfooX2FresourcesX23consume(int32_t);
+extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) {
+  auto ptr = foo::foo::resources::R::remove_resource(arg0);
+  assert(ptr.has_value());
+  foo::foo::resources::R::Dtor(*ptr);
+}
+extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) {
+  auto result0 = foo::foo::resources::R::New((uint32_t(arg0)));
+  return result0.release()->get_handle();
+}
+extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0,
+                                                            int32_t arg1) {
+  (**foo::foo::resources::R::lookup_resource(arg0)).Add((uint32_t(arg1)));
+}
+extern "C" int32_t fooX3AfooX2FresourcesX00create() {
+  auto result0 = foo::foo::resources::Create();
+  return result0.release()->get_handle();
+}
+extern "C" void fooX3AfooX2FresourcesX00borrows(int32_t arg0) {
+  foo::foo::resources::Borrows(**foo::foo::resources::R::lookup_resource(arg0));
+}
+extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) {
+  auto obj0 = foo::foo::resources::R::lookup_resource(arg0);
+  //assert(obj0 != nullptr);
+  foo::foo::resources::Consume(foo::foo::resources::R::Owned(*obj0));
+}
+exports::foo::foo::resources::R::~R() {
+  if (this->rep) {
+    fooX3AfooX2FresourcesX23X5BdtorX5Dr(this->rep);
+  }
+}
+exports::foo::foo::resources::R::R(uint32_t a) {
+  auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a)));
+  wit::ResourceExportBase retobj = wit::ResourceExportBase{ret};
+  this->index = retobj.get_handle();
+  this->rep = retobj.take_rep();
+}
+void exports::foo::foo::resources::R::Add(uint32_t b) const {
+  fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b)));
+}
+exports::foo::foo::resources::R::R(wit::ResourceExportBase &&b)
+    : wit::ResourceExportBase(std::move(b)) {}
+extern "C" int32_t
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *arg0) {
+  return exports::foo::foo::resources::R::store_resource(std::move(arg0));
+}
+extern "C" uint8_t *
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t arg0) {
+  return *exports::foo::foo::resources::R::lookup_resource(arg0);
+}
+extern "C" void
+X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) {
+  auto obj = exports::foo::foo::resources::R::remove_resource(arg0);
+  fooX3AfooX2FresourcesX23X5BdtorX5Dr(*obj);
+}
+exports::foo::foo::resources::R exports::foo::foo::resources::Create() {
+  auto ret = fooX3AfooX2FresourcesX23create();
+  return wit::ResourceExportBase{ret};
+}
+void exports::foo::foo::resources::Borrows(std::reference_wrapper<const R> o) {
+  fooX3AfooX2FresourcesX23borrows(o.get().get_rep());
+}
+void exports::foo::foo::resources::Consume(R &&o) {
+  auto rep0 = o.take_rep();
+  fooX3AfooX2FresourcesX23consume(o.get_handle());
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_resources/wit/resources_simple.wit b/crates/cpp/tests/native_resources/wit/resources_simple.wit
new file mode 100644
index 000000000..c849359af
--- /dev/null
+++ b/crates/cpp/tests/native_resources/wit/resources_simple.wit
@@ -0,0 +1,16 @@
+package foo:foo;
+
+interface resources {
+    resource r {
+        constructor(a: u32);
+        add: func(b: u32);
+    }
+    create: func() -> r;
+    borrows: func(o: borrow<r>);
+    consume: func(o: r);
+}
+
+world the-world {
+  import resources;
+  export resources;
+}
diff --git a/crates/cpp/tests/native_strings/.gitignore b/crates/cpp/tests/native_strings/.gitignore
new file mode 100644
index 000000000..56eab019c
--- /dev/null
+++ b/crates/cpp/tests/native_strings/.gitignore
@@ -0,0 +1,3 @@
+/*.o
+/libstrings.so
+/app-strings
diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile
new file mode 100644
index 000000000..54cdcdf09
--- /dev/null
+++ b/crates/cpp/tests/native_strings/Makefile
@@ -0,0 +1,36 @@
+CXXFLAGS=-g -O0 -I../../helper-types
+WIT_BINDGEN=../../../../target/debug/wit-bindgen
+
+all: libstrings.so app-strings
+
+libstrings.so: the_world.pie.o guest.pie.o
+	$(CXX) $(CXXFLAGS) -shared -o $@ $^ -Wl,--version-script=guest.lds
+	
+%.pie.o: %.cpp
+	$(CXX) $(CXXFLAGS) -fPIE -o $@ -c $^
+
+app-strings: the_world_native.o main.o
+	$(CXX) $(CXXFLAGS) -o $@ $^ -L. -lstrings
+
+bindgen: wit/strings.wit
+	$(WIT_BINDGEN) cpp wit --wasm64 --format
+	$(WIT_BINDGEN) cpp wit --wasm64 --format --direct
+	cd rust/src ; ../../$(WIT_BINDGEN) rust ../../wit --wasm64
+
+guest.wasm: the_world.cpp guest.cpp
+	/opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) 
+	
+guest_release.wasm: the_world.cpp guest.cpp
+	/opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) -g0 -O3
+	
+clean:
+	-rm *.o libstrings.so app-strings
+
+run:
+	LD_LIBRARY_PATH=. ./app-strings
+
+valgrind:
+	LD_LIBRARY_PATH=. valgrind ./app-strings
+
+w2c2_guest.c: guest_release.wasm
+	w2c2 $^ $@
diff --git a/crates/cpp/tests/native_strings/guest.cpp b/crates/cpp/tests/native_strings/guest.cpp
new file mode 100644
index 000000000..90fa6258d
--- /dev/null
+++ b/crates/cpp/tests/native_strings/guest.cpp
@@ -0,0 +1,13 @@
+#include "the_world_cpp.h"
+
+void exports::foo::foo::strings::A(wit::string &&x) {
+    ::foo::foo::strings::A(x.get_view());
+}
+
+wit::string exports::foo::foo::strings::B() {
+    return ::foo::foo::strings::B();
+}
+
+wit::string exports::foo::foo::strings::C(wit::string &&x, wit::string &&b) {
+    return ::foo::foo::strings::C(x.get_view(), b.get_view());
+}
diff --git a/crates/cpp/tests/native_strings/guest.lds b/crates/cpp/tests/native_strings/guest.lds
new file mode 100644
index 000000000..da4b4f12e
--- /dev/null
+++ b/crates/cpp/tests/native_strings/guest.lds
@@ -0,0 +1,7 @@
+{
+    global:
+        fooX3AfooX2FstringsX23*;
+        cabi_post_fooX3AfooX2FstringsX23*;
+        cabi_realloc;
+    local: *;
+};
diff --git a/crates/cpp/tests/native_strings/main.cpp b/crates/cpp/tests/native_strings/main.cpp
new file mode 100644
index 000000000..0f86198e5
--- /dev/null
+++ b/crates/cpp/tests/native_strings/main.cpp
@@ -0,0 +1,33 @@
+
+#include "the_world_cpp_native.h"
+#include <iostream>
+
+void foo::foo::strings::A(std::string_view x) {
+    std::cout << x << std::endl;
+}
+wit::string foo::foo::strings::B() {
+    wit::string b = wit::string::from_view(std::string_view("hello B"));
+    return b;
+}
+wit::string foo::foo::strings::C(std::string_view a, std::string_view b) {
+    std::cout << a << '|' << b << std::endl;
+    wit::string c = wit::string::from_view(std::string_view("hello C"));
+    return c;
+}
+
+int main() {
+    wit::string a = wit::string::from_view(std::string_view("hello A"));
+    exports::foo::foo::strings::A(a);
+
+    {
+        auto b = exports::foo::foo::strings::B();
+        std::cout << b.inner() << std::endl;
+        // make sure that b's result is destructed before calling C
+    }
+
+    wit::string c1 = wit::string::from_view(std::string_view("hello C1"));
+    wit::string c2 = wit::string::from_view(std::string_view("hello C2"));
+    auto c = exports::foo::foo::strings::C(c1, c2);
+    std::cout << c.inner() << std::endl;
+    return 0;
+}
diff --git a/crates/cpp/tests/native_strings/rust/Cargo.lock b/crates/cpp/tests/native_strings/rust/Cargo.lock
new file mode 100644
index 000000000..d85cfd92e
--- /dev/null
+++ b/crates/cpp/tests/native_strings/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "strings"
+version = "0.1.0"
diff --git a/crates/cpp/tests/native_strings/rust/Cargo.toml b/crates/cpp/tests/native_strings/rust/Cargo.toml
new file mode 100644
index 000000000..54920869d
--- /dev/null
+++ b/crates/cpp/tests/native_strings/rust/Cargo.toml
@@ -0,0 +1,12 @@
+[workspace]
+
+[package]
+name = "strings"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+# wit-bindgen-rt = "0.21.0"
diff --git a/crates/cpp/tests/native_strings/rust/src/lib.rs b/crates/cpp/tests/native_strings/rust/src/lib.rs
new file mode 100644
index 000000000..0fb041a18
--- /dev/null
+++ b/crates/cpp/tests/native_strings/rust/src/lib.rs
@@ -0,0 +1,49 @@
+use std::alloc::Layout;
+
+use the_world::exports::foo::foo::strings::Guest;
+
+mod the_world;
+
+struct MyWorld;
+
+impl Guest for MyWorld {
+    fn a(x: String,) {
+        the_world::foo::foo::strings::a(&x);
+    }
+
+    fn b() -> String {
+        the_world::foo::foo::strings::b()
+    }
+
+    fn c(a: String,b: String,) -> String {
+        the_world::foo::foo::strings::c(&a, &b)
+    }
+}
+
+the_world::export!(MyWorld with_types_in the_world);
+
+// the crate wit-bindgen-rt doesn't work on native
+#[no_mangle]
+pub unsafe extern "C" fn cabi_realloc(
+    old_ptr: *mut u8,
+    old_len: usize,
+    align: usize,
+    new_len: usize,
+) -> *mut u8 {
+    let layout;
+    let ptr = if old_len == 0 {
+        if new_len == 0 {
+            return align as *mut u8;
+        }
+        layout = Layout::from_size_align_unchecked(new_len, align);
+        std::alloc::alloc(layout)
+    } else {
+        debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!");
+        layout = Layout::from_size_align_unchecked(old_len, align);
+        std::alloc::realloc(old_ptr, layout, new_len)
+    };
+    if ptr.is_null() {
+            unreachable!();
+    }
+    return ptr;
+}
diff --git a/crates/cpp/tests/native_strings/rust/src/the_world.rs b/crates/cpp/tests/native_strings/rust/src/the_world.rs
new file mode 100644
index 000000000..18fde4703
--- /dev/null
+++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs
@@ -0,0 +1,266 @@
+// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code)]
+pub mod foo {
+  #[allow(dead_code)]
+  pub mod foo {
+    #[allow(dead_code, clippy::all)]
+    pub mod strings {
+      #[used]
+      #[doc(hidden)]
+      #[cfg(target_arch = "wasm32")]
+      static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports;
+      use super::super::super::_rt;
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn a(x: &str,) -> (){
+        unsafe {
+          let vec0 = x;
+          let ptr0 = vec0.as_ptr().cast::<u8>();
+          let len0 = vec0.len();
+
+          #[link(wasm_import_module = "foo:foo/strings")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "a")]
+            fn fooX3AfooX2FstringsX00a(_: *mut u8, _: usize, );
+          }
+          fooX3AfooX2FstringsX00a(ptr0.cast_mut(), len0);
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn b() -> _rt::String{
+        unsafe {
+          #[repr(align(8))]
+          struct RetArea([::core::mem::MaybeUninit::<u8>; 16]);
+          let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]);
+          let ptr0 = ret_area.0.as_mut_ptr().cast::<u8>();
+          #[link(wasm_import_module = "foo:foo/strings")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "b")]
+            fn fooX3AfooX2FstringsX00b(_: *mut u8, );
+          }
+          fooX3AfooX2FstringsX00b(ptr0);
+          let l1 = *ptr0.add(0).cast::<*mut u8>();
+          let l2 = *ptr0.add(8).cast::<usize>();
+          let len3 = l2;
+          let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3);
+          let result4 = _rt::string_lift(bytes3);
+          result4
+        }
+      }
+      #[allow(unused_unsafe, clippy::all)]
+      pub fn c(a: &str,b: &str,) -> _rt::String{
+        unsafe {
+          #[repr(align(8))]
+          struct RetArea([::core::mem::MaybeUninit::<u8>; 16]);
+          let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]);
+          let vec0 = a;
+          let ptr0 = vec0.as_ptr().cast::<u8>();
+          let len0 = vec0.len();
+          let vec1 = b;
+          let ptr1 = vec1.as_ptr().cast::<u8>();
+          let len1 = vec1.len();
+          let ptr2 = ret_area.0.as_mut_ptr().cast::<u8>();
+          #[link(wasm_import_module = "foo:foo/strings")]
+          extern "C" {
+            #[cfg_attr(target_arch = "wasm32", link_name = "c")]
+            fn fooX3AfooX2FstringsX00c(_: *mut u8, _: usize, _: *mut u8, _: usize, _: *mut u8, );
+          }
+          fooX3AfooX2FstringsX00c(ptr0.cast_mut(), len0, ptr1.cast_mut(), len1, ptr2);
+          let l3 = *ptr2.add(0).cast::<*mut u8>();
+          let l4 = *ptr2.add(8).cast::<usize>();
+          let len5 = l4;
+          let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5);
+          let result6 = _rt::string_lift(bytes5);
+          result6
+        }
+      }
+
+    }
+
+  }
+}
+#[allow(dead_code)]
+pub mod exports {
+  #[allow(dead_code)]
+  pub mod foo {
+    #[allow(dead_code)]
+    pub mod foo {
+      #[allow(dead_code, clippy::all)]
+      pub mod strings {
+        #[used]
+        #[doc(hidden)]
+        #[cfg(target_arch = "wasm32")]
+        static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports;
+        use super::super::super::super::_rt;
+        #[doc(hidden)]
+        #[allow(non_snake_case)]
+        pub unsafe fn _export_a_cabi<T: Guest>(arg0: *mut u8,arg1: usize,) {#[cfg(target_arch="wasm32")]
+        _rt::run_ctors_once();let len0 = arg1;
+        let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0);
+        T::a(_rt::string_lift(bytes0));
+      }
+      #[doc(hidden)]
+      #[allow(non_snake_case)]
+      pub unsafe fn _export_b_cabi<T: Guest>() -> *mut u8 {#[cfg(target_arch="wasm32")]
+      _rt::run_ctors_once();let result0 = T::b();
+      let ptr1 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
+      let vec2 = (result0.into_bytes()).into_boxed_slice();
+      let ptr2 = vec2.as_ptr().cast::<u8>();
+      let len2 = vec2.len();
+      ::core::mem::forget(vec2);
+      *ptr1.add(8).cast::<usize>() = len2;
+      *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut();
+      ptr1
+    }
+    #[doc(hidden)]
+    #[allow(non_snake_case)]
+    pub unsafe fn __post_return_b<T: Guest>(arg0: *mut u8,) {
+      let l0 = *arg0.add(0).cast::<*mut u8>();
+      let l1 = *arg0.add(8).cast::<usize>();
+      _rt::cabi_dealloc(l0, l1, 1);
+    }
+    #[doc(hidden)]
+    #[allow(non_snake_case)]
+    pub unsafe fn _export_c_cabi<T: Guest>(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 {#[cfg(target_arch="wasm32")]
+    _rt::run_ctors_once();let len0 = arg1;
+    let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0);
+    let len1 = arg3;
+    let bytes1 = _rt::Vec::from_raw_parts(arg2.cast(), len1, len1);
+    let result2 = T::c(_rt::string_lift(bytes0), _rt::string_lift(bytes1));
+    let ptr3 = _RET_AREA.0.as_mut_ptr().cast::<u8>();
+    let vec4 = (result2.into_bytes()).into_boxed_slice();
+    let ptr4 = vec4.as_ptr().cast::<u8>();
+    let len4 = vec4.len();
+    ::core::mem::forget(vec4);
+    *ptr3.add(8).cast::<usize>() = len4;
+    *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut();
+    ptr3
+  }
+  #[doc(hidden)]
+  #[allow(non_snake_case)]
+  pub unsafe fn __post_return_c<T: Guest>(arg0: *mut u8,) {
+    let l0 = *arg0.add(0).cast::<*mut u8>();
+    let l1 = *arg0.add(8).cast::<usize>();
+    _rt::cabi_dealloc(l0, l1, 1);
+  }
+  pub trait Guest {
+    fn a(x: _rt::String,) -> ();
+    fn b() -> _rt::String;
+    fn c(a: _rt::String,b: _rt::String,) -> _rt::String;
+  }
+  #[doc(hidden)]
+
+  macro_rules! __export_foo_foo_strings_cabi{
+    ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+      #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#a")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      unsafe extern "C" fn fooX3AfooX2FstringsX23a(arg0: *mut u8,arg1: usize,) {
+        $($path_to_types)*::_export_a_cabi::<$ty>(arg0, arg1)
+      }
+      #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#b")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      unsafe extern "C" fn fooX3AfooX2FstringsX23b() -> *mut u8 {
+        $($path_to_types)*::_export_b_cabi::<$ty>()
+      }
+      #[cfg_attr(target_arch = "wasm32", export_name = "cabi_post_foo:foo/strings#b")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      unsafe extern "C" fn cabi_post_fooX3AfooX2FstringsX23b(arg0: *mut u8,) {
+        $($path_to_types)*::__post_return_b::<$ty>(arg0)
+      }
+      #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#c")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      unsafe extern "C" fn fooX3AfooX2FstringsX23c(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 {
+        $($path_to_types)*::_export_c_cabi::<$ty>(arg0, arg1, arg2, arg3)
+      }
+      #[cfg_attr(target_arch = "wasm32", export_name = "cabi_post_foo:foo/strings#c")]
+      #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+      unsafe extern "C" fn cabi_post_fooX3AfooX2FstringsX23c(arg0: *mut u8,) {
+        $($path_to_types)*::__post_return_c::<$ty>(arg0)
+      }
+    };);
+  }
+  #[doc(hidden)]
+  pub(crate) use __export_foo_foo_strings_cabi;
+  #[repr(align(8))]
+  struct _RetArea([::core::mem::MaybeUninit::<u8>; 16]);
+  static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 16]);
+
+}
+
+}
+}
+}
+mod _rt {
+  pub use alloc_crate::string::String;
+  pub use alloc_crate::vec::Vec;
+  pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
+    if cfg!(debug_assertions) {
+      String::from_utf8(bytes).unwrap()
+    } else {
+      String::from_utf8_unchecked(bytes)
+    }
+  }
+  
+  #[cfg(target_arch = "wasm32")]
+  pub fn run_ctors_once() {
+    wit_bindgen::rt::run_ctors_once();
+  }
+  pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) {
+    if size == 0 {
+      return;
+    }
+    let layout = alloc::Layout::from_size_align_unchecked(size, align);
+    alloc::dealloc(ptr, layout);
+  }
+  extern crate alloc as alloc_crate;
+  pub use alloc_crate::alloc;
+}
+
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_the_world_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::foo::foo::strings::__export_foo_foo_strings_cabi!($ty with_types_in $($path_to_types_root)*::exports::foo::foo::strings);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_the_world_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.28.0:the-world:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 286] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x9e\x01\x01A\x02\x01\
+A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x01\
+\x01@\x02\x01as\x01bs\0s\x04\0\x01c\x01\x02\x03\x01\x0ffoo:foo/strings\x05\0\x01\
+B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x01\x01@\x02\
+\x01as\x01bs\0s\x04\0\x01c\x01\x02\x04\x01\x0ffoo:foo/strings\x05\x01\x04\x01\x11\
+foo:foo/the-world\x04\0\x0b\x0f\x01\0\x09the-world\x03\0\0\0G\x09producers\x01\x0c\
+processed-by\x02\x0dwit-component\x070.214.0\x10wit-bindgen-rust\x060.28.0";
+
+#[inline(never)]
+#[doc(hidden)]
+#[cfg(target_arch = "wasm32")]
+pub fn __link_custom_section_describing_imports() {
+  wit_bindgen::rt::maybe_link_cabi_realloc();
+}
+
diff --git a/crates/cpp/tests/native_strings/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp
new file mode 100644
index 000000000..03c455465
--- /dev/null
+++ b/crates/cpp/tests/native_strings/the_world.cpp
@@ -0,0 +1,125 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_the_world(void);
+void __component_type_object_force_link_the_world_public_use_in_this_compilation_unit(
+    void) {
+  __component_type_object_force_link_the_world();
+}
+#endif
+#include "the_world_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc"))) void *
+cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void)old_size;
+  if (new_size == 0)
+    return (void *)align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret)
+    abort();
+  return ret;
+}
+
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("a"))) void
+fooX3AfooX2FstringsX00a(uint8_t *, size_t);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("b"))) void
+fooX3AfooX2FstringsX00b(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("c"))) void
+fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+void foo::foo::strings::A(std::string_view x) {
+  auto const &vec0 = x;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  fooX3AfooX2FstringsX00a(ptr0, len0);
+}
+wit::string foo::foo::strings::B() {
+  uint64_t ret_area[2];
+  uint8_t *ptr0 = (uint8_t *)(&ret_area);
+  fooX3AfooX2FstringsX00b(ptr0);
+  auto len1 = *((size_t *)(ptr0 + 8));
+
+  auto result2 = wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1);
+  return result2;
+}
+wit::string foo::foo::strings::C(std::string_view a, std::string_view b) {
+  auto const &vec0 = a;
+  auto ptr0 = (uint8_t *)(vec0.data());
+  auto len0 = (size_t)(vec0.size());
+  auto const &vec1 = b;
+  auto ptr1 = (uint8_t *)(vec1.data());
+  auto len1 = (size_t)(vec1.size());
+  uint64_t ret_area[2];
+  uint8_t *ptr2 = (uint8_t *)(&ret_area);
+  fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2);
+  auto len3 = *((size_t *)(ptr2 + 8));
+
+  auto result4 = wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3);
+  return result4;
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void
+fooX3AfooX2FstringsX23a(uint8_t *arg0, size_t arg1) {
+  auto len0 = arg1;
+
+  exports::foo::foo::strings::A(wit::string((char const *)(arg0), len0));
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t *
+fooX3AfooX2FstringsX23b() {
+  auto result0 = exports::foo::foo::strings::B();
+  static uint64_t ret_area[2];
+  uint8_t *ptr1 = (uint8_t *)(&ret_area);
+  auto const &vec2 = result0;
+  auto ptr2 = (uint8_t *)(vec2.data());
+  auto len2 = (size_t)(vec2.size());
+  result0.leak();
+
+  *((size_t *)(ptr1 + 8)) = len2;
+  *((uint8_t **)(ptr1 + 0)) = ptr2;
+  return ptr1;
+}
+extern "C"
+    __attribute__((__weak__,
+                   __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void
+    cabi_post_fooX3AfooX2FstringsX23b(uint8_t *arg0) {
+  if ((*((size_t *)(arg0 + 8))) > 0) {
+    wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0))));
+  }
+}
+extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t *
+fooX3AfooX2FstringsX23c(uint8_t *arg0, size_t arg1, uint8_t *arg2,
+                        size_t arg3) {
+  auto len0 = arg1;
+
+  auto len1 = arg3;
+
+  auto result2 =
+      exports::foo::foo::strings::C(wit::string((char const *)(arg0), len0),
+                                    wit::string((char const *)(arg2), len1));
+  static uint64_t ret_area[2];
+  uint8_t *ptr3 = (uint8_t *)(&ret_area);
+  auto const &vec4 = result2;
+  auto ptr4 = (uint8_t *)(vec4.data());
+  auto len4 = (size_t)(vec4.size());
+  result2.leak();
+
+  *((size_t *)(ptr3 + 8)) = len4;
+  *((uint8_t **)(ptr3 + 0)) = ptr4;
+  return ptr3;
+}
+extern "C"
+    __attribute__((__weak__,
+                   __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void
+    cabi_post_fooX3AfooX2FstringsX23c(uint8_t *arg0) {
+  if ((*((size_t *)(arg0 + 8))) > 0) {
+    wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0))));
+  }
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_strings/the_world_cpp.h b/crates/cpp/tests/native_strings/the_world_cpp.h
new file mode 100644
index 000000000..41957c9c7
--- /dev/null
+++ b/crates/cpp/tests/native_strings/the_world_cpp.h
@@ -0,0 +1,30 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_THE_WORLD_H
+#define __CPP_GUEST_BINDINGS_THE_WORLD_H
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <wit-guest.h>
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+// export_interface Interface(Id { idx: 0 })
+} // namespace strings
+} // namespace foo
+} // namespace foo
+namespace exports {
+namespace foo {
+namespace foo {
+namespace strings {
+void A(wit::string &&x);
+wit::string B();
+wit::string C(wit::string &&a, wit::string &&b);
+} // namespace strings
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/native_strings/the_world_cpp_native.h b/crates/cpp/tests/native_strings/the_world_cpp_native.h
new file mode 100644
index 000000000..18546c4cb
--- /dev/null
+++ b/crates/cpp/tests/native_strings/the_world_cpp_native.h
@@ -0,0 +1,31 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_NATIVE_BINDINGS_THE_WORLD_H
+#define __CPP_NATIVE_BINDINGS_THE_WORLD_H
+#define WIT_HOST_DIRECT
+#include <cstdint>
+#include <string_view>
+#include <utility>
+#include <wit-host.h>
+namespace foo {
+namespace foo {
+namespace strings {
+void A(std::string_view x);
+wit::string B();
+wit::string C(std::string_view a, std::string_view b);
+// export_interface Interface(Id { idx: 0 })
+} // namespace strings
+} // namespace foo
+} // namespace foo
+namespace exports {
+namespace foo {
+namespace foo {
+namespace strings {
+void A(wit::string x);
+wit::guest_owned<std::string_view> B();
+wit::guest_owned<std::string_view> C(wit::string a, wit::string b);
+} // namespace strings
+} // namespace foo
+} // namespace foo
+} // namespace exports
+
+#endif
diff --git a/crates/cpp/tests/native_strings/the_world_native.cpp b/crates/cpp/tests/native_strings/the_world_native.cpp
new file mode 100644
index 000000000..9e5ad10ca
--- /dev/null
+++ b/crates/cpp/tests/native_strings/the_world_native.cpp
@@ -0,0 +1,79 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#include "the_world_cpp_native.h"
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("a"))) void
+fooX3AfooX2FstringsX23a(uint8_t *, size_t);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("b"))) uint8_t *
+fooX3AfooX2FstringsX23b();
+extern "C" __attribute__((import_module("cabi_post_foo:foo/strings")))
+__attribute__((import_name("b"))) void
+cabi_post_fooX3AfooX2FstringsX23b(uint8_t *);
+extern "C" __attribute__((import_module("foo:foo/strings")))
+__attribute__((import_name("c"))) uint8_t *
+fooX3AfooX2FstringsX23c(uint8_t *, size_t, uint8_t *, size_t);
+extern "C" __attribute__((import_module("cabi_post_foo:foo/strings")))
+__attribute__((import_name("c"))) void
+cabi_post_fooX3AfooX2FstringsX23c(uint8_t *);
+extern "C" void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) {
+  auto len0 = arg1;
+
+  foo::foo::strings::A(std::string_view((char const *)(arg0), len0));
+}
+extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0, uint8_t *resultptr) {
+  auto result0 = foo::foo::strings::B();
+  auto const &vec1 = result0;
+  auto ptr1 = vec1.data();
+  auto len1 = vec1.size();
+  *((size_t *)(arg0 + 8)) = len1;
+  *((uint8_t **)(arg0 + 0)) = ptr1;
+}
+extern "C" void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1,
+                                        uint8_t *arg2, size_t arg3,
+                                        uint8_t *arg4, uint8_t *resultptr) {
+  auto len0 = arg1;
+
+  auto len1 = arg3;
+
+  auto result2 =
+      foo::foo::strings::C(std::string_view((char const *)(arg0), len0),
+                           std::string_view((char const *)(arg2), len1));
+  auto const &vec3 = result2;
+  auto ptr3 = vec3.data();
+  auto len3 = vec3.size();
+  *((size_t *)(arg4 + 8)) = len3;
+  *((uint8_t **)(arg4 + 0)) = ptr3;
+}
+void exports::foo::foo::strings::A(wit::string x) {
+  auto const &vec0 = x;
+  auto ptr0 = vec0.data();
+  auto len0 = vec0.size();
+  fooX3AfooX2FstringsX23a(ptr0, len0);
+}
+wit::guest_owned<std::string_view> exports::foo::foo::strings::B() {
+  auto ret = fooX3AfooX2FstringsX23b();
+  auto len0 = *((size_t *)(ret + 8));
+
+  auto result1 =
+      std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0);
+  return wit::guest_owned<std::string_view>(result1, ret,
+                                            cabi_post_fooX3AfooX2FstringsX23b);
+}
+wit::guest_owned<std::string_view>
+exports::foo::foo::strings::C(wit::string a, wit::string b) {
+  auto const &vec0 = a;
+  auto ptr0 = vec0.data();
+  auto len0 = vec0.size();
+  auto const &vec1 = b;
+  auto ptr1 = vec1.data();
+  auto len1 = vec1.size();
+  auto ret = fooX3AfooX2FstringsX23c(ptr0, len0, ptr1, len1);
+  auto len2 = *((size_t *)(ret + 8));
+
+  auto result3 =
+      std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2);
+  return wit::guest_owned<std::string_view>(result3, ret,
+                                            cabi_post_fooX3AfooX2FstringsX23c);
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/native_strings/w2c2/.gitignore b/crates/cpp/tests/native_strings/w2c2/.gitignore
new file mode 100644
index 000000000..f1cad9d4d
--- /dev/null
+++ b/crates/cpp/tests/native_strings/w2c2/.gitignore
@@ -0,0 +1,5 @@
+/libstrings.so
+/*.o
+/w2c2_base.h
+/w2c2_guest.?
+/the-world_bridge.c
diff --git a/crates/cpp/tests/native_strings/w2c2/Makefile b/crates/cpp/tests/native_strings/w2c2/Makefile
new file mode 100644
index 000000000..423211f7d
--- /dev/null
+++ b/crates/cpp/tests/native_strings/w2c2/Makefile
@@ -0,0 +1,36 @@
+CXXFLAGS=-g -O0 -I../../../helper-types
+WIT_BINDGEN=../../../../../target/debug/wit-bindgen
+W2C2_PATH=$(HOME)/github/w2c2
+
+all: libstrings.so
+
+w2c2_base.h: $(W2C2_PATH)/w2c2/w2c2_base.h
+	ln -s $^ .
+
+%.pie.o: %.c
+	$(CC) $(CXXFLAGS) -fPIE -o $@ -c $^
+
+libstrings.so: w2c2_guest.pie.o the-world_bridge.pie.o wasi_dummy.pie.o
+	$(CC) $(CXXFLAGS) -shared -o $@ $^ -Wl,--version-script=../guest.lds
+	
+guest_release.wasm: ../the_world.cpp ../guest.cpp
+	/opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) -g0 -O3
+	
+clean:
+	-rm *.o libstrings.so
+
+run:
+	LD_LIBRARY_PATH=. ../app-strings
+
+valgrind:
+	LD_LIBRARY_PATH=. valgrind ../app-strings
+
+w2c2_guest.c: guest_release.wasm
+	$(W2C2_PATH)/build/w2c2/w2c2 $^ $@
+
+the-world_bridge.c: the-world_bridge_target.c
+	cp $^ $@
+
+# not yet up to the task
+#the-world_bridge.c: $(WIT_BINDGEN)
+#	$(WIT_BINDGEN) bridge ../wit --instance guestrelease --include w2c2_guest.h
diff --git a/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c b/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c
new file mode 100644
index 000000000..3c2d967f5
--- /dev/null
+++ b/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c
@@ -0,0 +1,98 @@
+
+#include <stdint.h>
+#include <stdio.h>
+#include "w2c2_guest.h"
+
+static guestreleaseInstance* instance;
+static guestreleaseInstance app_instance;
+
+void trap(Trap trap) {
+  abort();
+}
+
+guestreleaseInstance* get_app() {
+  if (!instance) {
+    guestreleaseInstantiate(&app_instance, NULL);
+    instance = &app_instance;
+  }
+  return instance;
+}
+
+__attribute__ ((visibility ("default"))) 
+void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  uint32_t result = guestrelease_cabi_realloc(get_app(), ptr ? (uint8_t*)ptr-linmem : 0, old_size, align, new_size);
+  return result+linmem;
+}
+
+// Import IF strings
+// Func a GuestImport
+extern void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1);
+void fooX3AfooX2Fstrings__a(void*app,U32 arg0,U32 arg1) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  fooX3AfooX2FstringsX00a(linmem+arg0, arg1);
+}
+
+// Func b GuestImport
+extern void fooX3AfooX2FstringsX00b(uint8_t *arg0);
+void fooX3AfooX2Fstrings__b(void*app,U32 arg0) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  static size_t result[2];
+  fooX3AfooX2FstringsX00b((uint8_t*)&result);
+  uint32_t *result_out = (uint32_t*)(linmem+arg0);
+  result_out[0] = ((uint8_t*)(result[0]))-linmem;
+  result_out[1] = result[1];
+}
+// Func c GuestImport
+extern void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1,
+                                        uint8_t *arg2, size_t arg3,
+                                        uint8_t *arg4);
+void fooX3AfooX2Fstrings__c(void*app,U32 arg0,U32 arg1,U32 arg2,U32 arg3,U32 arg4) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  static size_t result[2];
+  fooX3AfooX2FstringsX00c(linmem+arg0, arg1, linmem+arg2, arg3, (uint8_t*)&result);
+  uint32_t *result_out = (uint32_t*)(linmem+arg4);
+  result_out[0] = ((uint8_t*)(result[0]))-linmem;
+  result_out[1] = result[1];
+}
+// Export IF strings
+// Func a GuestExport
+__attribute__ ((visibility ("default"))) 
+void fooX3AfooX2FstringsX23a(uint8_t *arg0, size_t arg1) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  guestrelease_fooX3AfooX2FstringsX23a(get_app(), arg0-linmem, arg1);
+}
+// Func b GuestExport
+__attribute__ ((visibility ("default"))) uint8_t *
+fooX3AfooX2FstringsX23b() {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  uint32_t result = guestrelease_fooX3AfooX2FstringsX23b(get_app());
+  static size_t ret_area[3];
+  ret_area[0] = (size_t)(((uint32_t*)(linmem+result))[0]+linmem);
+  ret_area[1] = ((uint32_t*)(linmem+result))[1];
+  ret_area[2] = result;
+  return (uint8_t*)ret_area;
+}
+__attribute__ ((visibility ("default"))) 
+void cabi_post_fooX3AfooX2FstringsX23b(uint8_t * arg0) {
+  //uint8_t *linmem = guestrelease_memory(get_app())->data;
+  guestrelease_cabi_post_fooX583AfooX582FstringsX5823b(get_app(), ((size_t*)arg0)[2]);
+}
+// Func c GuestExport
+__attribute__ ((visibility ("default"))) 
+uint8_t * fooX3AfooX2FstringsX23c(uint8_t * arg0, size_t arg1, uint8_t *arg2, size_t arg3) {
+  uint8_t *linmem = guestrelease_memory(get_app())->data;
+  uint32_t result = guestrelease_fooX3AfooX2FstringsX23c(get_app(), arg0-linmem, arg1, arg2-linmem, arg3);
+  static size_t ret_area[3];
+  ret_area[0] = (size_t)(((uint32_t*)(linmem+result))[0]+linmem);
+  ret_area[1] = ((uint32_t*)(linmem+result))[1];
+  ret_area[2] = result;
+  return (uint8_t*)ret_area;
+}
+__attribute__ ((visibility ("default"))) 
+extern void
+cabi_post_fooX3AfooX2FstringsX23c(uint8_t * arg0) {
+  //uint8_t *linmem = guestrelease_memory(get_app())->data;
+  guestrelease_cabi_post_fooX583AfooX582FstringsX5823c(get_app(), ((size_t*)arg0)[2]);
+}
diff --git a/crates/cpp/tests/native_strings/w2c2/wasi_dummy.c b/crates/cpp/tests/native_strings/w2c2/wasi_dummy.c
new file mode 100644
index 000000000..3e587b9ef
--- /dev/null
+++ b/crates/cpp/tests/native_strings/w2c2/wasi_dummy.c
@@ -0,0 +1,13 @@
+#include "w2c2_guest.h"
+
+U32 wasi_snapshot_preview1__args_get(void*,U32,U32) {
+    abort();
+}
+
+U32 wasi_snapshot_preview1__args_sizes_get(void*,U32,U32) {
+    abort();
+}
+
+void wasi_snapshot_preview1__proc_exit(void*,U32) {
+    abort();
+}
diff --git a/crates/cpp/tests/native_strings/wamr/.gitignore b/crates/cpp/tests/native_strings/wamr/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/crates/cpp/tests/native_strings/wamr/CMakeLists.txt b/crates/cpp/tests/native_strings/wamr/CMakeLists.txt
new file mode 100644
index 000000000..9437f4ba0
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.14)
+project(wasm_executor)
+
+set (WAMR_BUILD_PLATFORM "linux")
+set (WAMR_BUILD_TARGET "X86_64")
+set (WAMR_BUILD_INTERP 1)
+set (WAMR_BUILD_FAST_INTERP 0)
+set (WAMR_BUILD_JIT 0)
+set (WAMR_BUILD_FAST_JIT 0)
+set (WAMR_BUILD_AOT 0)
+set (WAMR_BUILD_LIBC_BUILTIN 1)
+set (WAMR_BUILD_LIBC_WASI 1)
+set (WAMR_BUILD_SIMD 1)
+# set (WAMR_BUILD_LIB_WASI_THREADS 1)
+set (WAMR_ROOT_DIR ../../wasm-micro-runtime)
+
+include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
+add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
+
+add_library(strings SHARED 
+    wamr_env.c the-world_bridge.c 
+    ${WAMR_ROOT_DIR}/core/shared/utils/uncommon/bh_read_file.c)
+target_include_directories(strings PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${WAMR_ROOT_DIR}/core/shared/utils/uncommon)
+target_link_libraries (strings vmlib)
diff --git a/crates/cpp/tests/native_strings/wamr/Makefile b/crates/cpp/tests/native_strings/wamr/Makefile
new file mode 100644
index 000000000..59bb4273b
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/Makefile
@@ -0,0 +1,23 @@
+WIT_BINDGEN=../../../../../target/debug/wit-bindgen
+
+all: libstrings.so guest_release.wasm
+
+libstrings.so: 
+	mkdir build
+	(cd build; cmake .. ; make)
+	
+guest_release.wasm: ../the_world.cpp ../guest.cpp
+	/opt/wasi-sdk/bin/clang++ -o $@ $^ -I../../../helper-types -g0 -O3
+	
+clean:
+	-rm -r build guest_release.wasm
+
+run:
+	LD_LIBRARY_PATH=. ../app-strings
+
+# valgrind:
+# 	LD_LIBRARY_PATH=. valgrind ../app-strings
+
+# not yet up to the task
+#the-world_bridge.c: $(WIT_BINDGEN)
+#	$(WIT_BINDGEN) bridge ../wit --instance guestrelease --include w2c2_guest.h
diff --git a/crates/cpp/tests/native_strings/wamr/libstrings.so b/crates/cpp/tests/native_strings/wamr/libstrings.so
new file mode 120000
index 000000000..087edba73
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/libstrings.so
@@ -0,0 +1 @@
+build/libstrings.so
\ No newline at end of file
diff --git a/crates/cpp/tests/native_strings/wamr/the-world_bridge.c b/crates/cpp/tests/native_strings/wamr/the-world_bridge.c
new file mode 100644
index 000000000..c6fa3a13c
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/the-world_bridge.c
@@ -0,0 +1,201 @@
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "wamr_env.h"
+
+#define nullptr 0
+
+typedef struct wamr_env wamr_env;
+
+static wamr_env* instance = nullptr;
+
+wamr_env* get_app() {
+  if (!instance) {
+    instance = create_wamr_env();
+  }
+  return instance;
+}
+
+uint8_t* guestrelease_memory(wamr_env* wamr_env) {
+  return (uint8_t*)wasm_runtime_addr_app_to_native(
+        wasm_runtime_get_module_inst(wamr_env->exec_env), 0);
+}
+
+uint32_t guestrelease_cabi_realloc(wamr_env* wamr_env, uint32_t olda, uint32_t olds, uint32_t align, uint32_t new_size) {
+  WASMFunctionInstanceCommon *cabi_alloc_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "cabi_realloc");
+//                                    "(iiii)i");
+  wasm_val_t results[1] = {WASM_INIT_VAL};
+    wasm_val_t arguments[4] = {WASM_I32_VAL((int32_t)olda), WASM_I32_VAL((int32_t)olds), WASM_I32_VAL((int32_t)align), WASM_I32_VAL((int32_t)new_size)};
+
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, cabi_alloc_ptr, 1, results, 4, arguments))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+        return 0;
+    }
+    return results[0].of.i32;
+}
+
+__attribute__ ((visibility ("default"))) 
+void *cabi_realloc(void *ptr, size_t old_size, size_t align,
+                              size_t new_size) {
+  uint8_t *linmem = guestrelease_memory(get_app());
+  uint32_t result = guestrelease_cabi_realloc(get_app(), ptr ? (uint8_t*)ptr-linmem : 0, old_size, align, new_size);
+  return result+linmem;
+}
+
+// Import IF strings
+// Func a GuestImport
+extern void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1);
+void fooX3AfooX2Fstrings__a(void*app,uint8_t* arg0,uint32_t arg1) {
+  //uint8_t *linmem = guestrelease_memory(get_app());
+  fooX3AfooX2FstringsX00a(arg0, arg1);
+}
+
+// Func b GuestImport
+extern void fooX3AfooX2FstringsX00b(uint8_t *arg0);
+void fooX3AfooX2Fstrings__b(void*app,uint8_t* arg0) {
+  uint8_t *linmem = guestrelease_memory(get_app());
+  static size_t result[2];
+  fooX3AfooX2FstringsX00b((uint8_t*)&result);
+  uint32_t *result_out = (uint32_t*)(arg0);
+  result_out[0] = ((uint8_t*)(result[0]))-linmem;
+  result_out[1] = result[1];
+}
+// Func c GuestImport
+extern void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1,
+                                        uint8_t *arg2, size_t arg3,
+                                        uint8_t *arg4);
+void fooX3AfooX2Fstrings__c(void*app,uint8_t* arg0,uint32_t arg1,uint8_t* arg2,uint32_t arg3,uint8_t* arg4) {
+  uint8_t *linmem = guestrelease_memory(get_app());
+  static size_t result[2];
+  fooX3AfooX2FstringsX00c(arg0, arg1, arg2, arg3, (uint8_t*)&result);
+  uint32_t *result_out = (uint32_t*)(arg4);
+  result_out[0] = ((uint8_t*)(result[0]))-linmem;
+  result_out[1] = result[1];
+}
+// Export IF strings
+// Func a GuestExport
+__attribute__ ((visibility ("default"))) 
+void fooX3AfooX2FstringsX23a(uint8_t *arg0, size_t arg1) {
+  wamr_env *wamr_env = get_app();
+  uint8_t *linmem = guestrelease_memory(wamr_env);
+  WASMFunctionInstanceCommon *func_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "foo:foo/strings#a");
+    wasm_val_t arguments[2] = {WASM_I32_VAL((int32_t)(arg0-linmem)), WASM_I32_VAL((int32_t)arg1)};
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, func_ptr, 0, nullptr, 2, arguments))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+    }
+}
+// Func b GuestExport
+__attribute__ ((visibility ("default"))) uint8_t *
+fooX3AfooX2FstringsX23b() {
+  wamr_env *wamr_env = get_app();
+  uint8_t *linmem = guestrelease_memory(wamr_env);
+  WASMFunctionInstanceCommon *func_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "foo:foo/strings#b");
+    wasm_val_t results[1] = {WASM_INIT_VAL};
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, func_ptr, 1, results, 0, nullptr))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+    }
+  uint32_t result = results[0].of.i32;
+  static size_t ret_area[3];
+  ret_area[0] = (size_t)(((uint32_t*)(linmem+result))[0]+linmem);
+  ret_area[1] = ((uint32_t*)(linmem+result))[1];
+  ret_area[2] = result;
+  return (uint8_t*)ret_area;
+}
+__attribute__ ((visibility ("default"))) 
+void cabi_post_fooX3AfooX2FstringsX23b(uint8_t * arg0) {
+  wamr_env *wamr_env = get_app();
+  // uint8_t *linmem = guestrelease_memory(wamr_env);
+  WASMFunctionInstanceCommon *func_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "cabi_post_fooX3AfooX2FstringsX23b");
+    wasm_val_t arguments[1] = {WASM_I32_VAL((int32_t)(((size_t*)arg0)[2]))};
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, func_ptr, 0, nullptr, 1, arguments))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+    }
+}
+// Func c GuestExport
+__attribute__ ((visibility ("default"))) 
+uint8_t * fooX3AfooX2FstringsX23c(uint8_t * arg0, size_t arg1, uint8_t *arg2, size_t arg3) {
+  wamr_env *wamr_env = get_app();
+  uint8_t *linmem = guestrelease_memory(wamr_env);
+  WASMFunctionInstanceCommon *func_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "foo:foo/strings#c");
+    wasm_val_t results[1] = {WASM_INIT_VAL};
+    wasm_val_t arguments[4] = {WASM_I32_VAL((int32_t)(arg0-linmem)), WASM_I32_VAL((int32_t)arg1), WASM_I32_VAL((int32_t)(arg2-linmem)), WASM_I32_VAL((int32_t)arg3)};
+
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, func_ptr, 1, results, 4, arguments))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+        return 0;
+    }
+  uint32_t result = results[0].of.i32;
+  // arg0-linmem, arg1, arg2-linmem, arg3);
+  static size_t ret_area[3];
+  ret_area[0] = (size_t)(((uint32_t*)(linmem+result))[0]+linmem);
+  ret_area[1] = ((uint32_t*)(linmem+result))[1];
+  ret_area[2] = result;
+  return (uint8_t*)ret_area;
+}
+__attribute__ ((visibility ("default"))) 
+extern void
+cabi_post_fooX3AfooX2FstringsX23c(uint8_t * arg0) {
+  wamr_env *wamr_env = get_app();
+  // uint8_t *linmem = guestrelease_memory(wamr_env);
+  WASMFunctionInstanceCommon *func_ptr = wasm_runtime_lookup_function(
+                                    wasm_runtime_get_module_inst(wamr_env->exec_env), 
+                                    "cabi_post_fooX3AfooX2FstringsX23c");
+    wasm_val_t arguments[1] = {WASM_I32_VAL((int32_t)(((size_t*)arg0)[2]))};
+    if (!wasm_runtime_call_wasm_a(wamr_env->exec_env, func_ptr, 0, nullptr, 1, arguments))
+    {
+        const char *exception;
+        if ((exception = wasm_runtime_get_exception(wasm_runtime_get_module_inst(wamr_env->exec_env))))
+        {
+            printf("Exception: %s\n", exception);
+        }
+    }
+}
+
+//#include "executor.h"
+
+void register_functions() {
+  static NativeSymbol foo_foo_strings_funs[] = {
+      {"a", (void *)fooX3AfooX2Fstrings__a, "(*~)", nullptr},
+      {"b", (void *)fooX3AfooX2Fstrings__b, "(*)", nullptr},
+      {"c", (void *)fooX3AfooX2Fstrings__c, "(*~*~*)", nullptr},
+  };
+  wasm_runtime_register_natives("foo:foo/strings", foo_foo_strings_funs,
+                                sizeof(foo_foo_strings_funs) /
+                                    sizeof(NativeSymbol));
+}
diff --git a/crates/cpp/tests/native_strings/wamr/wamr_env.c b/crates/cpp/tests/native_strings/wamr/wamr_env.c
new file mode 100644
index 000000000..9ba2efea4
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/wamr_env.c
@@ -0,0 +1,86 @@
+/*
+ * Adapted from wasm-micro-runtime/samples/basic/src/main.c
+ *
+ * Copyright (C) 2019 Intel Corporation.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "wamr_env.h"
+#include "bh_read_file.h"
+
+#define nullptr 0
+
+struct wamr_env *create_wamr_env() {
+  struct wamr_env *result = (struct wamr_env *)malloc(sizeof(struct wamr_env));
+  char const *wasm_path = "guest_release.wasm";
+  const uint32_t stack_size = 65536, heap_size = 2 * stack_size;
+  uint32_t buf_size;
+
+  if (!result)
+    return nullptr;
+  memset(result, 0, sizeof *result);
+
+  RuntimeInitArgs init_args;
+  memset(&init_args, 0, sizeof(RuntimeInitArgs));
+
+  init_args.mem_alloc_type = Alloc_With_Pool;
+  init_args.mem_alloc_option.pool.heap_buf = result->global_heap_buf;
+  init_args.mem_alloc_option.pool.heap_size = sizeof(result->global_heap_buf);
+  init_args.running_mode = Mode_Interp;
+  if (!wasm_runtime_full_init(&init_args)) {
+    printf("Init runtime environment failed.\n");
+    return result;
+  }
+
+  register_functions();
+
+  wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE);
+
+  result->buffer = bh_read_file_to_buffer(wasm_path, &buf_size);
+
+  if (!result->buffer) {
+    printf("Open wasm app file [%s] failed.\n", wasm_path);
+    return result;
+  }
+
+  result->module =
+      wasm_runtime_load((uint8 *)result->buffer, buf_size, result->error_buf,
+                        sizeof(result->error_buf));
+  if (!result->module) {
+    printf("Load wasm module failed. error: %s\n", result->error_buf);
+    return result;
+  }
+
+  result->module_inst =
+      wasm_runtime_instantiate(result->module, stack_size, heap_size,
+                               result->error_buf, sizeof(result->error_buf));
+
+  if (!result->module_inst) {
+    printf("Instantiate wasm module failed. error: %s\n", result->error_buf);
+    return result;
+  }
+
+  result->exec_env =
+      wasm_runtime_create_exec_env(result->module_inst, stack_size);
+  if (!result->exec_env) {
+    printf("Create wasm execution environment failed.\n");
+  }
+
+  return result;
+}
+
+void free_wamr_env(struct wamr_env *result) {
+  if (!result)
+    return;
+  if (result->exec_env)
+    wasm_runtime_destroy_exec_env(result->exec_env);
+  if (result->module_inst) {
+    wasm_runtime_deinstantiate(result->module_inst);
+  }
+  if (result->module)
+    wasm_runtime_unload(result->module);
+  if (result->buffer)
+    BH_FREE(result->buffer);
+  wasm_runtime_destroy();
+  free(result);
+}
diff --git a/crates/cpp/tests/native_strings/wamr/wamr_env.h b/crates/cpp/tests/native_strings/wamr/wamr_env.h
new file mode 100644
index 000000000..717d52c68
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wamr/wamr_env.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "wasm_c_api.h"
+#include "wasm_export.h"
+
+void register_functions();
+
+struct wamr_env {
+  char global_heap_buf[512 * 1024];
+  char *buffer;
+  char error_buf[128];
+
+  wasm_module_t module;
+  wasm_module_inst_t module_inst;
+  wasm_exec_env_t exec_env;
+};
+
+struct wamr_env *create_wamr_env();
+void free_wamr_env(struct wamr_env *);
diff --git a/crates/cpp/tests/native_strings/wit/strings.wit b/crates/cpp/tests/native_strings/wit/strings.wit
new file mode 120000
index 000000000..ce038e736
--- /dev/null
+++ b/crates/cpp/tests/native_strings/wit/strings.wit
@@ -0,0 +1 @@
+../../../../../tests/codegen/strings.wit
\ No newline at end of file
diff --git a/crates/cpp/tests/smoke_details/result8.wit b/crates/cpp/tests/smoke_details/result8.wit
new file mode 100644
index 000000000..3ece74f19
--- /dev/null
+++ b/crates/cpp/tests/smoke_details/result8.wit
@@ -0,0 +1,9 @@
+package test:test;
+
+interface a {
+   f: func() -> result<_, u8>;
+}
+
+world result8 {
+   export a;
+}
diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs
new file mode 100644
index 000000000..e4f041157
--- /dev/null
+++ b/crates/cpp/tests/symmetric.rs
@@ -0,0 +1,357 @@
+use std::{
+    fs::{self, File},
+    io::{self, Write},
+    path::PathBuf,
+    process::Command,
+};
+
+use wit_bindgen_core::wit_parser::{Resolve, WorldId};
+
+fn tester_source_file(dir_name: &str, tester_source_dir: &PathBuf) -> Option<PathBuf> {
+    let mut tester_source_file = tester_source_dir.clone();
+    tester_source_file.push(&format!("{dir_name}.rs"));
+    if matches!(std::fs::exists(&tester_source_file), Ok(true)) {
+        Some(tester_source_file)
+    } else {
+        None
+    }
+}
+
+fn create_cargo_files(
+    dir_name: &str,
+    out_dir: &PathBuf,
+    toplevel: &PathBuf,
+    source_files: &PathBuf,
+    tester_source_dir: &PathBuf,
+) -> io::Result<()> {
+    let Some(tester_source_file) = tester_source_file(dir_name, tester_source_dir) else {
+        println!("Skipping {}", dir_name);
+        return Ok(());
+    };
+
+    let mut out_dir = out_dir.clone();
+    out_dir.push(dir_name);
+    // println!("{cpp:?} {out_dir:?}");
+
+    let mut dir = source_files.clone();
+    dir.push(dir_name);
+
+    drop(std::fs::remove_dir_all(&out_dir));
+    std::fs::create_dir_all(&out_dir)?;
+    let mut testee_dir = out_dir.clone();
+    testee_dir.push("rust");
+    std::fs::create_dir(&testee_dir)?;
+    let testee_cargo = format!(
+        "[package]\n\
+            name = \"{dir_name}\"\n\
+            publish = false\n\
+            edition = \"2021\"\n\
+            \n\
+            [dependencies]\n\
+            wit-bindgen = {{ path = \"{toplevel}/crates/guest-rust\" }}\n\
+            test-rust-wasm = {{ path = \"{toplevel}/crates/cpp/tests/symmetric_tests/test-rust-wasm\" }}\n\
+            futures = \"0.3\"\n\
+            once_cell = \"1.20\"\n\
+            \n\
+            [lib]\n\
+            crate-type = [\"cdylib\"]\n\
+            ",
+        toplevel = toplevel.display()
+    );
+    let mut filename = testee_dir.clone();
+    filename.push("Cargo.toml");
+    File::create(&filename)?.write_all(testee_cargo.as_bytes())?;
+    drop(testee_cargo);
+    // let mut testee_dir = out_dir.clone();
+    // testee_dir.push("rust");
+    //let mut filename = testee_dir.clone();
+    filename.pop();
+    filename.push("src");
+    std::fs::create_dir(&filename)?;
+    filename.push(format!("lib.rs"));
+    let mut original = dir.clone();
+    original.push("wasm.rs");
+    std::os::unix::fs::symlink(original, filename)?;
+
+    let tester_cargo = format!(
+        "[package]\n\
+            name = \"tester-{dir_name}\"\n\
+            publish = false\n\
+            edition = \"2021\"\n\
+            \n\
+            [dependencies]\n\
+            wit-bindgen = {{ path = \"{toplevel}/crates/guest-rust\" }}\n\
+            {dir_name} = {{ path = \"rust\" }}\n\
+            futures = \"0.3\"\n\
+            once_cell = \"1.20\"\n\
+            ",
+        toplevel = toplevel.display()
+    );
+    let mut filename = out_dir.clone();
+    filename.push("Cargo.toml");
+    File::create(&filename)?.write_all(tester_cargo.as_bytes())?;
+    filename.pop();
+    // let mut filename = out_dir.clone();
+    filename.push("src");
+    std::fs::create_dir(&filename)?;
+    filename.push(format!("main.rs"));
+    std::os::unix::fs::symlink(tester_source_file, &filename)?;
+
+    Ok(())
+}
+
+fn tests(
+    dir_name: &str,
+    out_dir: &PathBuf,
+    _toplevel: &PathBuf,
+    source_files: &PathBuf,
+    tester_source_dir: &PathBuf,
+) -> io::Result<()> {
+    // modelled after wit-bindgen/tests/runtime/main.rs
+    let Some(_tester_source_file) = tester_source_file(dir_name, tester_source_dir) else {
+        println!("Skipping {}", dir_name);
+        return Ok(());
+    };
+
+    let mut dir = source_files.clone();
+    dir.push(dir_name);
+
+    // let mut rust = Vec::new();
+    let mut cpp = Vec::new();
+    for file in dir.read_dir()? {
+        let path = file?.path();
+        match path.extension().and_then(|s| s.to_str()) {
+            // Some("rs") => rust.push(path),
+            Some("cpp") => cpp.push(path),
+            _ => {}
+        }
+    }
+
+    let mut out_dir = out_dir.clone();
+    out_dir.push(dir_name);
+    // println!("{cpp:?} {out_dir:?}");
+
+    let mut testee_dir = out_dir.clone();
+    testee_dir.push("rust");
+    let mut filename = testee_dir.clone();
+    filename.push("src");
+    //    std::fs::create_dir(&filename)?;
+    filename.push(format!("lib.rs"));
+    let mut original = dir.clone();
+    original.push("wasm.rs");
+    //    std::os::unix::fs::symlink(original, filename)?;
+
+    let mut filename = out_dir.clone();
+    filename.push("src");
+    //    std::fs::create_dir(&filename)?;
+    filename.push(format!("main.rs"));
+    //    std::os::unix::fs::symlink(tester_source_file, &filename)?;
+
+    let mut cmd = Command::new("cargo");
+    cmd.arg("build")
+        .current_dir(testee_dir)
+        .env("RUSTFLAGS", "-Ltarget/debug")
+        .env("SYMMETRIC_ABI", "1")
+        .env("WIT_BINDGEN_DEBUG", "1");
+    let status = cmd.status().unwrap();
+    assert!(status.success());
+
+    let mut cmd = Command::new("cargo");
+    cmd.arg("run")
+        .current_dir(&out_dir)
+        .env("RUSTFLAGS", "-Ltarget/debug")
+        .env("SYMMETRIC_ABI", "1")
+        .env("WIT_BINDGEN_DEBUG", "1");
+    let status = cmd.status().unwrap();
+    assert!(status.success());
+
+    for path in cpp.iter() {
+        let (mut resolve, mut world) = resolve_wit_dir(&dir);
+        let world_name = &resolve.worlds[world].name;
+        let cpp_dir = out_dir.join("cpp");
+        drop(fs::remove_dir_all(&cpp_dir));
+        fs::create_dir_all(&cpp_dir).unwrap();
+
+        let snake = world_name.replace("-", "_");
+        let mut files = Default::default();
+        let mut opts = wit_bindgen_cpp::Opts::default();
+        opts.symmetric = true;
+        if let Some(path) = path.file_name().and_then(|s| s.to_str()) {
+            if path.contains(".new.") {
+                opts.new_api = true;
+            }
+        }
+        let mut cpp = opts.build();
+        cpp.apply_resolve_options(&mut resolve, &mut world);
+        cpp.generate(&resolve, world, &mut files).unwrap();
+
+        for (file, contents) in files.iter() {
+            let dst = cpp_dir.join(file);
+            fs::write(dst, contents).unwrap();
+        }
+
+        let compiler = "clang++";
+        let mut cmd = Command::new(compiler);
+        let out_name = cpp_dir.join(format!("lib{}.so", dir_name));
+        cmd.arg(path)
+            .arg(cpp_dir.join(format!("{snake}.cpp")))
+            .arg("-shared")
+            .arg("-fPIC")
+            .arg("-I")
+            .arg(&cpp_dir)
+            .arg("-I")
+            .arg(&(String::from(env!("CARGO_MANIFEST_DIR")) + "/test_headers"))
+            .arg("-Wall")
+            .arg("-Wextra")
+            .arg("-Wno-unused-parameter")
+            .arg("-std=c++17")
+            .arg("-g")
+            .arg("-o")
+            .arg(&out_name);
+        println!("{:?}", cmd);
+        let output = match cmd.output() {
+            Ok(output) => output,
+            Err(e) => panic!("failed to spawn compiler: {}", e),
+        };
+
+        if !output.status.success() {
+            println!("status: {}", output.status);
+            println!("stdout: ------------------------------------------");
+            println!("{}", String::from_utf8_lossy(&output.stdout));
+            println!("stderr: ------------------------------------------");
+            println!("{}", String::from_utf8_lossy(&output.stderr));
+            panic!("failed to compile");
+        } else {
+            let mut tester = out_dir.clone();
+            tester.pop();
+            tester.push("target");
+            tester.push("debug");
+            tester.push(&format!("tester-{dir_name}"));
+            let run = Command::new(tester)
+                .env("LD_LIBRARY_PATH", cpp_dir)
+                .output();
+            match run {
+                Ok(output) => {
+                    if !output.status.success() {
+                        println!("status: {}", output.status);
+                        println!("stdout: ------------------------------------------");
+                        println!("{}", String::from_utf8_lossy(&output.stdout));
+                        println!("stderr: ------------------------------------------");
+                        println!("{}", String::from_utf8_lossy(&output.stderr));
+                        panic!("failed to run");
+                    }
+                }
+                Err(e) => panic!("failed to run tester: {}", e),
+            }
+        }
+    }
+
+    Ok(())
+}
+
+fn resolve_wit_dir(dir: &PathBuf) -> (Resolve, WorldId) {
+    let mut resolve = Resolve::new();
+    let (pkg, _files) = resolve.push_path(dir).unwrap();
+    let world = resolve.select_world(pkg, None).unwrap();
+    (resolve, world)
+}
+
+#[test]
+fn symmetric_integration() -> io::Result<()> {
+    let mut out_dir = std::env::current_exe()?;
+    out_dir.pop();
+    out_dir.pop();
+    out_dir.pop();
+    out_dir.push("symmetric-tests");
+    if !out_dir.try_exists().unwrap_or(false) {
+        std::fs::create_dir_all(&out_dir)?;
+    }
+
+    let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+
+    let mut toplevel = manifest_dir.clone();
+    toplevel.pop();
+    toplevel.pop();
+
+    let mut test_link = out_dir.clone();
+    test_link.push("tests");
+    if !test_link.try_exists().unwrap_or(false) {
+        let mut original = toplevel.clone();
+        original.push("tests");
+        std::os::unix::fs::symlink(original, &test_link)?;
+    }
+
+    let mut source_files = toplevel.clone();
+    source_files.push("tests");
+    source_files.push("runtime");
+
+    let mut tester_source_dir = manifest_dir.clone();
+    tester_source_dir.push("tests");
+    tester_source_dir.push("symmetric_tests");
+
+    let default_testcases = vec![
+        "flavorful",
+        "lists",
+        "many_arguments",
+        "numbers",
+        "options",
+        "records",
+        "results",
+        "smoke",
+        "strings",
+    ];
+    let testcases: Vec<String> = std::env::var_os("SYMMETRIC_TESTS").map_or_else(
+        || {
+            default_testcases
+                .iter()
+                .map(|s| s.to_string())
+                .collect::<Vec<String>>()
+        },
+        |var| {
+            var.into_string()
+                .expect("UTF8 expected")
+                .split(',')
+                .map(|s| s.to_string())
+                .collect()
+        },
+    );
+    // create workspace
+    {
+        let mut workspace = format!(
+            "[workspace]\n\
+                resolver = \"2\"\n\
+                \n\
+                members = [\n"
+        );
+        for dir_name in testcases.iter() {
+            if tester_source_file(dir_name, &tester_source_dir).is_some() {
+                workspace.push_str(&format!(
+                    "    \"{}\",\n    \"{}/rust\",\n",
+                    dir_name, dir_name
+                ));
+            }
+            create_cargo_files(
+                dir_name,
+                &out_dir,
+                &toplevel,
+                &source_files,
+                &tester_source_dir,
+            )?;
+        }
+        workspace.push_str("]\n");
+        let mut filename = out_dir.clone();
+        filename.push("Cargo.toml");
+        File::create(&filename)?.write_all(workspace.as_bytes())?;
+    }
+    for dir_name in testcases {
+        tests(
+            &dir_name,
+            &out_dir,
+            &toplevel,
+            &source_files,
+            &tester_source_dir,
+        )?;
+    }
+
+    Ok(())
+}
diff --git a/crates/cpp/tests/symmetric_async/Cargo.lock b/crates/cpp/tests/symmetric_async/Cargo.lock
new file mode 100644
index 000000000..e309431df
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/Cargo.lock
@@ -0,0 +1,224 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "async_module"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "sleep",
+ "symmetric_executor",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "dummy-rt"
+version = "0.1.0"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.167"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+
+[[package]]
+name = "main"
+version = "0.1.0"
+dependencies = [
+ "async_module",
+ "symmetric_executor",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "sleep"
+version = "0.1.0"
+dependencies = [
+ "symmetric_executor",
+]
+
+[[package]]
+name = "symmetric_executor"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "libc",
+]
+
+[[package]]
+name = "symmetric_stream"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "symmetric_executor",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+]
diff --git a/crates/cpp/tests/symmetric_async/Cargo.toml b/crates/cpp/tests/symmetric_async/Cargo.toml
new file mode 100644
index 000000000..50596891a
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+members = [ "async_module", "main","sleep"]
+resolver = "2"
diff --git a/crates/cpp/tests/symmetric_async/README.md b/crates/cpp/tests/symmetric_async/README.md
new file mode 100644
index 000000000..d11afed03
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/README.md
@@ -0,0 +1,26 @@
+# Native async design
+
+## With the canonical ABI
+
+Imported asynchronous functions take two arguments: A pointer to the argument buffer and a pointer to
+the result buffer. They return an i32, the two highest bits indicate the state of the async call, 
+the lower bits contain the task id to wait for completion. The argument buffer is freed by the callee.
+
+Exported asynchronous function receive their arguments normally in registers, the return value is set via 
+"[task-return]name" and it returns either null or a pointer to the state to pass to the callback
+once the blocking task completes.
+
+## Proposal for native
+
+Symmetric asynchronous functions receive their arguments normally, if they return a value they pass
+a return value buffer and they return a pointer to the object (EventSubscription) to wait on 
+or null (completed).
+
+If the bottommost bit of the return value is set (objects have an even address) the call wasn't started (backpressure) and should be retried once the returned event gets active.
+
+This combines the most efficient parts of the import and export (minimizing allocations and calls).
+
+## Executor
+
+See the crates/symmetric_executor directory. The main functions are create_timer, create_event, subscribe,
+register_callback and run.
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/.gitignore b/crates/cpp/tests/symmetric_async/async_cpp/.gitignore
new file mode 100644
index 000000000..925b4b450
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/.gitignore
@@ -0,0 +1,2 @@
+/*.o
+/*.so
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/Makefile b/crates/cpp/tests/symmetric_async/async_cpp/Makefile
new file mode 100644
index 000000000..7bc4e0fd8
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/Makefile
@@ -0,0 +1,16 @@
+CXXFLAGS=-g -Isrc -fPIC
+LDFLAGS=-L../../../../symmetric_executor/cpp-client \
+	-L../../../../symmetric_executor/target/debug \
+	-L../target/debug/deps
+
+libasync_module.so: async_module.o middle.o
+	$(CXX) -shared -o $@ $^ $(LDFLAGS) -lruntime -lsleep -lsymmetric_executor -lsymmetric_stream
+
+%.o: src/%.cpp
+	$(CXX) $(CXXFLAGS) -c -o $@ $^
+
+clean:
+	-rm libasync_module.so async_module.o middle.o
+
+run: libasync_module.so
+	LD_LIBRARY_PATH=.:../target/debug/deps ../target/debug/main
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/generate.sh b/crates/cpp/tests/symmetric_async/async_cpp/generate.sh
new file mode 100644
index 000000000..f2926eec6
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/generate.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+(cd src;../../../../../../target/debug/wit-bindgen cpp ../../wit/async_module.wit --symmetric --new-api)
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp
new file mode 100644
index 000000000..c10c0d327
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp
@@ -0,0 +1,88 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_async_module(void);
+void __component_type_object_force_link_async_module_public_use_in_this_compilation_unit(void) {
+  __component_type_object_force_link_async_module();
+}
+#endif
+#include "async_module_cpp.h"
+#include "module_cpp.h"
+#include <cstdlib> // realloc
+#include <chrono>
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc")))
+void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void) old_size;
+  if (new_size == 0) return (void*) align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret) abort();
+  return ret;
+}
+
+static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) {
+  std::unique_ptr<std::promise<void>> ptr((std::promise<void>*)data);
+  ptr->set_value();
+  return symmetric::runtime::symmetric_executor::CallbackState::kReady;
+}
+
+extern "C" void* testX3AtestX2FwaitX00X5BasyncX5Dsleep(int64_t);
+std::future<void> test::test::wait::Sleep(uint64_t nanoseconds)
+{
+  std::promise<void> result;
+  std::future<void> result1 = result.get_future();
+  void* wait = testX3AtestX2FwaitX00X5BasyncX5Dsleep((int64_t(nanoseconds)));
+  if (!wait) { result.set_value(); } else {
+    std::unique_ptr<std::promise<void>> ptr = std::make_unique<std::promise<void>>(std::move(result));
+    symmetric::runtime::symmetric_executor::EventSubscription ev = symmetric::runtime::symmetric_executor::EventSubscription(wit::ResourceImportBase((uint8_t*)wait));
+    symmetric::runtime::symmetric_executor::CallbackFunction fun = symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)fulfil_promise));
+    symmetric::runtime::symmetric_executor::CallbackData data = symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)ptr.release()));
+    symmetric::runtime::symmetric_executor::Register(std::move(ev), std::move(fun), std::move(data));
+  }
+  return result1;
+}
+
+static symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std::future<void>* fut) {
+  fut->get();
+  delete fut;
+  return symmetric::runtime::symmetric_executor::CallbackState::kReady;
+}
+
+extern "C" 
+void* testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(uint8_t* arg0, size_t arg1, uint8_t* arg2)
+{
+  auto len0 = arg1;
+
+  auto store = [arg2](wit::string && result1) {
+    auto ptr2 = (uint8_t*)(result1.data());
+    auto len2 = (size_t)(result1.size());
+    result1.leak();
+
+    *((size_t*)(arg2 + sizeof(void*))) = len2;
+    *((uint8_t**)(arg2 + 0)) = ptr2;
+  };
+
+  auto result1 = exports::test::test::string_delay::Forward(std::string_view((char const*)(arg0), len0));
+  if (result1.wait_for(std::chrono::seconds::zero()) == std::future_status::ready) {
+    store(result1.get());
+    return nullptr;
+  } else {
+    symmetric::runtime::symmetric_executor::EventGenerator gen;
+    auto waiting = gen.Subscribe();
+    auto task = std::async(std::launch::async, [store](std::future<wit::string>&& result1, 
+            symmetric::runtime::symmetric_executor::EventGenerator &&gen){
+      store(result1.get());
+      gen.Activate();
+    }, std::move(result1), std::move(gen));
+    auto fut = std::make_unique<std::future<void>>(std::move(task));
+    symmetric::runtime::symmetric_executor::Register(waiting.Dup(), 
+      symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)wait_on_future)),
+      symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)fut.release())));
+    return waiting.into_handle();
+  }
+}
+
+// Component Adapters
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module_cpp.h b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module_cpp.h
new file mode 100644
index 000000000..16d0e4c41
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module_cpp.h
@@ -0,0 +1,16 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_ASYNC_MODULE_H
+#define __CPP_GUEST_BINDINGS_ASYNC_MODULE_H
+#define WIT_SYMMETRIC
+#include <cstdint>
+#include <utility>
+#include <string_view>
+#include <wit-guest.h>
+#include <future>
+namespace test {namespace test {namespace wait {std::future<void> Sleep(uint64_t nanoseconds);
+// export_interface Interface(Id { idx: 0 })
+}}}
+namespace exports {namespace test {namespace test {namespace string_delay {std::future<wit::string> Forward(std::string_view s);
+}}}}
+
+#endif
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp b/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp
new file mode 100644
index 000000000..3ff4782ca
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp
@@ -0,0 +1,22 @@
+#include "async_module_cpp.h"
+#include <thread>
+
+std::future<wit::string> exports::test::test::string_delay::Forward(std::string_view s) {
+    if (s[0]=='A') {
+        std::promise<wit::string> result;
+        result.set_value(wit::string::from_view("directly returned"));
+        return result.get_future();
+    } else if (s[0]=='B') {
+        return std::async(std::launch::async, [](){
+            auto delay = ::test::test::wait::Sleep(5ull*1000*1000*1000);
+            delay.wait();
+            return wit::string::from_view("after five seconds");
+        });
+    } else {
+        return std::async(std::launch::async, [](){
+            auto delay = ::test::test::wait::Sleep(1*1000*1000*1000);
+            delay.wait();
+            return wit::string::from_view("after one second");
+        });
+    }
+}
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h b/crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h
new file mode 120000
index 000000000..c18f1b904
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h
@@ -0,0 +1 @@
+../../../../../symmetric_executor/cpp-client/module_cpp.h
\ No newline at end of file
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h b/crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h
new file mode 120000
index 000000000..14b0cb97c
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h
@@ -0,0 +1 @@
+../../../../helper-types/wit-common.h
\ No newline at end of file
diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h b/crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h
new file mode 120000
index 000000000..459000308
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h
@@ -0,0 +1 @@
+../../../../helper-types/wit-guest.h
\ No newline at end of file
diff --git a/crates/cpp/tests/symmetric_async/async_module/Cargo.toml b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml
new file mode 100644
index 000000000..96d7509f2
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "async_module"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+futures = "0.3.31"
+sleep = { path = "../sleep" }
+symmetric_executor = { path = "../../../../symmetric_executor" }
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+#wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "../../../../symmetric_executor/dummy-rt"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_async/async_module/build.rs b/crates/cpp/tests/symmetric_async/async_module/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_module/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs
new file mode 100644
index 000000000..d856d5e44
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs
@@ -0,0 +1,179 @@
+// Generated by `wit-bindgen` 0.40.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod test {
+    pub mod test {
+
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod wait {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            #[allow(unused_unsafe, clippy::all)]
+            pub async fn async_sleep(nanoseconds: u64) -> () {
+                unsafe {
+                    #[link(wasm_import_module = "test:test/wait")]
+                    #[link(name = "sleep")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")]
+                        fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: u64) -> *mut u8;
+                    }
+                    ::wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe {
+                        testX3AtestX2FwaitX00X5BasyncX5Dsleep(nanoseconds)
+                    })
+                    .await;
+                }
+            }
+        }
+    }
+}
+#[allow(dead_code, clippy::all)]
+pub mod exports {
+    pub mod test {
+        pub mod test {
+
+            #[allow(dead_code, unused_imports, clippy::all)]
+            pub mod string_delay {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+
+                use super::super::super::super::_rt;
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_async_forward_cabi<T: Guest>(
+                    arg0: *mut u8,
+                    arg1: usize,
+                    arg2: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result = async move {
+                        let len0 = arg1;
+                        let string0 = String::from(
+                            std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap(),
+                        );
+                        let result = T::async_forward(string0).await;
+                        result
+                    };
+                    let result = wit_bindgen_symmetric_rt::async_support::first_poll(result, move |result1| {
+                        let vec2 = (result1.into_bytes()).into_boxed_slice();
+                        let ptr2 = vec2.as_ptr().cast::<u8>();
+                        let len2 = vec2.len();
+                        ::core::mem::forget(vec2);
+                        let output = arg2.cast::<usize>();
+                            *unsafe { &mut *output } = ptr2 as usize;
+                            *unsafe { &mut *output.add(1) } = len2;
+                    });
+                    result.cast()
+                }
+                pub trait Guest {
+                    async fn async_forward(s: _rt::String) -> _rt::String;
+                }
+                #[doc(hidden)]
+
+                macro_rules! __export_test_test_string_delay_cabi{
+        ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+          #[cfg_attr(target_arch = "wasm32", export_name = "[async]forward")]
+          #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+          unsafe extern "C" fn testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(arg0: *mut u8,arg1: usize,arg2: *mut u8,) -> *mut u8 {
+            $($path_to_types)*::_export_async_forward_cabi::<$ty>(arg0, arg1, arg2)
+          }
+        };);
+      }
+                #[doc(hidden)]
+                pub(crate) use __export_test_test_string_delay_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+
+    pub fn as_i64<T: AsI64>(t: T) -> i64 {
+        t.as_i64()
+    }
+
+    pub trait AsI64 {
+        fn as_i64(self) -> i64;
+    }
+
+    impl<'a, T: Copy + AsI64> AsI64 for &'a T {
+        fn as_i64(self) -> i64 {
+            (*self).as_i64()
+        }
+    }
+
+    impl AsI64 for i64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+
+    impl AsI64 for u64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    pub use alloc_crate::string::String;
+    pub use alloc_crate::vec::Vec;
+    extern crate alloc as alloc_crate;
+}
+
+/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as
+/// the root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_async_module_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::test::test::string_delay::__export_test_test_string_delay_cabi!($ty with_types_in $($path_to_types_root)*::exports::test::test::string_delay);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_async_module_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[unsafe(link_section = "component-type:wit-bindgen:0.40.0:test:test:async-module:encoded world")]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 278] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x93\x01\x01A\x02\x01\
+A\x04\x01B\x02\x01@\x01\x0bnanosecondsw\x01\0\x04\0\x0c[async]sleep\x01\0\x03\0\x0e\
+test:test/wait\x05\0\x01B\x02\x01@\x01\x01ss\0s\x04\0\x0e[async]forward\x01\0\x04\
+\0\x16test:test/string-delay\x05\x01\x04\0\x16test:test/async-module\x04\0\x0b\x12\
+\x01\0\x0casync-module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-co\
+mponent\x070.227.1\x10wit-bindgen-rust\x060.40.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs
new file mode 100644
index 000000000..9973e2af8
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs
@@ -0,0 +1,21 @@
+mod async_module;
+
+async_module::export!(Guest with_types_in async_module);
+
+struct Guest;
+
+impl async_module::exports::test::test::string_delay::Guest for Guest {
+    async fn async_forward(s: String) -> String {
+        match s.as_str() {
+            "A" => "directly returned".into(),
+            "B" => {
+                async_module::test::test::wait::async_sleep(5_000_000_000).await;
+                "after five seconds".into()
+            }
+            _ => {
+                async_module::test::test::wait::async_sleep(1_000_000_000).await;
+                "after one second".into()
+            }
+        }
+    }
+}
diff --git a/crates/cpp/tests/symmetric_async/generate.sh b/crates/cpp/tests/symmetric_async/generate.sh
new file mode 100755
index 000000000..ac4e4230c
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/generate.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+(cd async_module/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_module.wit --async all --symmetric)
+cargo fmt
diff --git a/crates/cpp/tests/symmetric_async/main/Cargo.toml b/crates/cpp/tests/symmetric_async/main/Cargo.toml
new file mode 100644
index 000000000..88a81ae31
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/main/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "main"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+async_module = { path = "../async_module" }
+symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"] }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
diff --git a/crates/cpp/tests/symmetric_async/main/build.rs b/crates/cpp/tests/symmetric_async/main/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/main/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_async/main/src/main.rs b/crates/cpp/tests/symmetric_async/main/src/main.rs
new file mode 100644
index 000000000..8b1dd8e61
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/main/src/main.rs
@@ -0,0 +1,67 @@
+use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription};
+
+#[link(name = "async_module")]
+extern "C" {
+    pub fn testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(
+        addr: *const u8,
+        len: usize,
+        results: *mut (),
+    ) -> *mut ();
+}
+
+extern "C" fn print_result(obj: *mut ()) -> CallbackState {
+    let addrptr = unsafe { *obj.cast::<*mut u8>() };
+    let lenptr = unsafe { obj.byte_add(core::mem::size_of::<*const u8>()) };
+    let len = unsafe { *lenptr.cast::<usize>() };
+    let vec = unsafe { Vec::from_raw_parts(addrptr, len, len) };
+    let string = std::str::from_utf8(&vec).unwrap();
+    println!("Result {string}");
+    CallbackState::Ready
+}
+
+fn main() {
+    let mut result1: [usize; 2] = [0, 0];
+    let handle1 = unsafe {
+        testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(
+            "A".as_ptr(),
+            1,
+            result1.as_mut_ptr().cast(),
+        )
+    };
+    assert_eq!(handle1, core::ptr::null_mut());
+    let vec = unsafe { Vec::from_raw_parts(result1[0] as *mut u8, result1[1], result1[1]) };
+    let string = std::str::from_utf8(&vec).unwrap();
+    println!("Result {string}");
+
+    let mut result2: [usize; 2] = [0, 0];
+    let handle2 = unsafe {
+        testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(
+            "B".as_ptr(),
+            1,
+            result2.as_mut_ptr().cast(),
+        )
+    };
+    assert_ne!(handle2, core::ptr::null_mut());
+    wit_bindgen_symmetric_rt::register(
+        unsafe { EventSubscription::from_handle(handle2 as usize) },
+        print_result,
+        result2.as_mut_ptr().cast(),
+    );
+
+    let mut result3: [usize; 2] = [0, 0];
+    let handle3 = unsafe {
+        testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(
+            "C".as_ptr(),
+            1,
+            result3.as_mut_ptr().cast(),
+        )
+    };
+    assert_ne!(handle3, core::ptr::null_mut());
+    wit_bindgen_symmetric_rt::register(
+        unsafe { EventSubscription::from_handle(handle3 as usize) },
+        print_result,
+        result3.as_mut_ptr().cast(),
+    );
+
+    wit_bindgen_symmetric_rt::run();
+}
diff --git a/crates/cpp/tests/symmetric_async/sleep/Cargo.toml b/crates/cpp/tests/symmetric_async/sleep/Cargo.toml
new file mode 100644
index 000000000..8d73cef54
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/sleep/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "sleep"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+symmetric_executor = { path = "../../../../symmetric_executor" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_async/sleep/build.rs b/crates/cpp/tests/symmetric_async/sleep/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/sleep/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs
new file mode 100644
index 000000000..565ac1eda
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs
@@ -0,0 +1,16 @@
+#[link(name = "symmetric_executor")]
+extern "C" {
+    fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(
+        nanoseconds: u64,
+    ) -> *mut ();
+}
+
+#[no_mangle]
+unsafe extern "C" fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(
+    nanoseconds: u64,
+    // args: *const (),
+    // _results: *mut (),
+) -> *mut () {
+    // let nanoseconds = *args.cast::<u64>();
+    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(nanoseconds)
+}
diff --git a/crates/cpp/tests/symmetric_async/wit/async_module.wit b/crates/cpp/tests/symmetric_async/wit/async_module.wit
new file mode 100644
index 000000000..76d826324
--- /dev/null
+++ b/crates/cpp/tests/symmetric_async/wit/async_module.wit
@@ -0,0 +1,14 @@
+package test:test;
+
+interface string-delay {
+    forward: async func(s: string) -> string;
+}
+
+interface wait {
+    sleep: async func(nanoseconds: u64);
+}
+
+world async-module {
+    import wait;
+    export string-delay;
+}
diff --git a/crates/cpp/tests/symmetric_future/Cargo.lock b/crates/cpp/tests/symmetric_future/Cargo.lock
new file mode 100644
index 000000000..68fd3f217
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/Cargo.lock
@@ -0,0 +1,226 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "dummy-rt"
+version = "0.1.0"
+
+[[package]]
+name = "future"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "source",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "main"
+version = "0.1.0"
+dependencies = [
+ "future",
+ "symmetric_executor",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "source"
+version = "0.1.0"
+dependencies = [
+ "symmetric_executor",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "symmetric_executor"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "libc",
+]
+
+[[package]]
+name = "symmetric_stream"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "symmetric_executor",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
+
+[[package]]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+]
diff --git a/crates/cpp/tests/symmetric_future/Cargo.toml b/crates/cpp/tests/symmetric_future/Cargo.toml
new file mode 100644
index 000000000..6b29008fd
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+members = [ "future","main", "source"]
+resolver="2"
diff --git a/crates/cpp/tests/symmetric_future/future/Cargo.toml b/crates/cpp/tests/symmetric_future/future/Cargo.toml
new file mode 100644
index 000000000..e011077b4
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/future/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "future"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+futures = "0.3.31"
+source = { path = "../source" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "../../../../symmetric_executor/dummy-rt"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_future/future/build.rs b/crates/cpp/tests/symmetric_future/future/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/future/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs
new file mode 100644
index 000000000..4d075cb46
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs
@@ -0,0 +1,214 @@
+// Generated by `wit-bindgen` 0.40.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod test {
+    pub mod test {
+
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod future_source {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader<u32> {
+                unsafe {
+                    #[link(wasm_import_module = "test:test/future-source")]
+                    #[link(name = "source")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "create")]
+                        fn testX3AtestX2Ffuture_sourceX00create() -> usize;
+                    }
+                    let ret = testX3AtestX2Ffuture_sourceX00create();
+                    wit_bindgen_symmetric_rt::async_support::FutureReader::new(
+                        wit_bindgen_symmetric_rt::async_support::Stream::from_handle(ret),
+                    )
+                }
+            }
+        }
+    }
+}
+#[allow(dead_code, clippy::all)]
+pub mod exports {
+    pub mod test {
+        pub mod test {
+
+            #[allow(dead_code, unused_imports, clippy::all)]
+            pub mod future_test {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+
+                use super::super::super::super::_rt;
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_create_cabi<T: Guest>() -> usize {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::create();
+                    (result0).take_handle() as usize
+                }
+                pub trait Guest {
+                    fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader<u32>;
+                }
+                #[doc(hidden)]
+
+                macro_rules! __export_test_test_future_test_cabi{
+        ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+          #[cfg_attr(target_arch = "wasm32", export_name = "create")]
+          #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+          unsafe extern "C" fn testX3AtestX2Ffuture_testX00create() -> usize {
+            $($path_to_types)*::_export_create_cabi::<$ty>()
+          }
+        };);
+      }
+                #[doc(hidden)]
+                pub(crate) use __export_test_test_future_test_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+
+    pub fn as_i32<T: AsI32>(t: T) -> i32 {
+        t.as_i32()
+    }
+
+    pub trait AsI32 {
+        fn as_i32(self) -> i32;
+    }
+
+    impl<'a, T: Copy + AsI32> AsI32 for &'a T {
+        fn as_i32(self) -> i32 {
+            (*self).as_i32()
+        }
+    }
+
+    impl AsI32 for i32 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for u32 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for i16 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for u16 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for i8 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for u8 {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for char {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+
+    impl AsI32 for usize {
+        #[inline]
+        fn as_i32(self) -> i32 {
+            self as i32
+        }
+    }
+    pub use alloc_crate::boxed::Box;
+
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen_symmetric_rt::run_ctors_once();
+    }
+    extern crate alloc as alloc_crate;
+}
+pub mod wit_future {
+    #![allow(dead_code, unused_variables, clippy::all)]
+
+    #[doc(hidden)]
+    pub trait FuturePayload: Unpin + Sized + 'static {}
+    impl FuturePayload for u32 {}
+    /// Creates a new Component Model `future` with the specified payload type.
+    pub fn new<T: FuturePayload>() -> (
+        wit_bindgen_symmetric_rt::async_support::FutureWriter<T>,
+        wit_bindgen_symmetric_rt::async_support::FutureReader<T>,
+    ) {
+        wit_bindgen_symmetric_rt::async_support::future_support::new_future()
+    }
+}
+
+/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as
+/// the root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_future_world_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::test::test::future_test::__export_test_test_future_test_cabi!($ty with_types_in $($path_to_types_root)*::exports::test::test::future_test);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_future_world_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[unsafe(link_section = "component-type:wit-bindgen:0.40.0:test:test:future-world:encoded world")]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 264] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x85\x01\x01A\x02\x01\
+A\x04\x01B\x03\x01e\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/f\
+uture-source\x05\0\x01B\x03\x01e\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x15\
+test:test/future-test\x05\x01\x04\0\x16test:test/future-world\x04\0\x0b\x12\x01\0\
+\x0cfuture-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-componen\
+t\x070.227.1\x10wit-bindgen-rust\x060.40.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs
new file mode 100644
index 000000000..1ca28dcfd
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs
@@ -0,0 +1,20 @@
+mod future_world;
+
+use future_world::test::test::future_source;
+use wit_bindgen_symmetric_rt::async_support;
+
+future_world::export!(MyStruct with_types_in future_world);
+
+struct MyStruct;
+
+impl future_world::exports::test::test::future_test::Guest for MyStruct {
+    fn create() -> async_support::FutureReader<u32> {
+        let (write, read) = async_support::future_support::new_future();
+        let input = future_source::create();
+        async_support::spawn(async move {
+            let input = input.await.unwrap();
+            write.write(input * 2).await;
+        });
+        read
+    }
+}
diff --git a/crates/cpp/tests/symmetric_future/generate.sh b/crates/cpp/tests/symmetric_future/generate.sh
new file mode 100755
index 000000000..43a301f69
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/generate.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --async none --symmetric)
+cargo fmt
diff --git a/crates/cpp/tests/symmetric_future/main/Cargo.toml b/crates/cpp/tests/symmetric_future/main/Cargo.toml
new file mode 100644
index 000000000..82d68f2f1
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/main/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "main"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+future = { path = "../future" }
+symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"]}
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
diff --git a/crates/cpp/tests/symmetric_future/main/build.rs b/crates/cpp/tests/symmetric_future/main/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/main/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_future/main/src/main.rs b/crates/cpp/tests/symmetric_future/main/src/main.rs
new file mode 100644
index 000000000..55846d1f7
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/main/src/main.rs
@@ -0,0 +1,52 @@
+use wit_bindgen_symmetric_rt::{
+    async_support::Stream,
+    symmetric_stream::{Address, Buffer},
+    CallbackState,
+};
+
+#[link(name = "future")]
+extern "C" {
+    pub fn testX3AtestX2Ffuture_testX00create() -> usize;
+}
+
+struct CallbackInfo {
+    stream: Stream,
+    data: u32,
+}
+
+extern "C" fn ready(arg: *mut ()) -> CallbackState {
+    let info = unsafe { &*arg.cast::<CallbackInfo>() };
+    let buffer = info.stream.read_result();
+    if let Some(buffer) = buffer {
+        let len = buffer.get_size();
+        if len > 0 {
+            println!("data {}", info.data);
+        }
+    }
+    // finished
+    CallbackState::Ready
+}
+
+fn main() {
+    let result_future = unsafe { testX3AtestX2Ffuture_testX00create() };
+    // function should have completed (not async)
+    // assert!(continuation.is_null());
+    let stream = unsafe { Stream::from_handle(result_future) };
+    let mut info = Box::pin(CallbackInfo {
+        stream: stream.clone(),
+        data: 0,
+    });
+    let buffer = Buffer::new(
+        unsafe { Address::from_handle(&mut info.data as *mut u32 as usize) },
+        1,
+    );
+    stream.start_reading(buffer);
+    let subscription = stream.read_ready_subscribe();
+    println!("Register read in main");
+    wit_bindgen_symmetric_rt::register(
+        subscription,
+        ready,
+        (&*info as *const CallbackInfo).cast_mut().cast(),
+    );
+    wit_bindgen_symmetric_rt::run();
+}
diff --git a/crates/cpp/tests/symmetric_future/source/Cargo.toml b/crates/cpp/tests/symmetric_future/source/Cargo.toml
new file mode 100644
index 000000000..995658bad
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/source/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "source"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+symmetric_executor = { path = "../../../../symmetric_executor" }
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_future/source/build.rs b/crates/cpp/tests/symmetric_future/source/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/source/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_future/source/src/lib.rs b/crates/cpp/tests/symmetric_future/source/src/lib.rs
new file mode 100644
index 000000000..832dcb40f
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/source/src/lib.rs
@@ -0,0 +1,31 @@
+use wit_bindgen_symmetric_rt::{async_support::Stream, register, CallbackState, EventSubscription};
+
+extern "C" fn timer_call(data: *mut ()) -> CallbackState {
+    let stream: Stream = unsafe { Stream::from_handle(data as usize) };
+    let buffer = stream.start_writing();
+    let addr = buffer.get_address().take_handle() as *mut u32;
+    let size = buffer.capacity();
+    assert!(size >= 1);
+    *unsafe { &mut *addr } = 21;
+    buffer.set_size(1);
+    stream.finish_writing(Some(buffer));
+    // let _ = stream.take_handle();
+    CallbackState::Ready
+}
+
+extern "C" fn write_ready(data: *mut ()) -> CallbackState {
+    println!("we can write now, starting timer");
+    let ms_30 = EventSubscription::from_timeout(30 * 1_000_000);
+    register(ms_30, timer_call, data);
+    CallbackState::Ready
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub fn testX3AtestX2Ffuture_sourceX00create() -> usize {
+    let stream = Stream::new();
+    let event = stream.write_ready_subscribe();
+    let stream_copy = stream.clone();
+    register(event, write_ready, stream_copy.take_handle() as *mut ());
+    stream.take_handle()
+}
diff --git a/crates/cpp/tests/symmetric_future/wit/future.wit b/crates/cpp/tests/symmetric_future/wit/future.wit
new file mode 100644
index 000000000..d59ccbb68
--- /dev/null
+++ b/crates/cpp/tests/symmetric_future/wit/future.wit
@@ -0,0 +1,14 @@
+package test:test;
+
+interface future-source {
+    create: func() -> future<u32>;
+}
+
+interface future-test {
+    create: func() -> future<u32>;
+}
+
+world future-world {
+    import future-source;
+    export future-test;
+}
diff --git a/crates/cpp/tests/symmetric_lists/rust_a/Cargo.toml b/crates/cpp/tests/symmetric_lists/rust_a/Cargo.toml
new file mode 100644
index 000000000..d4aa7a836
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_a/Cargo.toml
@@ -0,0 +1,10 @@
+[workspace]
+
+[package]
+name = "rust_a"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+rust_b = { path = "../rust_b" }
+wit-bindgen = { path = "../../../../guest-rust" }
diff --git a/crates/cpp/tests/symmetric_lists/rust_a/Makefile b/crates/cpp/tests/symmetric_lists/rust_a/Makefile
new file mode 100644
index 000000000..196b4dd88
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_a/Makefile
@@ -0,0 +1,3 @@
+
+bindgen:
+	(cd src; ../../../../../../target/debug/wit-bindgen rust ../../wit/lists.wit -w x --generate-all --symmetric)
diff --git a/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs b/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs
new file mode 100644
index 000000000..4f5f0a6b8
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs
@@ -0,0 +1,22 @@
+// compile with: RUSTFLAGS=-L../rust_b/target/debug cargo build
+
+mod x;
+
+// force linking to librust_b.so
+#[allow(dead_code)]
+fn b() {
+    #[link(name = "rust_b")]
+    extern "C" {
+        fn testX3AtestX2FiX00f(_: *mut u8, _: usize, _: *mut u8);
+    }
+    unsafe { testX3AtestX2FiX00f(core::ptr::null_mut(), 0, core::ptr::null_mut()) };
+}
+
+fn main() {
+    let input = vec!["hello".into(), "world".into()];
+    let output = x::test::test::i::f(&input);
+    println!("{output:?}");
+    let input2 = vec![1,2,3];
+    let output2 = x::test::test::i::g(&input2);
+    println!("{output2:?}");
+}
diff --git a/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs b/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs
new file mode 100644
index 000000000..bd8902872
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs
@@ -0,0 +1,153 @@
+// Generated by `wit-bindgen` 0.30.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code)]
+pub mod test {
+    #[allow(dead_code)]
+    pub mod test {
+        #[allow(dead_code, clippy::all)]
+        pub mod i {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn f(a: &[_rt::String]) -> _rt::Vec<_rt::String> {
+                unsafe {
+                    #[cfg_attr(target_pointer_width = "64", repr(align(8)))]
+                    #[cfg_attr(target_pointer_width = "32", repr(align(4)))]
+                    struct RetArea(
+                        [::core::mem::MaybeUninit<u8>; 2 * core::mem::size_of::<*const u8>()],
+                    );
+                    let mut ret_area = RetArea(
+                        [::core::mem::MaybeUninit::uninit(); 2 * core::mem::size_of::<*const u8>()],
+                    );
+                    let vec1 = a;
+                    let len1 = vec1.len();
+                    let layout1 = _rt::alloc::Layout::from_size_align_unchecked(
+                        vec1.len() * (2 * core::mem::size_of::<*const u8>()),
+                        core::mem::size_of::<*const u8>(),
+                    );
+                    let result1 = if layout1.size() != 0 {
+                        let ptr = _rt::alloc::alloc(layout1).cast::<u8>();
+                        if ptr.is_null() {
+                            _rt::alloc::handle_alloc_error(layout1);
+                        }
+                        ptr
+                    } else {
+                        ::core::ptr::null_mut()
+                    };
+                    for (i, e) in vec1.into_iter().enumerate() {
+                        let base = result1.add(i * (2 * core::mem::size_of::<*const u8>()));
+                        {
+                            let vec0 = e;
+                            let ptr0 = vec0.as_ptr().cast::<u8>();
+                            let len0 = vec0.len();
+                            *base.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len0;
+                            *base.add(0).cast::<*mut u8>() = ptr0.cast_mut();
+                        }
+                    }
+                    let ptr2 = ret_area.0.as_mut_ptr().cast::<u8>();
+                    #[link(wasm_import_module = "test:test/i")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "f")]
+                        fn testX3AtestX2FiX00f(_: *mut u8, _: usize, _: *mut u8);
+                    }
+                    testX3AtestX2FiX00f(result1, len1, ptr2);
+                    _rt::alloc::dealloc(result1, layout1);
+                    let l3 = *ptr2.add(0).cast::<*mut u8>();
+                    let l4 = *ptr2.add(core::mem::size_of::<*const u8>()).cast::<usize>();
+                    let base8 = l3;
+                    let len8 = l4;
+                    let mut result8 = _rt::Vec::with_capacity(len8);
+                    for i in 0..len8 {
+                        let base = base8.add(i * (2 * core::mem::size_of::<*const u8>()));
+                        let e8 = {
+                            let l5 = *base.add(0).cast::<*mut u8>();
+                            let l6 = *base.add(core::mem::size_of::<*const u8>()).cast::<usize>();
+                            let len7 = l6;
+                            let bytes7 = if len7 > 0 {
+                                _rt::Vec::from_raw_parts(l5.cast(), len7, len7)
+                            } else {
+                                Default::default()
+                            };
+
+                            _rt::string_lift(bytes7)
+                        };
+                        result8.push(e8);
+                    }
+                    _rt::cabi_dealloc(
+                        base8,
+                        len8 * (2 * core::mem::size_of::<*const u8>()),
+                        core::mem::size_of::<*const u8>(),
+                    );
+                    result8
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn g(a: &[u8]) -> _rt::Vec<u8> {
+                unsafe {
+                    #[cfg_attr(target_pointer_width = "64", repr(align(8)))]
+                    #[cfg_attr(target_pointer_width = "32", repr(align(4)))]
+                    struct RetArea(
+                        [::core::mem::MaybeUninit<u8>; 2 * core::mem::size_of::<*const u8>()],
+                    );
+                    let mut ret_area = RetArea(
+                        [::core::mem::MaybeUninit::uninit(); 2 * core::mem::size_of::<*const u8>()],
+                    );
+                    let vec0 = a;
+                    let ptr0 = vec0.as_ptr().cast::<u8>();
+                    let len0 = vec0.len();
+                    let ptr1 = ret_area.0.as_mut_ptr().cast::<u8>();
+                    #[link(wasm_import_module = "test:test/i")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "g")]
+                        fn testX3AtestX2FiX00g(_: *mut u8, _: usize, _: *mut u8);
+                    }
+                    testX3AtestX2FiX00g(ptr0.cast_mut(), len0, ptr1);
+                    let l2 = *ptr1.add(0).cast::<*mut u8>();
+                    let l3 = *ptr1.add(core::mem::size_of::<*const u8>()).cast::<usize>();
+                    let len4 = l3;
+                    _rt::Vec::from_raw_parts(l2.cast(), len4, len4)
+                }
+            }
+        }
+    }
+}
+mod _rt {
+    pub use alloc_crate::alloc;
+    pub use alloc_crate::string::String;
+    pub use alloc_crate::vec::Vec;
+    pub unsafe fn string_lift(bytes: Vec<u8>) -> String {
+        if cfg!(debug_assertions) {
+            String::from_utf8(bytes).unwrap()
+        } else {
+            String::from_utf8_unchecked(bytes)
+        }
+    }
+    pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) {
+        if size == 0 {
+            return;
+        }
+        let layout = alloc::Layout::from_size_align_unchecked(size, align);
+        alloc::dealloc(ptr, layout);
+    }
+    extern crate alloc as alloc_crate;
+}
+
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.30.0:test:test:x:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 194] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07K\x01A\x02\x01A\x02\x01\
+B\x06\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\x01p}\x01@\x01\x01a\x02\0\x02\
+\x04\0\x01g\x01\x03\x03\x01\x0btest:test/i\x05\0\x04\x01\x0btest:test/x\x04\0\x0b\
+\x07\x01\0\x01x\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\
+\x070.216.0\x10wit-bindgen-rust\x060.30.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/symmetric_lists/rust_b/Cargo.toml b/crates/cpp/tests/symmetric_lists/rust_b/Cargo.toml
new file mode 100644
index 000000000..8fd19d382
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_b/Cargo.toml
@@ -0,0 +1,12 @@
+[workspace]
+
+[package]
+name = "rust_b"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+wit-bindgen = { path = "../../../../guest-rust" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_lists/rust_b/Makefile b/crates/cpp/tests/symmetric_lists/rust_b/Makefile
new file mode 100644
index 000000000..26fd41ee6
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_b/Makefile
@@ -0,0 +1,3 @@
+
+bindgen:
+	(cd src; ../../../../../../target/debug/wit-bindgen rust ../../wit/lists.wit -w w --generate-all --symmetric)
diff --git a/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs b/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs
new file mode 100644
index 000000000..ebecc3949
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs
@@ -0,0 +1,15 @@
+mod w;
+
+struct MyImpl;
+
+impl w::exports::test::test::i::Guest for MyImpl {
+    fn f(a: Vec<String>) -> Vec<String> {
+        a
+    }
+
+    fn g(a: Vec<u8>) -> Vec<u8> {
+        a
+    }
+}
+
+w::export!(MyImpl with_types_in w);
diff --git a/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs
new file mode 100644
index 000000000..e660ea1d6
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs
@@ -0,0 +1,156 @@
+#[allow(dead_code)]
+pub mod exports {
+    #[allow(dead_code)]
+    pub mod test {
+        #[allow(dead_code)]
+        pub mod test {
+            #[allow(dead_code, clippy::all)]
+            pub mod i {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+                use super::super::super::super::_rt;
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_f_cabi<T: Guest>(arg0: *mut u8, arg1: usize, arg2: *mut u8) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let base3 = arg0;
+                    let len3 = arg1;
+                    let mut result3 = _rt::Vec::with_capacity(len3);
+                    for i in 0..len3 {
+                        let base = base3.add(i * (2 * core::mem::size_of::<*const u8>()));
+                        let e3 = {
+                            let l0 = *base.add(0).cast::<*mut u8>();
+                            let l1 = *base.add(core::mem::size_of::<*const u8>()).cast::<usize>();
+                            let len2 = l1;
+                            let string2 = String::from(
+                                std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(),
+                            );
+                            string2
+                        };
+                        result3.push(e3);
+                    }
+                    let result4 = T::f(result3);
+                    let vec6 = result4;
+                    let len6 = vec6.len();
+                    let layout6 = _rt::alloc::Layout::from_size_align_unchecked(
+                        vec6.len() * (2 * core::mem::size_of::<*const u8>()),
+                        core::mem::size_of::<*const u8>(),
+                    );
+                    let result6 = if layout6.size() != 0 {
+                        let ptr = _rt::alloc::alloc(layout6).cast::<u8>();
+                        if ptr.is_null() {
+                            _rt::alloc::handle_alloc_error(layout6);
+                        }
+                        ptr
+                    } else {
+                        ::core::ptr::null_mut()
+                    };
+                    for (i, e) in vec6.into_iter().enumerate() {
+                        let base = result6.add(i * (2 * core::mem::size_of::<*const u8>()));
+                        {
+                            let vec5 = (e.into_bytes()).into_boxed_slice();
+                            let ptr5 = vec5.as_ptr().cast::<u8>();
+                            let len5 = vec5.len();
+                            ::core::mem::forget(vec5);
+                            *base.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len5;
+                            *base.add(0).cast::<*mut u8>() = ptr5.cast_mut();
+                        }
+                    }
+                    *arg2.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len6;
+                    *arg2.add(0).cast::<*mut u8>() = result6;
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_g_cabi<T: Guest>(arg0: *mut u8, arg1: usize, arg2: *mut u8) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let len0 = arg1;
+                    let result1 =
+                        T::g(unsafe { std::slice::from_raw_parts(arg0.cast(), len0) }.to_vec());
+                    let vec2 = (result1).into_boxed_slice();
+                    let ptr2 = vec2.as_ptr().cast::<u8>();
+                    let len2 = vec2.len();
+                    ::core::mem::forget(vec2);
+                    *arg2.add(core::mem::size_of::<*const u8>()).cast::<usize>() = len2;
+                    *arg2.add(0).cast::<*mut u8>() = ptr2.cast_mut();
+                }
+                pub trait Guest {
+                    fn f(a: _rt::Vec<_rt::String>) -> _rt::Vec<_rt::String>;
+                    fn g(a: _rt::Vec<u8>) -> _rt::Vec<u8>;
+                }
+                #[doc(hidden)]
+                macro_rules! __export_test_test_i_cabi {
+                    ($ty:ident with_types_in $($path_to_types:tt)*) => {
+                        const _ : () = { #[cfg_attr(target_arch = "wasm32", export_name =
+                        "f")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe
+                        extern "C" fn testX3AtestX2FiX00f(arg0 : * mut u8, arg1 : usize,
+                        arg2 : * mut u8,) { $($path_to_types)*:: _export_f_cabi::<$ty >
+                        (arg0, arg1, arg2) } #[cfg_attr(target_arch = "wasm32",
+                        export_name = "g")] #[cfg_attr(not(target_arch = "wasm32"),
+                        no_mangle)] unsafe extern "C" fn testX3AtestX2FiX00g(arg0 : * mut
+                        u8, arg1 : usize, arg2 : * mut u8,) { $($path_to_types)*::
+                        _export_g_cabi::<$ty > (arg0, arg1, arg2) } };
+                    };
+                }
+                #[doc(hidden)]
+                pub(crate) use __export_test_test_i_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    pub use alloc_crate::alloc;
+    pub use alloc_crate::string::String;
+    pub use alloc_crate::vec::Vec;
+    extern crate alloc as alloc_crate;
+}
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! __export_w_impl {
+    ($ty:ident) => {
+        self::export!($ty with_types_in self);
+    };
+    ($ty:ident with_types_in $($path_to_types_root:tt)*) => {
+        $($path_to_types_root)*:: exports::test::test::i::__export_test_test_i_cabi!($ty
+        with_types_in $($path_to_types_root)*:: exports::test::test::i);
+    };
+}
+#[doc(inline)]
+pub(crate) use __export_w_impl as export;
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.30.0:test:test:w:encoded world"]
+#[doc(hidden)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 194] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07K\x01A\x02\x01A\x02\x01\
+B\x06\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\x01p}\x01@\x01\x01a\x02\0\x02\
+\x04\0\x01g\x01\x03\x04\x01\x0btest:test/i\x05\0\x04\x01\x0btest:test/w\x04\0\x0b\
+\x07\x01\0\x01w\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\
+\x070.216.0\x10wit-bindgen-rust\x060.30.0";
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/symmetric_lists/wit/lists.wit b/crates/cpp/tests/symmetric_lists/wit/lists.wit
new file mode 100644
index 000000000..05185805c
--- /dev/null
+++ b/crates/cpp/tests/symmetric_lists/wit/lists.wit
@@ -0,0 +1,14 @@
+package test:test;
+
+interface i {
+    f: func(a: list<string>) -> list<string>;
+    g: func(a: list<u8>) -> list<u8>;
+}
+
+world w {
+    export i;
+}
+
+world x {
+    import i;
+}
diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock
new file mode 100644
index 000000000..f8e00cd67
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/Cargo.lock
@@ -0,0 +1,226 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "dummy-rt"
+version = "0.1.0"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.167"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+
+[[package]]
+name = "main"
+version = "0.1.0"
+dependencies = [
+ "stream",
+ "symmetric_executor",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "source"
+version = "0.1.0"
+dependencies = [
+ "symmetric_executor",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "stream"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "source",
+ "symmetric_stream",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "symmetric_executor"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "libc",
+]
+
+[[package]]
+name = "symmetric_stream"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "symmetric_executor",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+]
diff --git a/crates/cpp/tests/symmetric_stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/Cargo.toml
new file mode 100644
index 000000000..dbc97472a
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+members = [ "source", "main", "stream"]
+resolver = "2"
diff --git a/crates/cpp/tests/symmetric_stream/README.md b/crates/cpp/tests/symmetric_stream/README.md
new file mode 100644
index 000000000..4382d75c0
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/README.md
@@ -0,0 +1,70 @@
+# Native stream design
+
+A stream has the following members: 
+
+ - read_ready_event_send: the sending side of the read ready (data written, ready_size contains the number of elements)
+ - write_ready_event_send: the sending side of the write ready (addr and size are set)
+ - read_addr: the address of the registered buffer (valid on write_ready)
+ - read_size: maximum number of elements in the buffer (valid on write_ready)
+ - ready_size: valid number of elements in the buffer (valid on read_ready)
+ - active_instances: Number of references to the stream object, decreased by 
+
+## Special values of ready
+
+ - CLOSED: MIN (0x8000…) also EOF
+ - BLOCKED: -1 (normal)
+ - CANCELLED: 0 (TBD)
+
+## Sequence
+
+"take" means swap with idle value (read_addr=0, read_size=0, ready=-1)
+
+### Read
+
+ - if ready_size is CLOSED: End of file, ready_size should be BLOCKED
+ - if read_addr is set wait for read_ready event
+ - write addr and size
+ - activate write_ready
+ - wait for read_ready
+ - take ready_size and process data
+
+### Write
+
+ - (only initally) on EOF set ready_size to CLOSED
+ - wait for write_ready
+ - on EOF set ready_size to CLOSED
+ - assert addr and size is valid, ready is MIN (blocking)
+    - addr zero likely EOF (reader closed)
+ - take addr and size, write data to buffer
+ - store number of valid elements in ready_size
+ - activate read_ready
+
+## Functions
+
+A vtable is no longer necessary, but some functions enable shared implementations (perhaps interface by WIT?)
+
+ - create_stream: Create a new stream object
+ - start_reading: Register buffer and send event
+ - start_writing: Take and return the buffer
+ - finish_writing: (can also set eof independently of start_write) set available
+   amount and trigger event
+ - read_amount: Take and return the number of valid elements
+ - read/write_ready_event: Sending side of the event
+ - is_ready_to_write: Whether a write needs to wait for write_ready
+ - is_write_closed: Whether the write side closed
+ - close_read/write: Close one side of the stream
+
+### Open questions
+
+ - how to cancel a read?
+   - simply replace addr and size with zero? 
+     If already written nothing to do. How to handle race conditions when
+     destroying the buffer after cancel? Perhaps a roundtrip to close 
+     would be helpful. (activate write_ready, wait read_ready, check for EOF)
+   - add a write_busy flag? If addr is zero and ready BLOCKED, then wait for
+     ready
+ - how to cancel a write?
+   - simply flag EOF and activate read_ready
+ - Is a future the same data structure?
+   - read_size would always be one, ready_size up to one, 
+     finish_writing and read_amount could directly close the side
diff --git a/crates/cpp/tests/symmetric_stream/generate.sh b/crates/cpp/tests/symmetric_stream/generate.sh
new file mode 100755
index 000000000..21107d649
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/generate.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+(cd stream/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_stream.wit --async none --symmetric)
+cargo fmt
diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml
new file mode 100644
index 000000000..0bdf33fa3
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "main"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+stream = { path = "../stream" }
+symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"]}
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
diff --git a/crates/cpp/tests/symmetric_stream/main/build.rs b/crates/cpp/tests/symmetric_stream/main/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/main/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs
new file mode 100644
index 000000000..ac43fe073
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs
@@ -0,0 +1,59 @@
+use wit_bindgen_symmetric_rt::{
+    async_support::Stream,
+    symmetric_stream::{Address, Buffer},
+    CallbackState,
+};
+
+#[link(name = "stream")]
+extern "C" {
+    pub fn testX3AtestX2Fstream_testX00create() -> usize;
+}
+
+const DATALEN: usize = 2;
+
+struct CallbackInfo {
+    stream: Stream,
+    data: [u32; DATALEN],
+}
+
+extern "C" fn ready(arg: *mut ()) -> CallbackState {
+    let info = unsafe { &*arg.cast::<CallbackInfo>() };
+    let buffer = info.stream.read_result();
+    if let Some(buffer) = buffer {
+        let len = buffer.get_size();
+        for i in 0..len as usize {
+            println!("data {}", info.data[i]);
+        }
+        info.stream.start_reading(buffer);
+        // call again
+        CallbackState::Pending
+    } else {
+        // finished
+        CallbackState::Ready
+    }
+}
+
+fn main() {
+    // let mut result_stream: *mut () = core::ptr::null_mut();
+    let result_stream = unsafe { testX3AtestX2Fstream_testX00create() };
+    // function should have completed (not async)
+    // assert!(continuation.is_null());
+    let stream = unsafe { Stream::from_handle(result_stream) };
+    let mut info = Box::pin(CallbackInfo {
+        stream: stream.clone(),
+        data: [0, 0],
+    });
+    let buffer = Buffer::new(
+        unsafe { Address::from_handle(info.data.as_mut_ptr() as usize) },
+        DATALEN as u64,
+    );
+    stream.start_reading(buffer);
+    let subscription = stream.read_ready_subscribe();
+    println!("Register read in main");
+    wit_bindgen_symmetric_rt::register(
+        subscription,
+        ready,
+        (&*info as *const CallbackInfo).cast_mut().cast(),
+    );
+    wit_bindgen_symmetric_rt::run();
+}
diff --git a/crates/cpp/tests/symmetric_stream/source/Cargo.toml b/crates/cpp/tests/symmetric_stream/source/Cargo.toml
new file mode 100644
index 000000000..995658bad
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "source"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+symmetric_executor = { path = "../../../../symmetric_executor" }
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_stream/source/build.rs b/crates/cpp/tests/symmetric_stream/source/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/source/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs
new file mode 100644
index 000000000..b837dd7c6
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs
@@ -0,0 +1,48 @@
+use std::sync::atomic::{AtomicU32, Ordering};
+
+use wit_bindgen_symmetric_rt::{async_support::Stream, register, CallbackState, EventSubscription};
+
+static COUNT: AtomicU32 = AtomicU32::new(1);
+
+extern "C" fn timer_call(data: *mut ()) -> CallbackState {
+    let count = COUNT.fetch_add(1, Ordering::AcqRel);
+    let stream: Stream = unsafe { Stream::from_handle(data as usize) };
+    if count <= 5 {
+        let buffer = stream.start_writing();
+        let addr = buffer.get_address().take_handle() as *mut u32;
+        let size = buffer.capacity();
+        assert!(size >= 1);
+        *unsafe { &mut *addr } = count;
+        buffer.set_size(1);
+        stream.finish_writing(Some(buffer));
+    }
+    let _ = stream.take_handle();
+    CallbackState::Ready
+}
+
+extern "C" fn write_ready(data: *mut ()) -> CallbackState {
+    let count = COUNT.load(Ordering::Acquire);
+    if count > 5 {
+        let stream: Stream = unsafe { Stream::from_handle(data as usize) };
+        // EOF
+        stream.finish_writing(None);
+        CallbackState::Ready
+    } else {
+        if count == 1 {
+            println!("we can write now, starting timer");
+        }
+        let ms_30 = EventSubscription::from_timeout(30 * 1_000_000);
+        register(ms_30, timer_call, data);
+        CallbackState::Pending
+    }
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub fn testX3AtestX2Fstream_sourceX00create() -> usize {
+    let stream = Stream::new();
+    let event = stream.write_ready_subscribe();
+    let stream_copy = stream.clone();
+    register(event, write_ready, stream_copy.take_handle() as *mut ());
+    stream.take_handle()
+}
diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml
new file mode 100644
index 000000000..43a957e03
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "stream"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+futures = "0.3.31"
+source = { path = "../source" }
+#wit-bindgen = { path = "../../../../guest-rust" }
+wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" }
+symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "../../../../symmetric_executor/dummy-rt"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/cpp/tests/symmetric_stream/stream/build.rs b/crates/cpp/tests/symmetric_stream/stream/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/stream/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs
new file mode 100644
index 000000000..88a10e84b
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs
@@ -0,0 +1,26 @@
+use futures::{SinkExt, StreamExt};
+use stream_world::test::test::stream_source::create;
+use wit_bindgen_symmetric_rt::async_support;
+
+mod stream_world;
+
+stream_world::export!(MyStruct with_types_in stream_world);
+
+struct MyStruct;
+
+impl stream_world::exports::test::test::stream_test::Guest for MyStruct {
+    fn create() -> async_support::StreamReader<u32> {
+        let (mut writer, reader) = async_support::stream_support::new_stream();
+        let mut input = create();
+
+        async_support::spawn(async move {
+            while let Some(values) = input.next().await {
+                println!("received {} values", values.len());
+                for value in values {
+                    writer.feed(vec![value, value + 1]).await.unwrap();
+                }
+            }
+        });
+        reader
+    }
+}
diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs
new file mode 100644
index 000000000..705ebcecd
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs
@@ -0,0 +1,142 @@
+// Generated by `wit-bindgen` 0.40.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod test {
+    pub mod test {
+
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod stream_source {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            #[allow(unused_unsafe, clippy::all)]
+            pub fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader<u32> {
+                unsafe {
+                    #[link(wasm_import_module = "test:test/stream-source")]
+                    #[link(name = "source")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "create")]
+                        fn testX3AtestX2Fstream_sourceX00create() -> *mut u8;
+                    }
+                    let ret = testX3AtestX2Fstream_sourceX00create();
+                    wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret)
+                }
+            }
+        }
+    }
+}
+#[allow(dead_code, clippy::all)]
+pub mod exports {
+    pub mod test {
+        pub mod test {
+
+            #[allow(dead_code, unused_imports, clippy::all)]
+            pub mod stream_test {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+
+                use super::super::super::super::_rt;
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_create_cabi<T: Guest>() -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::create();
+                    (result0).take_handle() as *mut u8
+                }
+                pub trait Guest {
+                    fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader<u32>;
+                }
+                #[doc(hidden)]
+
+                macro_rules! __export_test_test_stream_test_cabi{
+        ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+          #[cfg_attr(target_arch = "wasm32", export_name = "create")]
+          #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+          unsafe extern "C" fn testX3AtestX2Fstream_testX00create() -> *mut u8 {
+            $($path_to_types)*::_export_create_cabi::<$ty>()
+          }
+        };);
+      }
+                #[doc(hidden)]
+                pub(crate) use __export_test_test_stream_test_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+    pub use alloc_crate::alloc;
+    pub use alloc_crate::boxed::Box;
+
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    extern crate alloc as alloc_crate;
+}
+pub mod wit_stream {
+    #![allow(dead_code, unused_variables, clippy::all)]
+
+    pub trait StreamPayload: Unpin + Sized + 'static {}
+    impl StreamPayload for u32 {}
+    /// Creates a new Component Model `stream` with the specified payload type.
+    pub fn new<T: StreamPayload>() -> (
+        wit_bindgen_symmetric_rt::async_support::StreamWriter<T>,
+        wit_bindgen_symmetric_rt::async_support::StreamReader<T>,
+    ) {
+        wit_bindgen_symmetric_rt::async_support::stream_support::new_stream()
+    }
+}
+
+/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as
+/// the root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_stream_world_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::test::test::stream_test::__export_test_test_stream_test_cabi!($ty with_types_in $($path_to_types_root)*::exports::test::test::stream_test);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_stream_world_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[unsafe(link_section = "component-type:wit-bindgen:0.40.0:test:test:stream-world:encoded world")]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 264] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x85\x01\x01A\x02\x01\
+A\x04\x01B\x03\x01f\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/s\
+tream-source\x05\0\x01B\x03\x01f\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x15\
+test:test/stream-test\x05\x01\x04\0\x16test:test/stream-world\x04\0\x0b\x12\x01\0\
+\x0cstream-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-componen\
+t\x070.227.1\x10wit-bindgen-rust\x060.40.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/cpp/tests/symmetric_stream/wit/async_stream.wit b/crates/cpp/tests/symmetric_stream/wit/async_stream.wit
new file mode 100644
index 000000000..f3d71d4d0
--- /dev/null
+++ b/crates/cpp/tests/symmetric_stream/wit/async_stream.wit
@@ -0,0 +1,14 @@
+package test:test;
+
+interface stream-source {
+    create: func() -> stream<u32>;
+}
+
+interface stream-test {
+    create: func() -> stream<u32>;
+}
+
+world stream-world {
+    import stream-source;
+    export stream-test;
+}
diff --git a/crates/cpp/tests/symmetric_tests/flavorful.rs b/crates/cpp/tests/symmetric_tests/flavorful.rs
new file mode 100644
index 000000000..68a5c993e
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/flavorful.rs
@@ -0,0 +1,150 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/flavorful",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+use std::sync::atomic::AtomicBool;
+
+use exports::test::flavorful::test as test_imports;
+use test::flavorful::test::*;
+
+#[derive(Default)]
+pub struct MyExports;
+
+static ERRORED: AtomicBool = AtomicBool::new(false);
+
+impl exports::test::flavorful::test::Guest for MyExports {
+    fn f_list_in_record1(ty: test_imports::ListInRecord1) {
+        assert_eq!(ty.a, "list_in_record1");
+    }
+
+    fn f_list_in_record2() -> test_imports::ListInRecord2 {
+        test_imports::ListInRecord2 {
+            a: "list_in_record2".to_string(),
+        }
+    }
+
+    fn f_list_in_record3(a: test_imports::ListInRecord3) -> test_imports::ListInRecord3 {
+        assert_eq!(a.a, "list_in_record3 input");
+        test_imports::ListInRecord3 {
+            a: "list_in_record3 output".to_string(),
+        }
+    }
+
+    fn f_list_in_record4(a: test_imports::ListInAlias) -> test_imports::ListInAlias {
+        assert_eq!(a.a, "input4");
+        test_imports::ListInRecord4 {
+            a: "result4".to_string(),
+        }
+    }
+
+    fn f_list_in_variant1(a: test_imports::ListInVariant1V1, b: test_imports::ListInVariant1V2) {
+        assert_eq!(a.unwrap(), "foo");
+        assert_eq!(b.unwrap_err(), "bar");
+    }
+
+    fn f_list_in_variant2() -> Option<String> {
+        Some("list_in_variant2".to_string())
+    }
+
+    fn f_list_in_variant3(a: test_imports::ListInVariant3) -> Option<String> {
+        assert_eq!(a.unwrap(), "input3");
+        Some("output3".to_string())
+    }
+
+    fn errno_result() -> Result<(), test_imports::MyErrno> {
+        if ERRORED.load(std::sync::atomic::Ordering::SeqCst) {
+            return Ok(());
+        }
+        test_imports::MyErrno::A.to_string();
+        format!("{:?}", test_imports::MyErrno::A);
+        fn assert_error<T: std::error::Error>() {}
+        assert_error::<test_imports::MyErrno>();
+        ERRORED.store(true, std::sync::atomic::Ordering::SeqCst);
+        Err(test_imports::MyErrno::B)
+    }
+
+    fn list_typedefs(
+        a: test_imports::ListTypedef,
+        b: test_imports::ListTypedef3,
+    ) -> (test_imports::ListTypedef2, test_imports::ListTypedef3) {
+        assert_eq!(a, "typedef1");
+        assert_eq!(b.len(), 1);
+        assert_eq!(b[0], "typedef2");
+        (b"typedef3".to_vec(), vec!["typedef4".to_string()])
+    }
+
+    fn list_of_variants(
+        bools: Vec<bool>,
+        results: Vec<Result<(), ()>>,
+        enums: Vec<test_imports::MyErrno>,
+    ) -> (Vec<bool>, Vec<Result<(), ()>>, Vec<test_imports::MyErrno>) {
+        assert_eq!(bools, [true, false]);
+        assert_eq!(results, [Ok(()), Err(())]);
+        assert_eq!(
+            enums,
+            [test_imports::MyErrno::Success, test_imports::MyErrno::A]
+        );
+        (
+            vec![false, true],
+            vec![Err(()), Ok(())],
+            vec![test_imports::MyErrno::A, test_imports::MyErrno::B],
+        )
+    }
+}
+
+pub fn main() {
+    test_imports();
+    // let exports = exports.test_flavorful_test();
+
+    f_list_in_record1(&ListInRecord1 {
+        a: "list_in_record1".to_string(),
+    });
+    assert_eq!(f_list_in_record2().a, "list_in_record2");
+
+    assert_eq!(
+        f_list_in_record3(&ListInRecord3 {
+            a: "list_in_record3 input".to_string()
+        })
+        .a,
+        "list_in_record3 output"
+    );
+
+    assert_eq!(
+        f_list_in_record4(&ListInAlias {
+            a: "input4".to_string()
+        })
+        .a,
+        "result4"
+    );
+
+    f_list_in_variant1(&Some("foo".to_string()), &Err("bar".to_string()));
+    assert_eq!(f_list_in_variant2(), Some("list_in_variant2".to_string()));
+    assert_eq!(
+        f_list_in_variant3(&Some("input3".to_string())),
+        Some("output3".to_string())
+    );
+
+    assert!(errno_result().is_err());
+    MyErrno::A.to_string();
+    format!("{:?}", MyErrno::A);
+    fn assert_error<T: std::error::Error>() {}
+    assert_error::<MyErrno>();
+
+    let (a, b) = list_typedefs(&"typedef1".to_string(), &vec!["typedef2".to_string()]);
+    assert_eq!(a, b"typedef3");
+    assert_eq!(b.len(), 1);
+    assert_eq!(b[0], "typedef4");
+    {
+        #[link(name = "flavorful")]
+        extern "C" {
+            fn test_imports();
+        }
+        let _ = || {
+            unsafe { test_imports() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/lists.rs b/crates/cpp/tests/symmetric_tests/lists.rs
new file mode 100644
index 000000000..4e91a6ed5
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/lists.rs
@@ -0,0 +1,134 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/lists",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+impl exports::test::lists::test::Guest for MyExports {
+    fn empty_list_param(a: Vec<u8>) {
+        assert!(a.is_empty());
+    }
+
+    fn empty_string_param(a: String) {
+        assert_eq!(a, "");
+    }
+
+    fn empty_list_result() -> Vec<u8> {
+        Vec::new()
+    }
+
+    fn empty_string_result() -> String {
+        String::new()
+    }
+
+    fn list_param(list: Vec<u8>) {
+        assert_eq!(list, [1, 2, 3, 4]);
+    }
+
+    fn list_param2(ptr: String) {
+        assert_eq!(ptr, "foo");
+    }
+
+    fn list_param3(ptr: Vec<String>) {
+        assert_eq!(ptr.len(), 3);
+        assert_eq!(ptr[0], "foo");
+        assert_eq!(ptr[1], "bar");
+        assert_eq!(ptr[2], "baz");
+    }
+
+    fn list_param4(ptr: Vec<Vec<String>>) {
+        assert_eq!(ptr.len(), 2);
+        assert_eq!(ptr[0][0], "foo");
+        assert_eq!(ptr[0][1], "bar");
+        assert_eq!(ptr[1][0], "baz");
+    }
+
+    fn list_result() -> Vec<u8> {
+        vec![1, 2, 3, 4, 5]
+    }
+
+    fn list_result2() -> String {
+        "hello!".to_string()
+    }
+
+    fn list_result3() -> Vec<String> {
+        vec!["hello,".to_string(), "world!".to_string()]
+    }
+
+    fn list_roundtrip(list: Vec<u8>) -> Vec<u8> {
+        list.to_vec()
+    }
+
+    fn string_roundtrip(s: String) -> String {
+        s.to_string()
+    }
+
+    fn list_minmax8(u: Vec<u8>, s: Vec<i8>) -> (Vec<u8>, Vec<i8>) {
+        assert_eq!(u, [u8::MIN, u8::MAX]);
+        assert_eq!(s, [i8::MIN, i8::MAX]);
+        (u, s)
+    }
+
+    fn list_minmax16(u: Vec<u16>, s: Vec<i16>) -> (Vec<u16>, Vec<i16>) {
+        assert_eq!(u, [u16::MIN, u16::MAX]);
+        assert_eq!(s, [i16::MIN, i16::MAX]);
+        (u, s)
+    }
+
+    fn list_minmax32(u: Vec<u32>, s: Vec<i32>) -> (Vec<u32>, Vec<i32>) {
+        assert_eq!(u, [u32::MIN, u32::MAX]);
+        assert_eq!(s, [i32::MIN, i32::MAX]);
+        (u, s)
+    }
+
+    fn list_minmax64(u: Vec<u64>, s: Vec<i64>) -> (Vec<u64>, Vec<i64>) {
+        assert_eq!(u, [u64::MIN, u64::MAX]);
+        assert_eq!(s, [i64::MIN, i64::MAX]);
+        (u, s)
+    }
+
+    fn list_minmax_float(u: Vec<f32>, s: Vec<f64>) -> (Vec<f32>, Vec<f64>) {
+        assert_eq!(u, [f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY]);
+        assert_eq!(s, [f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY]);
+        (u, s)
+    }
+}
+
+pub fn main() {
+    let bytes = allocated_bytes();
+    test_imports();
+    use test::lists::test::*;
+    empty_list_param(&[]);
+    empty_string_param("");
+    assert!(empty_list_result().is_empty());
+    assert_eq!(empty_string_result(), "");
+    list_param(&[1, 2, 3, 4]);
+    list_param2("foo");
+    list_param3(&["foo".to_owned(), "bar".to_owned(), "baz".to_owned()]);
+    list_param4(&[
+        vec!["foo".to_owned(), "bar".to_owned()],
+        vec!["baz".to_owned()],
+    ]);
+    assert_eq!(list_result(), [1, 2, 3, 4, 5]);
+    assert_eq!(list_result2(), "hello!");
+    assert_eq!(list_result3(), ["hello,", "world!"]);
+    assert_eq!(string_roundtrip("x"), "x");
+    assert_eq!(string_roundtrip(""), "");
+    assert_eq!(string_roundtrip("hello âš‘ world"), "hello âš‘ world");
+    // Ensure that we properly called `free` everywhere in all the glue that we
+    // needed to.
+    assert_eq!(bytes, allocated_bytes());
+    {
+        #[link(name = "lists")]
+        extern "C" {
+            fn test_imports();
+        }
+        let _ = || {
+            unsafe { test_imports() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/many_arguments.rs b/crates/cpp/tests/symmetric_tests/many_arguments.rs
new file mode 100644
index 000000000..2f7d2ac90
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/many_arguments.rs
@@ -0,0 +1,92 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/many_arguments",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports {}
+
+impl exports::imports::Guest for MyExports {
+    fn many_arguments(
+        a1: u64,
+        a2: u64,
+        a3: u64,
+        a4: u64,
+        a5: u64,
+        a6: u64,
+        a7: u64,
+        a8: u64,
+        a9: u64,
+        a10: u64,
+        a11: u64,
+        a12: u64,
+        a13: u64,
+        a14: u64,
+        a15: u64,
+        a16: u64,
+    ) {
+        assert_eq!(a1, 1);
+        assert_eq!(a2, 2);
+        assert_eq!(a3, 3);
+        assert_eq!(a4, 4);
+        assert_eq!(a5, 5);
+        assert_eq!(a6, 6);
+        assert_eq!(a7, 7);
+        assert_eq!(a8, 8);
+        assert_eq!(a9, 9);
+        assert_eq!(a10, 10);
+        assert_eq!(a11, 11);
+        assert_eq!(a12, 12);
+        assert_eq!(a13, 13);
+        assert_eq!(a14, 14);
+        assert_eq!(a15, 15);
+        assert_eq!(a16, 16);
+    }
+}
+
+fn main() {
+    many_arguments(
+        1,
+        2,
+        3,
+        4,
+        5,
+        6,
+        7,
+        8,
+        9,
+        10,
+        11,
+        12,
+        13,
+        14,
+        15,
+        16,
+    );
+    {
+        #[link(name = "many_arguments")]
+        extern "C" {
+            fn many_arguments(a1: i64,
+                a2: i64,
+                a3: i64,
+                a4: i64,
+                a5: i64,
+                a6: i64,
+                a7: i64,
+                a8: i64,
+                a9: i64,
+                a10: i64,
+                a11: i64,
+                a12: i64,
+                a13: i64,
+                a14: i64,
+                a15: i64,
+                a16: i64,);
+        }
+        let _ = || {
+            unsafe { many_arguments(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,) };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/numbers.rs b/crates/cpp/tests/symmetric_tests/numbers.rs
new file mode 100644
index 000000000..957af3d25
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/numbers.rs
@@ -0,0 +1,131 @@
+use std::sync::atomic::AtomicU32;
+
+wit_bindgen::generate!({
+    path: "../tests/runtime/numbers",
+    symmetric: true,
+    invert_direction: true,
+});
+
+static SCALAR: AtomicU32 = AtomicU32::new(0);
+
+export!(MyExports);
+
+pub struct MyExports;
+
+impl exports::test::numbers::test::Guest for MyExports {
+    fn roundtrip_u8(a: u8) -> u8 {
+        a
+    }
+
+    fn roundtrip_s8(a: i8) -> i8 {
+        a
+    }
+
+    fn roundtrip_u16(a: u16) -> u16 {
+        a
+    }
+
+    fn roundtrip_s16(a: i16) -> i16 {
+        a
+    }
+
+    fn roundtrip_u32(a: u32) -> u32 {
+        a
+    }
+
+    fn roundtrip_s32(a: i32) -> i32 {
+        a
+    }
+
+    fn roundtrip_u64(a: u64) -> u64 {
+        a
+    }
+
+    fn roundtrip_s64(a: i64) -> i64 {
+        a
+    }
+
+    fn roundtrip_f32(a: f32) -> f32 {
+        a
+    }
+
+    fn roundtrip_f64(a: f64) -> f64 {
+        a
+    }
+
+    fn roundtrip_char(a: char) -> char {
+        a
+    }
+
+    fn set_scalar(a: u32) -> () {
+        SCALAR.store(a, std::sync::atomic::Ordering::SeqCst);
+    }
+
+    fn get_scalar() -> u32 {
+        SCALAR.load(std::sync::atomic::Ordering::SeqCst)
+    }
+}
+
+pub fn main() {
+    test_imports();
+    use test::numbers::test::*;
+    assert_eq!(roundtrip_u8(1), 1);
+    assert_eq!(roundtrip_u8(u8::min_value()), u8::min_value());
+    assert_eq!(roundtrip_u8(u8::max_value()), u8::max_value());
+
+    assert_eq!(roundtrip_s8(1), 1);
+    assert_eq!(roundtrip_s8(i8::min_value()), i8::min_value());
+    assert_eq!(roundtrip_s8(i8::max_value()), i8::max_value());
+
+    assert_eq!(roundtrip_u16(1), 1);
+    assert_eq!(roundtrip_u16(u16::min_value()), u16::min_value());
+    assert_eq!(roundtrip_u16(u16::max_value()), u16::max_value());
+
+    assert_eq!(roundtrip_s16(1), 1);
+    assert_eq!(roundtrip_s16(i16::min_value()), i16::min_value());
+    assert_eq!(roundtrip_s16(i16::max_value()), i16::max_value());
+
+    assert_eq!(roundtrip_u32(1), 1);
+    assert_eq!(roundtrip_u32(u32::min_value()), u32::min_value());
+    assert_eq!(roundtrip_u32(u32::max_value()), u32::max_value());
+
+    assert_eq!(roundtrip_s32(1), 1);
+    assert_eq!(roundtrip_s32(i32::min_value()), i32::min_value());
+    assert_eq!(roundtrip_s32(i32::max_value()), i32::max_value());
+
+    assert_eq!(roundtrip_u64(1), 1);
+    assert_eq!(roundtrip_u64(u64::min_value()), u64::min_value());
+    assert_eq!(roundtrip_u64(u64::max_value()), u64::max_value());
+
+    assert_eq!(roundtrip_s64(1), 1);
+    assert_eq!(roundtrip_s64(i64::min_value()), i64::min_value());
+    assert_eq!(roundtrip_s64(i64::max_value()), i64::max_value());
+
+    assert_eq!(roundtrip_f32(1.0), 1.0);
+    assert_eq!(roundtrip_f32(f32::INFINITY), f32::INFINITY);
+    assert_eq!(roundtrip_f32(f32::NEG_INFINITY), f32::NEG_INFINITY);
+    assert!(roundtrip_f32(f32::NAN).is_nan());
+
+    assert_eq!(roundtrip_f64(1.0), 1.0);
+    assert_eq!(roundtrip_f64(f64::INFINITY), f64::INFINITY);
+    assert_eq!(roundtrip_f64(f64::NEG_INFINITY), f64::NEG_INFINITY);
+    assert!(roundtrip_f64(f64::NAN).is_nan());
+
+    assert_eq!(roundtrip_char('a'), 'a');
+    assert_eq!(roundtrip_char(' '), ' ');
+    assert_eq!(roundtrip_char('🚩'), '🚩');
+
+    set_scalar(2);
+    assert_eq!(get_scalar(), 2);
+    set_scalar(4);
+    assert_eq!(get_scalar(), 4);
+    {
+        #[link(name = "numbers")]
+        extern "C" {
+            fn test_imports();
+        }
+        let _ = || {
+            unsafe { test_imports() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/options.rs b/crates/cpp/tests/symmetric_tests/options.rs
new file mode 100644
index 000000000..f6d996fec
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/options.rs
@@ -0,0 +1,57 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/options",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+impl exports::test::options::test::Guest for MyExports {
+    fn option_none_param(a: Option<String>) {
+        assert!(a.is_none());
+    }
+
+    fn option_none_result() -> Option<String> {
+        None
+    }
+
+    fn option_some_param(a: Option<String>) {
+        assert_eq!(a, Some("foo".to_string()));
+    }
+
+    fn option_some_result() -> Option<String> {
+        Some("foo".to_string())
+    }
+
+    fn option_roundtrip(a: Option<String>) -> Option<String> {
+        a
+    }
+
+    fn double_option_roundtrip(a: Option<Option<u32>>) -> Option<Option<u32>> {
+        a
+    }
+}
+
+pub fn main() {
+    use test::options::test::*;
+    test_imports();
+    assert!(option_none_result().is_none());
+    assert_eq!(option_some_result(), Some("foo".to_string()));
+    option_none_param(None);
+    option_some_param(Some("foo"));
+    assert_eq!(option_roundtrip(Some("foo")), Some("foo".to_string()));
+    assert_eq!(double_option_roundtrip(Some(Some(42))), Some(Some(42)));
+    assert_eq!(double_option_roundtrip(Some(None)), Some(None));
+    assert_eq!(double_option_roundtrip(None), None);
+    {
+        #[link(name = "options")]
+        extern "C" {
+            fn test_imports();
+        }
+        let _ = || {
+            unsafe { test_imports() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/records.rs b/crates/cpp/tests/symmetric_tests/records.rs
new file mode 100644
index 000000000..13fee7a53
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/records.rs
@@ -0,0 +1,94 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/records",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+use exports::test::records::test as test_imports;
+
+impl test_imports::Guest for MyExports {
+    fn multiple_results() -> (u8, u16) {
+        (4, 5)
+    }
+
+    fn swap_tuple(a: (u8, u32)) -> (u32, u8) {
+        (a.1, a.0)
+    }
+
+    fn roundtrip_flags1(a: test_imports::F1) -> test_imports::F1 {
+        drop(format!("{:?}", a));
+        let _ = a & test_imports::F1::all();
+        a
+    }
+
+    fn roundtrip_flags2(a: test_imports::F2) -> test_imports::F2 {
+        a
+    }
+
+    fn roundtrip_flags3(
+        a: test_imports::Flag8,
+        b: test_imports::Flag16,
+        c: test_imports::Flag32,
+    ) -> (
+        test_imports::Flag8,
+        test_imports::Flag16,
+        test_imports::Flag32,
+    ) {
+        (a, b, c)
+    }
+
+    fn roundtrip_record1(a: test_imports::R1) -> test_imports::R1 {
+        drop(format!("{:?}", a));
+        a
+    }
+
+    fn tuple1(a: (u8,)) -> (u8,) {
+        (a.0,)
+    }
+}
+
+pub fn main() {
+    use test::records::test::*;
+
+    test_imports();
+    assert_eq!(multiple_results(), (100, 200));
+    assert_eq!(swap_tuple((1u8, 2u32)), (2u32, 1u8));
+    assert_eq!(roundtrip_flags1(F1::A), F1::A);
+    assert_eq!(roundtrip_flags1(F1::empty()), F1::empty());
+    assert_eq!(roundtrip_flags1(F1::B), F1::B);
+    assert_eq!(roundtrip_flags1(F1::A | F1::B), F1::A | F1::B);
+
+    assert_eq!(roundtrip_flags2(F2::C), F2::C);
+    assert_eq!(roundtrip_flags2(F2::empty()), F2::empty());
+    assert_eq!(roundtrip_flags2(F2::D), F2::D);
+    assert_eq!(roundtrip_flags2(F2::C | F2::E), F2::C | F2::E);
+
+    let r = roundtrip_record1(R1 {
+        a: 8,
+        b: F1::empty(),
+    });
+    assert_eq!(r.a, 8);
+    assert_eq!(r.b, F1::empty());
+
+    let r = roundtrip_record1(R1 {
+        a: 0,
+        b: F1::A | F1::B,
+    });
+    assert_eq!(r.a, 0);
+    assert_eq!(r.b, F1::A | F1::B);
+
+    assert_eq!(tuple1((1,)), (1,));
+    {
+        #[link(name = "records")]
+        extern "C" {
+            fn test_imports();
+        }
+        let _ = || {
+            unsafe { test_imports() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/results.rs b/crates/cpp/tests/symmetric_tests/results.rs
new file mode 100644
index 000000000..3422dde38
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/results.rs
@@ -0,0 +1,136 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/results",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+use exports::test::results::test as imports;
+
+impl exports::test::results::test::Guest for MyExports {
+    fn string_error(a: f32) -> Result<f32, String> {
+        if a == 0.0 {
+            Err("zero".to_owned())
+        } else {
+            Ok(a)
+        }
+    }
+
+    fn enum_error(a: f32) -> Result<f32, imports::E> {
+        if a == 0.0 {
+            Err(imports::E::A)
+        } else {
+            Ok(a)
+        }
+    }
+
+    fn record_error(a: f32) -> Result<f32, imports::E2> {
+        if a == 0.0 {
+            Err(imports::E2 {
+                line: 420,
+                column: 0,
+            })
+        } else if a == 1.0 {
+            Err(imports::E2 {
+                line: 77,
+                column: 2,
+            })
+        } else {
+            Ok(a)
+        }
+    }
+
+    fn variant_error(a: f32) -> Result<f32, imports::E3> {
+        if a == 0.0 {
+            Err(imports::E3::E2(imports::E2 {
+                line: 420,
+                column: 0,
+            }))
+        } else if a == 1.0 {
+            Err(imports::E3::E1(imports::E::B))
+        } else if a == 2.0 {
+            Err(imports::E3::E1(imports::E::C))
+        } else {
+            Ok(a)
+        }
+    }
+
+    fn empty_error(a: u32) -> Result<u32, ()> {
+        if a == 0 {
+            Err(())
+        } else if a == 1 {
+            Ok(42)
+        } else {
+            Ok(a)
+        }
+    }
+
+    fn double_error(a: u32) -> Result<Result<(), String>, String> {
+        if a == 0 {
+            Ok(Ok(()))
+        } else if a == 1 {
+            Ok(Err("one".into()))
+        } else {
+            Err("two".into())
+        }
+    }
+}
+
+pub fn main() {
+    use test::results::test::{
+        double_error, empty_error, enum_error, record_error, string_error, variant_error, E, E2, E3,
+    };
+
+    assert_eq!(string_error(0.0), Err("zero".to_owned()));
+    assert_eq!(string_error(1.0), Ok(1.0));
+
+    assert_eq!(enum_error(0.0), Err(E::A));
+    assert_eq!(enum_error(0.0), Err(E::A));
+
+    assert!(matches!(
+        record_error(0.0),
+        Err(E2 {
+            line: 420,
+            column: 0
+        })
+    ));
+    assert!(matches!(
+        record_error(1.0),
+        Err(E2 {
+            line: 77,
+            column: 2
+        })
+    ));
+
+    assert!(record_error(2.0).is_ok());
+
+    assert!(matches!(
+        variant_error(0.0),
+        Err(E3::E2(E2 {
+            line: 420,
+            column: 0
+        }))
+    ));
+    assert!(matches!(variant_error(1.0), Err(E3::E1(E::B))));
+    assert!(matches!(variant_error(2.0), Err(E3::E1(E::C))));
+
+    assert_eq!(empty_error(0), Err(()));
+    assert_eq!(empty_error(1), Ok(42));
+    assert_eq!(empty_error(2), Ok(2));
+
+    assert_eq!(double_error(0), Ok(Ok(())));
+    assert_eq!(double_error(1), Ok(Err("one".into())));
+    assert_eq!(double_error(2), Err("two".into()));
+    {
+        #[link(name = "results")]
+        extern "C" {
+            fn exp_testX3AresultsX2FtestX00string_error(a: f32, b: *mut u8);
+        }
+        let _ = || {
+            unsafe { exp_testX3AresultsX2FtestX00string_error(0.0, std::ptr::null_mut()) };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/smoke.rs b/crates/cpp/tests/symmetric_tests/smoke.rs
new file mode 100644
index 000000000..1cf3e06d2
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/smoke.rs
@@ -0,0 +1,34 @@
+use std::sync::atomic::AtomicBool;
+
+wit_bindgen::generate!({
+    path: "../tests/runtime/smoke",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+static HIT: AtomicBool = AtomicBool::new(false);
+
+impl exports::test::smoke::imports::Guest for MyExports {
+    fn thunk() {
+        HIT.store(true, std::sync::atomic::Ordering::SeqCst);
+        println!("tester called");
+    }
+}
+
+pub fn main() {
+    thunk();
+    assert!(HIT.load(std::sync::atomic::Ordering::SeqCst));
+    {
+        #[link(name = "smoke")]
+        extern "C" {
+            fn thunk();
+        }
+        let _ = || {
+            unsafe { thunk() };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/strings.rs b/crates/cpp/tests/symmetric_tests/strings.rs
new file mode 100644
index 000000000..74b49861c
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/strings.rs
@@ -0,0 +1,38 @@
+wit_bindgen::generate!({
+    path: "../tests/runtime/strings",
+    symmetric: true,
+    invert_direction: true,
+});
+
+export!(MyExports);
+
+pub struct MyExports;
+
+impl exports::test::strings::imports::Guest for MyExports {
+    fn take_basic(s: String) {
+        assert_eq!(s, "latin utf16");
+    }
+
+    fn return_unicode() -> String {
+        "🚀🚀🚀 𠈄𓀀".to_string()
+    }
+}
+
+pub fn main() {
+    test_imports();
+    assert_eq!(return_empty(), "");
+    assert_eq!(roundtrip("str"), "str");
+    assert_eq!(
+        roundtrip("🚀🚀🚀 𠈄𓀀"),
+        "🚀🚀🚀 𠈄𓀀"
+    );
+    {
+        #[link(name = "strings")]
+        extern "C" {
+            fn roundtrip(_: *mut u8, _: usize, _: *mut u8);
+        }
+        let _ = || {
+            unsafe { roundtrip(core::ptr::null_mut(), 0, core::ptr::null_mut()) };
+        };
+    }
+}
diff --git a/crates/cpp/tests/symmetric_tests/test-rust-wasm/Cargo.toml b/crates/cpp/tests/symmetric_tests/test-rust-wasm/Cargo.toml
new file mode 100644
index 000000000..4b0349221
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/test-rust-wasm/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "test-rust-wasm"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs b/crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs
new file mode 100644
index 000000000..81f77ce4a
--- /dev/null
+++ b/crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs
@@ -0,0 +1,15 @@
+// checking balanced memory can't work in symmetric because ownership is transferred both ways
+
+pub fn get() -> usize {
+    0
+}
+
+pub fn guard() -> impl Drop {
+    struct A;
+
+    impl Drop for A {
+        fn drop(&mut self) {}
+    }
+
+    A
+}
diff --git a/crates/cpp/tests/wasm-micro-runtime b/crates/cpp/tests/wasm-micro-runtime
new file mode 160000
index 000000000..028f43bc1
--- /dev/null
+++ b/crates/cpp/tests/wasm-micro-runtime
@@ -0,0 +1 @@
+Subproject commit 028f43bc18494866c44666e54e9c5a2cd84152f5
diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs
index b4be3798c..f026cb108 100644
--- a/crates/guest-rust/macro/src/lib.rs
+++ b/crates/guest-rust/macro/src/lib.rs
@@ -108,6 +108,9 @@ impl Parse for Config {
                     Opt::Stubs => {
                         opts.stubs = true;
                     }
+                    Opt::Wasm64 => {
+                        opts.wasm64 = true;
+                    }
                     Opt::ExportPrefix(prefix) => opts.export_prefix = Some(prefix.value()),
                     Opt::AdditionalDerives(paths) => {
                         opts.additional_derive_attributes = paths
@@ -147,6 +150,12 @@ impl Parse for Config {
                     Opt::DisableCustomSectionLinkHelpers(disable) => {
                         opts.disable_custom_section_link_helpers = disable.value();
                     }
+                    Opt::Symmetric(enable) => {
+                        opts.symmetric = enable.value();
+                    }
+                    Opt::InvertDirection(enable) => {
+                        opts.invert_direction = enable.value();
+                    }
                     Opt::Debug(enable) => {
                         debug = enable.value();
                     }
@@ -246,7 +255,7 @@ fn parse_source(
             };
             let (pkg, sources) = resolve.push_path(normalized_path)?;
             pkgs.push(pkg);
-            files.extend(sources.paths().map(|p| p.to_owned()));
+            files.extend(sources.package_paths(pkg).unwrap().map(|v| v.to_owned()));
         }
         Ok(())
     };
@@ -265,9 +274,14 @@ fn parse_source(
 }
 
 impl Config {
-    fn expand(self) -> Result<TokenStream> {
+    fn expand(mut self) -> Result<TokenStream> {
         let mut files = Default::default();
+        // for testing the symmetric ABI (modify guest code)
+        if std::env::var("SYMMETRIC_ABI").is_ok() {
+            self.opts.symmetric = true;
+        }
         let mut generator = self.opts.build();
+        generator.apply_resolve_options(&mut self.resolve, &mut self.world);
         generator
             .generate(&self.resolve, self.world, &mut files)
             .map_err(|e| anyhow_to_syn(Span::call_site(), e))?;
@@ -335,9 +349,12 @@ mod kw {
     syn::custom_keyword!(default_bindings_module);
     syn::custom_keyword!(export_macro_name);
     syn::custom_keyword!(pub_export_macro);
+    syn::custom_keyword!(wasm64);
     syn::custom_keyword!(generate_unused_types);
     syn::custom_keyword!(features);
     syn::custom_keyword!(disable_custom_section_link_helpers);
+    syn::custom_keyword!(symmetric);
+    syn::custom_keyword!(invert_direction);
     syn::custom_keyword!(imports);
     syn::custom_keyword!(debug);
 }
@@ -396,9 +413,12 @@ enum Opt {
     DefaultBindingsModule(syn::LitStr),
     ExportMacroName(syn::LitStr),
     PubExportMacro(syn::LitBool),
+    Wasm64,
     GenerateUnusedTypes(syn::LitBool),
     Features(Vec<syn::LitStr>),
     DisableCustomSectionLinkHelpers(syn::LitBool),
+    Symmetric(syn::LitBool),
+    InvertDirection(syn::LitBool),
     Async(AsyncConfig, Span),
     Debug(syn::LitBool),
 }
@@ -491,6 +511,9 @@ impl Parse for Opt {
         } else if l.peek(kw::stubs) {
             input.parse::<kw::stubs>()?;
             Ok(Opt::Stubs)
+        } else if l.peek(kw::wasm64) {
+            input.parse::<kw::wasm64>()?;
+            Ok(Opt::Wasm64)
         } else if l.peek(kw::export_prefix) {
             input.parse::<kw::export_prefix>()?;
             input.parse::<Token![:]>()?;
@@ -555,6 +578,14 @@ impl Parse for Opt {
             input.parse::<kw::disable_custom_section_link_helpers>()?;
             input.parse::<Token![:]>()?;
             Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?))
+        } else if l.peek(kw::symmetric) {
+            input.parse::<kw::symmetric>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::Symmetric(input.parse()?))
+        } else if l.peek(kw::invert_direction) {
+            input.parse::<kw::invert_direction>()?;
+            input.parse::<Token![:]>()?;
+            Ok(Opt::InvertDirection(input.parse()?))
         } else if l.peek(kw::debug) {
             input.parse::<kw::debug>()?;
             input.parse::<Token![:]>()?;
diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs
index adef40070..6a4c79a99 100644
--- a/crates/rust/src/bindgen.rs
+++ b/crates/rust/src/bindgen.rs
@@ -2,8 +2,8 @@ use crate::{int_repr, to_rust_ident, wasm_type, Identifier, InterfaceGenerator,
 use heck::*;
 use std::fmt::Write as _;
 use std::mem;
-use wit_bindgen_core::abi::{Bindgen, Instruction, LiftLower, WasmType};
-use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source};
+use wit_bindgen_core::abi::{AbiVariant, Bindgen, Instruction, LiftLower, WasmType};
+use wit_bindgen_core::{dealias, make_external_symbol, uwrite, uwriteln, wit_parser::*, Source};
 
 pub(super) struct FunctionBindgen<'a, 'b> {
     pub r#gen: &'b mut InterfaceGenerator<'a>,
@@ -77,9 +77,15 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
         }
     }
 
-    fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String {
+    fn declare_import(
+        &mut self,
+        module_prefix: &str,
+        name: &str,
+        params: &[WasmType],
+        results: &[WasmType],
+    ) -> String {
         // Define the actual function we're calling inline
-        let tmp = self.tmp();
+        // let tmp = self.tmp();
         let mut sig = "(".to_owned();
         for param in params.iter() {
             sig.push_str("_: ");
@@ -93,21 +99,19 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
             sig.push_str(wasm_type(*result));
         }
         let module_name = self.wasm_import_module;
+        let export_name = String::from(module_prefix)
+            + &make_external_symbol(module_name, name, AbiVariant::GuestImport);
         uwrite!(
             self.src,
             "
-                #[cfg(target_arch = \"wasm32\")]
-                #[link(wasm_import_module = \"{module_name}\")]
+                #[link(wasm_import_module = \"{module_prefix}{module_name}\")]
                 unsafe extern \"C\" {{
-                    #[link_name = \"{name}\"]
-                    fn wit_import{tmp}{sig};
+                    #[cfg_attr(target_arch = \"wasm32\", link_name = \"{name}\")]
+                    fn {export_name}{sig};
                 }}
-
-                #[cfg(not(target_arch = \"wasm32\"))]
-                unsafe extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }}
             "
         );
-        format!("wit_import{tmp}")
+        export_name
     }
 
     fn let_results(&mut self, amt: usize, results: &mut Vec<String>) {
@@ -207,6 +211,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
             || match self.lift_lower() {
                 LiftLower::LowerArgsLiftResults => false,
                 LiftLower::LiftArgsLowerResults => true,
+                LiftLower::Symmetric => todo!(),
             };
         self.r#gen.type_path(id, owned)
     }
@@ -441,7 +446,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 ..
             } => {
                 let op = &operands[0];
-                let result = format!("({op}).take_handle() as i32");
+                let result = format!(
+                    "({op}).take_handle(){cast}",
+                    cast = if self.r#gen.r#gen.opts.symmetric {
+                        " as *mut u8"
+                    } else {
+                        " as i32"
+                    }
+                );
                 results.push(result);
             }
 
@@ -450,7 +462,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 ..
             } => {
                 let op = &operands[0];
-                results.push(format!("({op}).handle() as i32"))
+                results.push(format!(
+                    "({op}).handle(){cast}",
+                    cast = if self.r#gen.r#gen.opts.symmetric {
+                        " as *mut u8"
+                    } else {
+                        " as i32"
+                    }
+                ))
             }
 
             Instruction::HandleLift { handle, .. } => {
@@ -464,23 +483,36 @@ impl Bindgen for FunctionBindgen<'_, '_> {
 
                 let result = if is_own {
                     let name = self.r#gen.type_path(dealiased_resource, true);
-                    format!("{name}::from_handle({op} as u32)")
+
+                    format!(
+                        "{name}::from_handle({op}{cast})",
+                        cast = if self.r#gen.r#gen.opts.symmetric {
+                            " as usize"
+                        } else {
+                            " as u32"
+                        }
+                    )
                 } else if self.r#gen.is_exported_resource(*resource) {
                     let name = resolve.types[*resource]
                         .name
                         .as_deref()
                         .unwrap()
                         .to_upper_camel_case();
-                    format!("{name}Borrow::lift({op} as u32 as usize)")
+                    format!("{name}Borrow::lift({op} as usize)")
                 } else {
                     let tmp = format!("handle{}", self.tmp());
                     self.handle_decls.push(format!("let {tmp};"));
                     let name = self.r#gen.type_path(dealiased_resource, true);
                     format!(
                         "{{\n
-                            {tmp} = {name}::from_handle({op} as u32);
+                            {tmp} = {name}::from_handle({op}{cast});
                             &{tmp}
-                        }}"
+                        }}",
+                        cast = if self.r#gen.r#gen.opts.symmetric {
+                            ""
+                        } else {
+                            " as u32"
+                        }
                     )
                 };
                 results.push(result);
@@ -488,7 +520,11 @@ impl Bindgen for FunctionBindgen<'_, '_> {
 
             Instruction::FutureLower { .. } => {
                 let op = &operands[0];
-                results.push(format!("({op}).take_handle() as i32"))
+                if self.r#gen.r#gen.opts.symmetric {
+                    results.push(format!("({op}).take_handle() as *mut u8"))
+                } else {
+                    results.push(format!("({op}).take_handle() as i32"))
+                }
             }
 
             Instruction::FutureLift { payload, .. } => {
@@ -507,16 +543,24 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                     .future_payloads
                     .get_index_of(&name)
                     .unwrap();
-                let path = self.r#gen.path_to_root();
-                results.push(format!(
-                    "{async_support}::FutureReader::from_handle_and_vtable\
-                     ({op} as u32, &{path}wit_future::vtable{ordinal}::VTABLE)"
-                ))
+                if self.r#gen.r#gen.opts.symmetric {
+                    results.push(format!("{async_support}::FutureReader::from_handle({op})"))
+                } else {
+                    let path = self.r#gen.path_to_root();
+                    results.push(format!(
+                        "{async_support}::FutureReader::from_handle_and_vtable\
+                        ({op} as u32, &{path}wit_future::vtable{ordinal}::VTABLE)"
+                    ))
+                }
             }
 
             Instruction::StreamLower { .. } => {
                 let op = &operands[0];
-                results.push(format!("({op}).take_handle() as i32"))
+                if self.r#gen.r#gen.opts.symmetric {
+                    results.push(format!("({op}).take_handle() as *mut u8"))
+                } else {
+                    results.push(format!("({op}).take_handle() as i32"))
+                }
             }
 
             Instruction::StreamLift { payload, .. } => {
@@ -535,11 +579,15 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                     .stream_payloads
                     .get_index_of(&name)
                     .unwrap();
-                let path = self.r#gen.path_to_root();
-                results.push(format!(
-                    "{async_support}::StreamReader::from_handle_and_vtable\
+                if self.r#gen.r#gen.opts.symmetric {
+                    results.push(format!("{async_support}::StreamReader::from_handle({op})"))
+                } else {
+                    let path = self.r#gen.path_to_root();
+                    results.push(format!(
+                        "{async_support}::StreamReader::from_handle_and_vtable\
                      ({op} as u32, &{path}wit_stream::vtable{ordinal}::VTABLE)"
-                ))
+                    ))
+                }
             }
 
             Instruction::ErrorContextLower { .. } => {
@@ -737,7 +785,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 let val = format!("vec{}", tmp);
                 let ptr = format!("ptr{}", tmp);
                 let len = format!("len{}", tmp);
-                if realloc.is_none() {
+                if realloc.is_none() || (self.r#gen.in_import && self.r#gen.r#gen.opts.symmetric) {
                     self.push_str(&format!("let {} = {};\n", val, operands[0]));
                 } else {
                     let op0 = operands.pop().unwrap();
@@ -745,7 +793,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 }
                 self.push_str(&format!("let {} = {}.as_ptr().cast::<u8>();\n", ptr, val));
                 self.push_str(&format!("let {} = {}.len();\n", len, val));
-                if realloc.is_some() {
+                if realloc.is_some() && !(self.r#gen.in_import && self.r#gen.r#gen.opts.symmetric) {
                     self.push_str(&format!("::core::mem::forget({});\n", val));
                 }
                 results.push(format!("{ptr}.cast_mut()"));
@@ -757,10 +805,17 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 let len = format!("len{}", tmp);
                 self.push_str(&format!("let {} = {};\n", len, operands[1]));
                 let vec = self.r#gen.path_to_vec();
-                let result = format!(
-                    "{vec}::from_raw_parts({}.cast(), {1}, {1})",
-                    operands[0], len
-                );
+                let result = if !self.r#gen.r#gen.opts.symmetric || self.r#gen.in_import {
+                    format!(
+                        "{vec}::from_raw_parts({}.cast(), {1}, {1})",
+                        operands[0], len
+                    )
+                } else {
+                    format!(
+                        "unsafe {{ std::slice::from_raw_parts({}.cast(), {1}) }}.to_vec()",
+                        operands[0], len
+                    )
+                };
                 results.push(result);
             }
 
@@ -769,7 +824,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 let val = format!("vec{}", tmp);
                 let ptr = format!("ptr{}", tmp);
                 let len = format!("len{}", tmp);
-                if realloc.is_none() {
+                if realloc.is_none() || (self.r#gen.in_import && self.r#gen.r#gen.opts.symmetric) {
                     self.push_str(&format!("let {} = {};\n", val, operands[0]));
                 } else {
                     let op0 = format!("{}.into_bytes()", operands[0]);
@@ -777,7 +832,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 }
                 self.push_str(&format!("let {} = {}.as_ptr().cast::<u8>();\n", ptr, val));
                 self.push_str(&format!("let {} = {}.len();\n", len, val));
-                if realloc.is_some() {
+                if realloc.is_some() && !(self.r#gen.in_import && self.r#gen.r#gen.opts.symmetric) {
                     self.push_str(&format!("::core::mem::forget({});\n", val));
                 }
                 results.push(format!("{ptr}.cast_mut()"));
@@ -789,15 +844,35 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 let tmp = self.tmp();
                 let len = format!("len{}", tmp);
                 uwriteln!(self.src, "let {len} = {};", operands[1]);
-                uwriteln!(
-                    self.src,
-                    "let bytes{tmp} = {vec}::from_raw_parts({}.cast(), {len}, {len});",
-                    operands[0],
-                );
-                if self.r#gen.r#gen.opts.raw_strings {
-                    results.push(format!("bytes{tmp}"));
+                if self.r#gen.r#gen.opts.symmetric && !self.r#gen.in_import {
+                    uwriteln!(
+                        self.src,
+                        "let string{tmp} = String::from(std::str::from_utf8(std::slice::from_raw_parts({}, {len})).unwrap());",
+                        operands[0],
+                    );
+                    results.push(format!("string{tmp}"));
                 } else {
-                    results.push(format!("{}(bytes{tmp})", self.r#gen.path_to_string_lift()));
+                    if self.r#gen.r#gen.opts.symmetric {
+                        // symmetric must not access zero page memory
+                        uwriteln!(
+                            self.src,
+                            "let bytes{tmp} = if {len}>0 {{
+                               {vec}::from_raw_parts({}.cast(), {len}, {len})
+                            }} else {{ Default::default() }};",
+                            operands[0]
+                        );
+                    } else {
+                        uwriteln!(
+                            self.src,
+                            "let bytes{tmp} = {vec}::from_raw_parts({}.cast(), {len}, {len});",
+                            operands[0],
+                        );
+                    }
+                    if self.r#gen.r#gen.opts.raw_strings {
+                        results.push(format!("bytes{tmp}"));
+                    } else {
+                        results.push(format!("{}(bytes{tmp})", self.r#gen.path_to_string_lift()));
+                    }
                 }
             }
 
@@ -824,6 +899,16 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 self.push_str(&format!(
                     "let ptr = {alloc}::alloc({layout}).cast::<u8>();\n",
                 ));
+                if self.r#gen.r#gen.opts.symmetric && self.r#gen.in_import {
+                    //if !self.r#gen.needs_deallocate {
+                    //    self.push_str("// ");
+                    //} else {
+                    assert!(self.r#gen.needs_deallocate);
+                    //}
+                    self.push_str(&format!(
+                        "if !ptr.is_null() {{ _deallocate.push((ptr, {layout})); }}\n"
+                    ));
+                }
                 self.push_str(&format!(
                     "if ptr.is_null()\n{{\n{alloc}::handle_alloc_error({layout});\n}}\nptr\n}}",
                 ));
@@ -877,20 +962,34 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 uwriteln!(self.src, "{result}.push(e{tmp});");
                 uwriteln!(self.src, "}}");
                 results.push(result);
-                let dealloc = self.r#gen.path_to_cabi_dealloc();
-                self.push_str(&format!(
-                    "{dealloc}({base}, {len} * {size}, {align});\n",
-                    size = size.format(POINTER_SIZE_EXPRESSION),
-                    align = align.format(POINTER_SIZE_EXPRESSION)
-                ));
+                if !self.r#gen.r#gen.opts.symmetric || self.r#gen.in_import {
+                    let dealloc = self.r#gen.path_to_cabi_dealloc();
+                    self.push_str(&format!(
+                        "{dealloc}({base}, {len} * {size}, {align});\n",
+                        size = size.format(POINTER_SIZE_EXPRESSION),
+                        align = align.format(POINTER_SIZE_EXPRESSION)
+                    ));
+                }
             }
 
             Instruction::IterElem { .. } => results.push("e".to_string()),
 
             Instruction::IterBasePointer => results.push("base".to_string()),
 
-            Instruction::CallWasm { name, sig, .. } => {
-                let func = self.declare_import(name, &sig.params, &sig.results);
+            Instruction::CallWasm {
+                name,
+                sig,
+                module_prefix,
+                ..
+            } => {
+                let module_prefix = if let Some(prefix) = self.r#gen.r#gen.import_prefix.as_ref() {
+                    let combined_prefix = prefix.clone() + module_prefix;
+                    std::borrow::Cow::Owned(combined_prefix)
+                } else {
+                    std::borrow::Cow::Borrowed(*module_prefix)
+                };
+                let func =
+                    self.declare_import(module_prefix.as_ref(), name, &sig.params, &sig.results);
 
                 // ... then call the function with all our operands
                 if !sig.results.is_empty() {
@@ -901,10 +1000,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
                 self.push_str("(");
                 self.push_str(&operands.join(", "));
                 self.push_str(");\n");
+                if self.r#gen.needs_deallocate {
+                    self.push_str(&format!("for (ptr,layout) in _deallocate.drain(..) {{ _rt::alloc::dealloc(ptr, layout); }}\n"));
+                    self.r#gen.needs_deallocate = false;
+                }
             }
 
             Instruction::AsyncCallWasm { name, .. } => {
-                let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]);
+                let func = self.declare_import("", name, &[WasmType::Pointer; 3], &[WasmType::I32]);
 
                 let async_support = self.r#gen.r#gen.async_support_path();
                 let operands = operands.join(", ");
@@ -1022,7 +1125,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
             }
 
             Instruction::AsyncCallReturn { name, params } => {
-                let func = self.declare_import(name, params, &[]);
+                let func = self.declare_import("", name, params, &[]);
 
                 uwriteln!(self.src, "{func}({});", operands.join(", "));
                 self.emit_cleanup();
diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs
index 0b13b38b4..5e9c6342d 100644
--- a/crates/rust/src/interface.rs
+++ b/crates/rust/src/interface.rs
@@ -11,7 +11,8 @@ use std::fmt::Write as _;
 use std::mem;
 use wit_bindgen_core::abi::{self, AbiVariant, LiftLower};
 use wit_bindgen_core::{
-    dealias, uwrite, uwriteln, wit_parser::*, AnonymousTypeGenerator, Source, TypeInfo,
+    dealias, make_external_component, make_external_symbol, symmetric, uwrite, uwriteln,
+    wit_parser::*, AnonymousTypeGenerator, Source, TypeInfo,
 };
 
 pub struct InterfaceGenerator<'a> {
@@ -25,6 +26,7 @@ pub struct InterfaceGenerator<'a> {
     pub return_pointer_area_size: ArchitectureSize,
     pub return_pointer_area_align: Alignment,
     pub(super) needs_runtime_module: bool,
+    pub(super) needs_deallocate: bool,
 }
 
 /// A description of the "mode" in which a type is printed.
@@ -203,63 +205,95 @@ impl<'i> InterfaceGenerator<'i> {
         }
 
         for (resource, (trait_name, methods)) in traits.iter() {
-            uwriteln!(self.src, "pub trait {trait_name}: 'static {{");
             let resource = resource.unwrap();
             let resource_name = self.resolve.types[resource].name.as_ref().unwrap();
+            if self.gen.opts.symmetric {
+                let resource_type = resource_name.to_pascal_case();
+                let resource_lowercase = resource_name.to_lower_camel_case();
+                uwriteln!(
+                    self.src,
+                    r#"#[doc(hidden)]
+            #[allow(non_snake_case)]
+            pub unsafe fn _export_drop_{resource_lowercase}_cabi<T: {trait_name}>(arg0: usize) {{
+                #[cfg(target_arch = "wasm32")]
+                _rt::run_ctors_once();
+                {resource_type}::dtor::<T>(arg0 as *mut u8);
+            }}"#
+                );
+            }
+
+            uwriteln!(self.src, "pub trait {trait_name}: 'static {{");
             let (_, interface_name) = interface.unwrap();
             let module = self.resolve.name_world_key(interface_name);
-            uwriteln!(
-                self.src,
-                r#"
-#[doc(hidden)]
-unsafe fn _resource_new(val: *mut u8) -> u32
-    where Self: Sized
-{{
-    #[cfg(not(target_arch = "wasm32"))]
+            let external_new = make_external_symbol(
+                &(String::from("[export]") + &module),
+                &(String::from("[resource-new]") + &resource_name),
+                AbiVariant::GuestImport,
+            );
+            let external_rep = make_external_symbol(
+                &(String::from("[export]") + &module),
+                &(String::from("[resource-rep]") + &resource_name),
+                AbiVariant::GuestImport,
+            );
+            if self.gen.opts.symmetric {
+                uwriteln!(
+                    self.src,
+                    r#"
+    #[doc(hidden)]
+    unsafe fn _resource_new(val: *mut u8) -> {handle_type}
+        where Self: Sized
     {{
-        let _ = val;
-        unreachable!();
+        val as {handle_type}
     }}
-
-    #[cfg(target_arch = "wasm32")]
+    
+    #[doc(hidden)]
+    fn _resource_rep(handle: {handle_type}) -> *mut u8
+        where Self: Sized
     {{
-        #[link(wasm_import_module = "[export]{module}")]
-        unsafe extern "C" {{
-            #[link_name = "[resource-new]{resource_name}"]
-            fn new(_: *mut u8) -> u32;
-        }}
-        unsafe {{ new(val) }}
+        handle as *mut u8
+    }}
+    
+                        "#,
+                    handle_type = "usize"
+                );
+            } else {
+                uwriteln!(
+                    self.src,
+                    r#"
+#[doc(hidden)]
+unsafe fn _resource_new(val: *mut u8) -> {handle_type}
+    where Self: Sized
+{{
+    #[link(wasm_import_module = "[export]{module}")]
+    unsafe extern "C" {{
+        #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]{resource_name}")]
+        fn {external_new}(_: *mut u8) -> {handle_type};
     }}
+    unsafe {{ {external_new}(val) }}
 }}
 
 #[doc(hidden)]
-fn _resource_rep(handle: u32) -> *mut u8
+fn _resource_rep(handle: {handle_type}) -> *mut u8
     where Self: Sized
 {{
-    #[cfg(not(target_arch = "wasm32"))]
-    {{
-        let _ = handle;
-        unreachable!();
+    #[link(wasm_import_module = "[export]{module}")]
+    unsafe extern "C" {{
+        #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]{resource_name}")]
+        fn {external_rep}(_: {handle_type}) -> *mut u8;
     }}
-
-    #[cfg(target_arch = "wasm32")]
-    {{
-        #[link(wasm_import_module = "[export]{module}")]
-        unsafe extern "C" {{
-            #[link_name = "[resource-rep]{resource_name}"]
-            fn rep(_: u32) -> *mut u8;
-        }}
-        unsafe {{
-            rep(handle)
-        }}
+    unsafe {{
+        {external_rep}(handle)
     }}
 }}
 
-                    "#
-            );
+                    "#,
+                    handle_type = "u32"
+                );
+            }
             for method in methods {
                 self.src.push_str(method);
             }
+
             uwriteln!(self.src, "}}");
         }
 
@@ -323,14 +357,35 @@ macro_rules! {macro_name} {{
                 }
             };
             let camel = name.to_upper_camel_case();
-            uwriteln!(
-                self.src,
-                r#"
+            if self.gen.opts.symmetric {
+                let dtor_symbol = make_external_symbol(
+                    &module,
+                    &(String::from("[resource-drop]") + &name),
+                    AbiVariant::GuestImport,
+                );
+                uwriteln!(
+                    self.src,
+                    r#"    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn {dtor_symbol}(arg0: usize) {{
+      $($path_to_types)*::_export_drop_{name}_cabi::<<$ty as $($path_to_types)*::Guest>::{camel}>(arg0)
+    }}
+"#
+                );
+            } else {
+                let dtor_symbol = make_external_symbol(
+                    &module,
+                    &(String::from("[dtor]") + &name),
+                    AbiVariant::GuestExport,
+                );
+                uwriteln!(
+                    self.src,
+                    r#"
                 const _: () = {{
                     #[doc(hidden)]
-                    #[unsafe(export_name = "{export_prefix}{module}#[dtor]{name}")]
+                    #[cfg_attr(target_arch = "wasm32", export_name = "{export_prefix}{module}#[dtor]{name}")]
+                    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
                     #[allow(non_snake_case)]
-                    unsafe extern "C" fn dtor(rep: *mut u8) {{
+                    unsafe extern "C" fn {dtor_symbol}(rep: *mut u8) {{
                         unsafe {{
                             $($path_to_types)*::{camel}::dtor::<
                                 <$ty as $($path_to_types)*::Guest>::{camel}
@@ -339,7 +394,8 @@ macro_rules! {macro_name} {{
                     }}
                 }};
                 "#
-            );
+                );
+            }
         }
         uwriteln!(self.src, "}};);");
         uwriteln!(self.src, "}}");
@@ -546,8 +602,11 @@ macro_rules! {macro_name} {{
                         };
 
                         let box_ = self.path_to_box();
-                        let code = format!(
-                            r#"
+                        let code = if self.gen.opts.symmetric {
+                            format!("\nimpl FuturePayload for {name} {{}}\n")
+                        } else {
+                            format!(
+                                r#"
 #[doc(hidden)]
 pub mod vtable{ordinal} {{
     fn write(future: u32, value: {name}) -> ::core::pin::Pin<{box_}<dyn ::core::future::Future<Output = bool>>> {{
@@ -557,8 +616,13 @@ pub mod vtable{ordinal} {{
             let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); {size}]);
             let address = buffer.0.as_mut_ptr() as *mut u8;
             {lower}
+            #[link(wasm_import_module = "{module}")]
+            extern "C" {{
+                #[link_name = "[async][future-write-{index}]{func_name}"]
+                fn wit_import(_: u32, _: *mut u8) -> u32;
+            }}
 
-            match unsafe {{ {async_support}::await_future_result(start_write, future, address).await }} {{
+            match unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} {{
                 {async_support}::AsyncWaitResult::Values(_) => true,
                 {async_support}::AsyncWaitResult::End => false,
                 {async_support}::AsyncWaitResult::Error(_) => unreachable!("received error while performing write"),
@@ -629,7 +693,8 @@ pub mod vtable{ordinal} {{
     }}
 }}
                         "#,
-                        );
+                            )
+                        };
 
                         self.r#gen.future_payloads.insert(name, code);
                     }
@@ -713,14 +778,22 @@ for (index, dst) in values.iter_mut().take(count).enumerate() {{
                         };
 
                         let box_ = self.path_to_box();
-                        let code = format!(
-                            r#"
+                        let code = if self.gen.opts.symmetric {
+                            format!("\nimpl StreamPayload for {name} {{}}\n")
+                        } else {
+                            format!(
+                                r#"
 #[doc(hidden)]
 pub mod vtable{ordinal} {{
     fn write(stream: u32, values: &[{name}]) -> ::core::pin::Pin<{box_}<dyn ::core::future::Future<Output = usize> + '_>> {{
         {box_}::pin(async move {{
             {lower_address}
             {lower}
+            #[link(wasm_import_module = "{module}")]
+            extern "C" {{
+                #[link_name = "[async][stream-write-{index}]{func_name}"]
+                fn wit_import(_: u32, _: *mut u8, _: u32) -> u32;
+            }}
 
             let mut total = 0;
             while total < values.len() {{
@@ -748,6 +821,11 @@ pub mod vtable{ordinal} {{
     ) -> ::core::pin::Pin<{box_}<dyn ::core::future::Future<Output = ::std::option::Option<::std::result::Result<usize, {async_support}::ErrorContext>>> + '_>> {{
         {box_}::pin(async move {{
             {lift_address}
+            #[link(wasm_import_module = "{module}")]
+            extern "C" {{
+                #[link_name = "[async][stream-read-{index}]{func_name}"]
+                fn wit_import(_: u32, _: *mut u8, _: u32) -> u32;
+            }}
 
             match unsafe {{
                 {async_support}::await_stream_result(
@@ -810,7 +888,8 @@ pub mod vtable{ordinal} {{
     }}
 }}
                         "#,
-                        );
+                            )
+                        };
 
                         self.r#gen.stream_payloads.insert(name, code);
                     }
@@ -855,6 +934,15 @@ pub mod vtable{ordinal} {{
         self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n");
         let params = self.print_signature(func, false, &sig);
         self.src.push_str("{\n");
+        if self.gen.opts.symmetric
+            && symmetric::has_non_canonical_list_rust(self.resolve, &func.params)
+        {
+            self.needs_deallocate = true;
+            uwriteln!(
+                self.src,
+                "let mut _deallocate: Vec<(*mut u8, _rt::alloc::Layout)> = Vec::new();"
+            );
+        }
         self.src.push_str("unsafe {\n");
 
         self.generate_guest_import_body(&self.wasm_import_module, func, params, async_);
@@ -893,7 +981,11 @@ pub mod vtable{ordinal} {{
         abi::call(
             f.r#gen.resolve,
             AbiVariant::GuestImport,
-            LiftLower::LowerArgsLiftResults,
+            if f.gen.gen.opts.symmetric {
+                LiftLower::Symmetric
+            } else {
+                LiftLower::LowerArgsLiftResults
+            },
             func,
             &mut f,
             async_,
@@ -967,7 +1059,11 @@ pub mod vtable{ordinal} {{
         abi::call(
             f.r#gen.resolve,
             AbiVariant::GuestExport,
-            LiftLower::LiftArgsLowerResults,
+            if f.gen.gen.opts.symmetric {
+                LiftLower::Symmetric
+            } else {
+                LiftLower::LiftArgsLowerResults
+            },
             func,
             &mut f,
             async_,
@@ -1020,7 +1116,9 @@ pub mod vtable{ordinal} {{
                     }}
                 "
             );
-        } else if abi::guest_export_needs_post_return(self.resolve, func) {
+        } else if abi::guest_export_needs_post_return(self.resolve, func)
+            && !self.gen.opts.symmetric
+        {
             uwrite!(
                 self.src,
                 "\
@@ -1061,18 +1159,35 @@ pub mod vtable{ordinal} {{
             Identifier::StreamOrFuturePayload => unreachable!(),
         };
         let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or("");
-        let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref());
-        let export_name = if async_ {
-            format!("[async-lift]{export_name}")
+        let (export_name, external_name) = if self.r#gen.opts.symmetric {
+            let export_name = func.name.clone(); // item_name().to_owned();
+            let mut external_name = make_external_symbol(
+                &wasm_module_export_name.unwrap_or_default(),
+                &func.name,
+                AbiVariant::GuestImport,
+            );
+            if let Some(export_prefix) = self.r#gen.opts.export_prefix.as_ref() {
+                external_name.insert_str(0, export_prefix);
+            }
+            (export_name, external_name)
         } else {
-            export_name.to_string()
+            let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref());
+            let export_name = if async_ {
+                format!("[async-lift]{export_name}")
+            } else {
+                export_name.to_string()
+            };
+            let external_name =
+                make_external_component(&(String::from(export_prefix) + &export_name));
+            (export_name, external_name)
         };
         uwrite!(
             self.src,
             "\
-                #[unsafe(export_name = \"{export_prefix}{export_name}\")]
-                unsafe extern \"C\" fn export_{name_snake}\
-",
+                #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}{export_name}\")]
+                #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)]
+                unsafe extern \"C\" fn {external_name}\
+        ",
         );
 
         let params = self.print_export_sig(func, async_);
@@ -1097,13 +1212,20 @@ pub mod vtable{ordinal} {{
                     }}
                 "
             );
-        } else if abi::guest_export_needs_post_return(self.resolve, func) {
+        } else if abi::guest_export_needs_post_return(self.resolve, func)
+            && !self.gen.opts.symmetric
+        {
+            let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or("");
+            let external_name = make_external_component(export_prefix)
+                + "cabi_post_"
+                + &make_external_component(&export_name);
             uwrite!(
                 self.src,
                 "\
-                    #[unsafe(export_name = \"{export_prefix}cabi_post_{export_name}\")]
-                    unsafe extern \"C\" fn _post_return_{name_snake}\
-"
+                    #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}cabi_post_{export_name}\")]
+                    #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)]
+                    unsafe extern \"C\" fn {external_name}\
+            "
             );
             let params = self.print_post_return_sig(func);
             self.src.push_str("{\n");
@@ -1118,7 +1240,12 @@ pub mod vtable{ordinal} {{
 
     fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec<String> {
         self.src.push_str("(");
-        let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func);
+        let sig = abi::wasm_signature_symmetric(
+            self.resolve,
+            AbiVariant::GuestExport,
+            func,
+            self.gen.opts.symmetric,
+        );
         let mut params = Vec::new();
         for (i, param) in sig.params.iter().enumerate() {
             let name = format!("arg{}", i);
@@ -2475,23 +2602,28 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
 
                     impl {camel} {{
                         #[doc(hidden)]
-                        pub unsafe fn from_handle(handle: u32) -> Self {{
+                        pub unsafe fn from_handle(handle: {handle_type}) -> Self {{
                             Self {{
                                 handle: unsafe {{ {resource}::from_handle(handle) }},
                             }}
                         }}
 
                         #[doc(hidden)]
-                        pub fn take_handle(&self) -> u32 {{
+                        pub fn take_handle(&self) -> {handle_type} {{
                             {resource}::take_handle(&self.handle)
                         }}
 
                         #[doc(hidden)]
-                        pub fn handle(&self) -> u32 {{
+                        pub fn handle(&self) -> {handle_type} {{
                             {resource}::handle(&self.handle)
                         }}
                     }}
-                "#
+                "#,
+                handle_type = if self.gen.opts.symmetric {
+                    "usize"
+                } else {
+                    "u32"
+                }
             );
             self.wasm_import_module.to_string()
         } else {
@@ -2548,19 +2680,19 @@ impl {camel} {{
     }}
 
     #[doc(hidden)]
-    pub unsafe fn from_handle(handle: u32) -> Self {{
+    pub unsafe fn from_handle(handle: {handle_type}) -> Self {{
         Self {{
             handle: unsafe {{ {resource}::from_handle(handle) }},
         }}
     }}
 
     #[doc(hidden)]
-    pub fn take_handle(&self) -> u32 {{
+    pub fn take_handle(&self) -> {handle_type} {{
         {resource}::take_handle(&self.handle)
     }}
 
     #[doc(hidden)]
-    pub fn handle(&self) -> u32 {{
+    pub fn handle(&self) -> {handle_type} {{
         {resource}::handle(&self.handle)
     }}
 
@@ -2624,34 +2756,49 @@ impl<'a> {camel}Borrow<'a>{{
        self.rep.cast()
     }}
 }}
-                "#
+                "#,
+                handle_type = if self.gen.opts.symmetric {
+                    "usize"
+                } else {
+                    "u32"
+                }
             );
-            format!("[export]{module}")
+            if self.gen.opts.symmetric {
+                module.clone()
+            } else {
+                format!("[export]{module}")
+            }
         };
 
         let wasm_resource = self.path_to_wasm_resource();
+        let export_name = make_external_symbol(
+            &wasm_import_module,
+            &format!("[resource-drop]{name}"),
+            AbiVariant::GuestImport,
+        );
         uwriteln!(
             self.src,
             r#"
                 unsafe impl {wasm_resource} for {camel} {{
                      #[inline]
-                     unsafe fn drop(_handle: u32) {{
-                         #[cfg(not(target_arch = "wasm32"))]
-                         unreachable!();
-
-                         #[cfg(target_arch = "wasm32")]
+                     unsafe fn drop(_handle: {handle_type}) {{
                          {{
                              #[link(wasm_import_module = "{wasm_import_module}")]
                              unsafe extern "C" {{
-                                 #[link_name = "[resource-drop]{name}"]
-                                 fn drop(_: u32);
+                                 #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]{name}")]
+                                 fn {export_name}(_: {handle_type});
                              }}
 
-                             unsafe {{ drop(_handle) }};
+                             unsafe {{ {export_name}(_handle) }};
                          }}
                      }}
                 }}
-            "#
+            "#,
+            handle_type = if self.gen.opts.symmetric {
+                "usize"
+            } else {
+                "u32"
+            }
         );
     }
 
diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs
index 1f7151b4f..c6e089bd8 100644
--- a/crates/rust/src/lib.rs
+++ b/crates/rust/src/lib.rs
@@ -25,6 +25,12 @@ struct InterfaceName {
     path: String,
 }
 
+#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
+enum Direction {
+    Import,
+    Export,
+}
+
 #[derive(Default)]
 struct RustWasm {
     types: Types,
@@ -50,6 +56,10 @@ struct RustWasm {
     /// Maps wit interface and type names to their Rust identifiers
     with: GenerationConfiguration,
 
+    // needed for symmetric disambiguation
+    interface_prefixes: HashMap<(Direction, WorldKey), String>,
+    import_prefix: Option<String>,
+
     future_payloads: IndexMap<String, String>,
     stream_payloads: IndexMap<String, String>,
 }
@@ -294,10 +304,23 @@ pub struct Opts {
     #[cfg_attr(feature = "clap", arg(long))]
     pub pub_export_macro: bool,
 
+    /// Generate code for 64bit wasm
+    #[cfg_attr(feature = "clap", arg(long))]
+    pub wasm64: bool,
+
     /// Whether to generate unused structures, not generated by default (false)
     #[cfg_attr(feature = "clap", arg(long))]
     pub generate_unused_types: bool,
 
+    /// Symmetric ABI, this enables to directly link components to each
+    /// other and removes the primary distinction between host and guest.
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub symmetric: bool,
+
+    /// Flip import and export on world (used for symmetric testing)
+    #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))]
+    pub invert_direction: bool,
+
     /// Whether or not to generate helper function/constants to help link custom
     /// sections into the final output.
     ///
@@ -339,7 +362,11 @@ impl RustWasm {
         resolve: &'a Resolve,
         in_import: bool,
     ) -> InterfaceGenerator<'a> {
-        let mut sizes = SizeAlign::default();
+        let mut sizes = if self.opts.symmetric {
+            SizeAlign::new_symmetric()
+        } else {
+            SizeAlign::default()
+        };
         sizes.fill(resolve);
 
         InterfaceGenerator {
@@ -353,6 +380,7 @@ impl RustWasm {
             return_pointer_area_size: Default::default(),
             return_pointer_area_align: Default::default(),
             needs_runtime_module: false,
+            needs_deallocate: false,
         }
     }
 
@@ -416,7 +444,11 @@ impl RustWasm {
     }
 
     fn async_support_path(&self) -> String {
-        format!("{}::async_support", self.runtime_path())
+        if self.opts.symmetric {
+            "wit_bindgen_symmetric_rt::async_support".into()
+        } else {
+            format!("{}::async_support", self.runtime_path())
+        }
     }
 
     fn name_interface(
@@ -481,15 +513,27 @@ impl RustWasm {
 
         if !self.future_payloads.is_empty() {
             let async_support = self.async_support_path();
+            let vtable_def = if self.opts.symmetric {
+                "".into()
+            } else {
+                format!(
+                    "
+        const VTABLE: &'static {async_support}::FutureVtable<Self>;
+    "
+                )
+            };
+            let construct = if self.opts.symmetric {
+                format!("{async_support}::future_support::new_future()")
+            } else {
+                format!("unsafe {{ {async_support}::future_new::<T>(T::VTABLE) }}")
+            };
             self.src.push_str(&format!(
                 "\
 pub mod wit_future {{
     #![allow(dead_code, unused_variables, clippy::all)]
 
     #[doc(hidden)]
-    pub trait FuturePayload: Unpin + Sized + 'static {{
-        const VTABLE: &'static {async_support}::FutureVtable<Self>;
-    }}"
+    pub trait FuturePayload: Unpin + Sized + 'static {{{vtable_def}}}"
             ));
             for code in self.future_payloads.values() {
                 self.src.push_str(code);
@@ -498,7 +542,7 @@ pub mod wit_future {{
                 "\
     /// Creates a new Component Model `future` with the specified payload type.
     pub fn new<T: FuturePayload>() -> ({async_support}::FutureWriter<T>, {async_support}::FutureReader<T>) {{
-        unsafe {{ {async_support}::future_new::<T>(T::VTABLE) }}
+        {construct}
     }}
 }}
                 ",
@@ -507,14 +551,26 @@ pub mod wit_future {{
 
         if !self.stream_payloads.is_empty() {
             let async_support = self.async_support_path();
+            let vtable_def = if self.opts.symmetric {
+                "".into()
+            } else {
+                format!(
+                    "
+        const VTABLE: &'static {async_support}::StreamVtable<Self>;
+    "
+                )
+            };
+            let construct = if self.opts.symmetric {
+                format!("{async_support}::stream_support::new_stream()")
+            } else {
+                format!("unsafe {{ {async_support}::stream_new::<T>(T::VTABLE) }}")
+            };
             self.src.push_str(&format!(
                 "\
 pub mod wit_stream {{
     #![allow(dead_code, unused_variables, clippy::all)]
 
-    pub trait StreamPayload: Unpin + Sized + 'static {{
-        const VTABLE: &'static {async_support}::StreamVtable<Self>;
-    }}"
+    pub trait StreamPayload: Unpin + Sized + 'static {{{vtable_def}}}"
             ));
             for code in self.stream_payloads.values() {
                 self.src.push_str(code);
@@ -523,7 +579,7 @@ pub mod wit_stream {{
                 &format!("\
     /// Creates a new Component Model `stream` with the specified payload type.
     pub fn new<T: StreamPayload>() -> ({async_support}::StreamWriter<T>, {async_support}::StreamReader<T>) {{
-        unsafe {{ {async_support}::stream_new::<T>(T::VTABLE) }}
+        {construct}
     }}
 }}
                 "),
@@ -662,12 +718,12 @@ pub fn run_ctors_once() {{
             }
 
             RuntimeItem::ResourceType => {
-                self.src.push_str(
+                self.src.push_str(&format!(
                     r#"
 
 use core::fmt;
 use core::marker;
-use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
+use core::sync::atomic::{{{atomic_type}, Ordering::Relaxed}};
 
 /// A type which represents a component model resource, either imported or
 /// exported into this component.
@@ -682,36 +738,36 @@ use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
 /// This type is primarily used in generated code for exported and imported
 /// resources.
 #[repr(transparent)]
-pub struct Resource<T: WasmResource> {
-    // NB: This would ideally be `u32` but it is not. The fact that this has
+pub struct Resource<T: WasmResource> {{
+    // NB: This would ideally be `{handle_type}` but it is not. The fact that this has
     // interior mutability is not exposed in the API of this type except for the
     // `take_handle` method which is supposed to in theory be private.
     //
     // This represents, almost all the time, a valid handle value. When it's
-    // invalid it's stored as `u32::MAX`.
-    handle: AtomicU32,
+    // invalid it's stored as `{invalid_value}`.
+    handle: {atomic_type},
     _marker: marker::PhantomData<T>,
-}
+}}
 
 /// A trait which all wasm resources implement, namely providing the ability to
 /// drop a resource.
 ///
 /// This generally is implemented by generated code, not user-facing code.
 #[allow(clippy::missing_safety_doc)]
-pub unsafe trait WasmResource {
+pub unsafe trait WasmResource {{
     /// Invokes the `[resource-drop]...` intrinsic.
-    unsafe fn drop(handle: u32);
-}
+    unsafe fn drop(handle: {handle_type});
+}}
 
-impl<T: WasmResource> Resource<T> {
+impl<T: WasmResource> Resource<T> {{
     #[doc(hidden)]
-    pub unsafe fn from_handle(handle: u32) -> Self {
-        debug_assert!(handle != u32::MAX);
-        Self {
-            handle: AtomicU32::new(handle),
+    pub unsafe fn from_handle(handle: {handle_type}) -> Self {{
+        debug_assert!(handle != {invalid_value});
+        Self {{
+            handle: {atomic_type}::new(handle),
             _marker: marker::PhantomData,
-        }
-    }
+        }}
+    }}
 
     /// Takes ownership of the handle owned by `resource`.
     ///
@@ -726,41 +782,48 @@ impl<T: WasmResource> Resource<T> {
     /// `take_handle` should only be exposed internally to generated code, not
     /// to user code.
     #[doc(hidden)]
-    pub fn take_handle(resource: &Resource<T>) -> u32 {
-        resource.handle.swap(u32::MAX, Relaxed)
-    }
+    pub fn take_handle(resource: &Resource<T>) -> {handle_type} {{
+        resource.handle.swap({invalid_value}, Relaxed)
+    }}
 
     #[doc(hidden)]
-    pub fn handle(resource: &Resource<T>) -> u32 {
+    pub fn handle(resource: &Resource<T>) -> {handle_type} {{
         resource.handle.load(Relaxed)
-    }
-}
+    }}
+}}
 
-impl<T: WasmResource> fmt::Debug for Resource<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl<T: WasmResource> fmt::Debug for Resource<T> {{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{
         f.debug_struct("Resource")
             .field("handle", &self.handle)
             .finish()
-    }
-}
+    }}
+}}
 
-impl<T: WasmResource> Drop for Resource<T> {
-    fn drop(&mut self) {
-        unsafe {
-            match self.handle.load(Relaxed) {
+impl<T: WasmResource> Drop for Resource<T> {{
+    fn drop(&mut self) {{
+        unsafe {{
+            match self.handle.load(Relaxed) {{
                 // If this handle was "taken" then don't do anything in the
                 // destructor.
-                u32::MAX => {}
+                {invalid_value} => {{}}
 
                 // ... but otherwise do actually destroy it with the imported
                 // component model intrinsic as defined through `T`.
                 other => T::drop(other),
-            }
-        }
-    }
-}
+            }}
+        }}
+    }}
+}}
                     "#,
-                );
+                    atomic_type = if self.opts.symmetric {
+                        "AtomicUsize"
+                    } else {
+                        "AtomicU32"
+                    },
+                    invalid_value = if self.opts.symmetric { "0" } else { "u32::MAX" },
+                    handle_type = if self.opts.symmetric { "usize" } else { "u32" }
+                ));
             }
         }
     }
@@ -1114,6 +1177,12 @@ impl WorldGenerator for RustWasm {
         id: InterfaceId,
         _files: &mut Files,
     ) -> Result<()> {
+        if let Some(prefix) = self
+            .interface_prefixes
+            .get(&(Direction::Import, name.clone()))
+        {
+            self.import_prefix = Some(prefix.clone());
+        }
         let mut to_define = Vec::new();
         for (name, ty_id) in resolve.interfaces[id].types.iter() {
             let full_name = full_wit_type_name(resolve, *ty_id);
@@ -1151,6 +1220,7 @@ impl WorldGenerator for RustWasm {
 
         r#gen.finish_append_submodule(&snake, module_path, docs);
 
+        let _ = self.import_prefix.take();
         Ok(())
     }
 
@@ -1178,6 +1248,14 @@ impl WorldGenerator for RustWasm {
         id: InterfaceId,
         _files: &mut Files,
     ) -> Result<()> {
+        let old_prefix = self.opts.export_prefix.clone();
+        if let Some(prefix) = self
+            .interface_prefixes
+            .get(&(Direction::Export, name.clone()))
+        {
+            self.opts.export_prefix =
+                Some(prefix.clone() + old_prefix.as_ref().unwrap_or(&String::new()));
+        }
         let mut to_define = Vec::new();
         for (name, ty_id) in resolve.interfaces[id].types.iter() {
             let full_name = full_wit_type_name(resolve, *ty_id);
@@ -1230,6 +1308,7 @@ impl WorldGenerator for RustWasm {
             let stub = r#gen.finish();
             self.src.push_str(&stub);
         }
+        self.opts.export_prefix = old_prefix;
         Ok(())
     }
 
@@ -1402,6 +1481,46 @@ impl WorldGenerator for RustWasm {
 
         Ok(())
     }
+
+    fn apply_resolve_options(&mut self, resolve: &mut Resolve, world: &mut WorldId) {
+        if self.opts.invert_direction {
+            resolve.invert_direction(*world);
+        }
+        if self.opts.symmetric {
+            let world = &resolve.worlds[*world];
+            let exports: HashMap<&WorldKey, &WorldItem> = world.exports.iter().collect();
+            for (key, _item) in world.imports.iter() {
+                // duplicate found
+                if exports.contains_key(key)
+                    && !self
+                        .interface_prefixes
+                        .contains_key(&(Direction::Import, key.clone()))
+                    && !self
+                        .interface_prefixes
+                        .contains_key(&(Direction::Export, key.clone()))
+                {
+                    self.interface_prefixes.insert(
+                        (Direction::Import, key.clone()),
+                        (if self.opts.invert_direction {
+                            "exp_"
+                        } else {
+                            "imp_"
+                        })
+                        .into(),
+                    );
+                    self.interface_prefixes.insert(
+                        (Direction::Export, key.clone()),
+                        (if self.opts.invert_direction {
+                            "imp_"
+                        } else {
+                            "exp_"
+                        })
+                        .into(),
+                    );
+                }
+            }
+        }
+    }
 }
 
 fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> Vec<String> {
diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock
new file mode 100644
index 000000000..d4123b331
--- /dev/null
+++ b/crates/symmetric_executor/Cargo.lock
@@ -0,0 +1,196 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "dummy-rt"
+version = "0.1.0"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.167"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "symmetric_executor"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+ "libc",
+]
+
+[[package]]
+name = "symmetric_stream"
+version = "0.1.0"
+dependencies = [
+ "dummy-rt",
+ "symmetric_executor",
+ "wit-bindgen-symmetric-rt",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+dependencies = [
+ "dummy-rt",
+ "futures",
+]
diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml
new file mode 100644
index 000000000..1cfed0fe6
--- /dev/null
+++ b/crates/symmetric_executor/Cargo.toml
@@ -0,0 +1,28 @@
+[workspace]
+package.version = "0.1.0"
+package.edition = "2021"
+members = [ "dummy-rt","symmetric_stream","rust-client" ]
+
+[package]
+name = "symmetric_executor"
+edition.workspace = true
+version.workspace = true
+
+[dependencies]
+futures = "0.3.31"
+libc = "0.2.167"
+#wit-bindgen = { path = "../guest-rust" }
+#wit-bindgen-rt = { path = "../guest-rust/rt" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "dummy-rt"
+
+[lib]
+crate-type = ["cdylib"]
+
+[features]
+# always off feature
+never = []
+# output debugging information
+trace = []
diff --git a/crates/symmetric_executor/cpp-client/.gitignore b/crates/symmetric_executor/cpp-client/.gitignore
new file mode 100644
index 000000000..198698796
--- /dev/null
+++ b/crates/symmetric_executor/cpp-client/.gitignore
@@ -0,0 +1,2 @@
+/*.o
+/libruntime.a
diff --git a/crates/symmetric_executor/cpp-client/Makefile b/crates/symmetric_executor/cpp-client/Makefile
new file mode 100644
index 000000000..a49f0a34b
--- /dev/null
+++ b/crates/symmetric_executor/cpp-client/Makefile
@@ -0,0 +1,9 @@
+CXXFLAGS=-I../../cpp/helper-types -g -fPIC
+
+all: libruntime.a
+
+libruntime.a: module.o
+	${AR} rcuvs $@ $^
+
+clean:
+	-rm module.o libruntime.a
diff --git a/crates/symmetric_executor/cpp-client/generate.sh b/crates/symmetric_executor/cpp-client/generate.sh
new file mode 100644
index 000000000..099a2d3c2
--- /dev/null
+++ b/crates/symmetric_executor/cpp-client/generate.sh
@@ -0,0 +1 @@
+../../../target/debug/wit-bindgen cpp ../wit -w module --symmetric --new-api
diff --git a/crates/symmetric_executor/cpp-client/module.cpp b/crates/symmetric_executor/cpp-client/module.cpp
new file mode 100644
index 000000000..b24ac613d
--- /dev/null
+++ b/crates/symmetric_executor/cpp-client/module.cpp
@@ -0,0 +1,246 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+
+// Ensure that the *_component_type.o object is linked in
+#ifdef __wasm32__
+extern void __component_type_object_force_link_module(void);
+void __component_type_object_force_link_module_public_use_in_this_compilation_unit(void) {
+  __component_type_object_force_link_module();
+}
+#endif
+#include "module_cpp.h"
+#include <cstdlib> // realloc
+
+extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size);
+
+__attribute__((__weak__, __export_name__("cabi_realloc")))
+void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
+  (void) old_size;
+  if (new_size == 0) return (void*) align;
+  void *ret = realloc(ptr, new_size);
+  if (!ret) abort();
+  return ret;
+}
+
+
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(uint8_t*);
+extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(int64_t);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator();
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(uint8_t*, uint8_t*, uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer(uint8_t*, int64_t);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address(uint8_t*);
+extern "C" int64_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size(uint8_t*, int64_t);
+extern "C" int64_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj();
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone(uint8_t*);
+extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading(uint8_t*, uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result(uint8_t*, uint8_t*);
+extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(uint8_t*);
+extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing(uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(uint8_t*, int32_t, uint8_t*);
+extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate(uint8_t*);
+symmetric::runtime::symmetric_executor::CallbackFunction::~CallbackFunction()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(handle);
+  }
+}
+symmetric::runtime::symmetric_executor::CallbackFunction::CallbackFunction(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+symmetric::runtime::symmetric_executor::CallbackData::~CallbackData()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(handle);
+  }
+}
+symmetric::runtime::symmetric_executor::CallbackData::CallbackData(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+symmetric::runtime::symmetric_executor::EventSubscription::~EventSubscription()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(handle);
+  }
+}
+bool symmetric::runtime::symmetric_executor::EventSubscription::Ready() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((*this).get_handle());
+  return (bool(ret));
+}
+symmetric::runtime::symmetric_executor::EventSubscription symmetric::runtime::symmetric_executor::EventSubscription::FromTimeout(uint64_t nanoseconds)
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout((int64_t(nanoseconds)));
+  return wit::ResourceImportBase{ret};
+}
+symmetric::runtime::symmetric_executor::EventSubscription symmetric::runtime::symmetric_executor::EventSubscription::Dup() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+void symmetric::runtime::symmetric_executor::EventSubscription::Reset() const
+{
+  symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((*this).get_handle());
+}
+symmetric::runtime::symmetric_executor::EventSubscription::EventSubscription(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+symmetric::runtime::symmetric_executor::EventGenerator::~EventGenerator()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(handle);
+  }
+}
+symmetric::runtime::symmetric_executor::EventGenerator::EventGenerator()
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator();
+  this->handle = wit::ResourceImportBase{ret}.into_handle();
+}
+symmetric::runtime::symmetric_executor::EventSubscription symmetric::runtime::symmetric_executor::EventGenerator::Subscribe() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+void symmetric::runtime::symmetric_executor::EventGenerator::Activate() const
+{
+  symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((*this).get_handle());
+}
+symmetric::runtime::symmetric_executor::EventGenerator::EventGenerator(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+void symmetric::runtime::symmetric_executor::Run()
+{
+  symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+}
+void symmetric::runtime::symmetric_executor::Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data)
+{
+  symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(trigger.into_handle(), callback.into_handle(), data.into_handle());
+}
+symmetric::runtime::symmetric_stream::Address::~Address()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(handle);
+  }
+}
+symmetric::runtime::symmetric_stream::Address::Address(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+symmetric::runtime::symmetric_stream::Buffer::~Buffer()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(handle);
+  }
+}
+symmetric::runtime::symmetric_stream::Buffer::Buffer(Address&& addr, uint64_t capacity)
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer(addr.into_handle(), (int64_t(capacity)));
+  this->handle = wit::ResourceImportBase{ret}.into_handle();
+}
+symmetric::runtime::symmetric_stream::Address symmetric::runtime::symmetric_stream::Buffer::GetAddress() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+uint64_t symmetric::runtime::symmetric_stream::Buffer::GetSize() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size((*this).get_handle());
+  return (uint64_t(ret));
+}
+void symmetric::runtime::symmetric_stream::Buffer::SetSize(uint64_t size) const
+{
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size((*this).get_handle(), (int64_t(size)));
+}
+uint64_t symmetric::runtime::symmetric_stream::Buffer::Capacity() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity((*this).get_handle());
+  return (uint64_t(ret));
+}
+symmetric::runtime::symmetric_stream::Buffer::Buffer(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+symmetric::runtime::symmetric_stream::StreamObj::~StreamObj()
+{
+  if (handle!=nullptr) {
+    symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(handle);
+  }
+}
+symmetric::runtime::symmetric_stream::StreamObj::StreamObj()
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj();
+  this->handle = wit::ResourceImportBase{ret}.into_handle();
+}
+symmetric::runtime::symmetric_stream::StreamObj symmetric::runtime::symmetric_stream::StreamObj::Clone() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+bool symmetric::runtime::symmetric_stream::StreamObj::IsWriteClosed() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed((*this).get_handle());
+  return (bool(ret));
+}
+void symmetric::runtime::symmetric_stream::StreamObj::StartReading(Buffer&& buffer) const
+{
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading((*this).get_handle(), buffer.into_handle());
+}
+void symmetric::runtime::symmetric_stream::StreamObj::WriteReadyActivate() const
+{
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((*this).get_handle());
+}
+symmetric::runtime::symmetric_executor::EventSubscription symmetric::runtime::symmetric_stream::StreamObj::ReadReadySubscribe() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+std::optional<symmetric::runtime::symmetric_stream::Buffer> symmetric::runtime::symmetric_stream::StreamObj::ReadResult() const
+{
+  uintptr_t ret_area[((2*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)];
+  uint8_t* ptr0 = (uint8_t*)(&ret_area);
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((*this).get_handle(), ptr0);
+  std::optional<Buffer> option1;
+  if ((int32_t) (*((uint8_t*) (ptr0 + 0)))) {
+    
+    option1.emplace(wit::ResourceImportBase{*((uint8_t**) (ptr0 + sizeof(void*)))});
+  }
+  return (option1);
+}
+bool symmetric::runtime::symmetric_stream::StreamObj::IsReadyToWrite() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((*this).get_handle());
+  return (bool(ret));
+}
+symmetric::runtime::symmetric_executor::EventSubscription symmetric::runtime::symmetric_stream::StreamObj::WriteReadySubscribe() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+symmetric::runtime::symmetric_stream::Buffer symmetric::runtime::symmetric_stream::StreamObj::StartWriting() const
+{
+  auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing((*this).get_handle());
+  return wit::ResourceImportBase{ret};
+}
+void symmetric::runtime::symmetric_stream::StreamObj::FinishWriting(std::optional<Buffer> &&buffer) const
+{
+  int32_t option2;
+  uint8_t* option3;
+  if ((buffer).has_value()) {
+    Buffer payload1 = (std::move(buffer)).value();
+    option2 = (int32_t(1));
+    option3 = payload1.into_handle();
+  } else {
+    option2 = (int32_t(0));
+    option3 = nullptr;
+  }
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((*this).get_handle(), option2, option3);
+}
+void symmetric::runtime::symmetric_stream::StreamObj::ReadReadyActivate() const
+{
+  symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((*this).get_handle());
+}
+symmetric::runtime::symmetric_stream::StreamObj::StreamObj(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {}
+
+// Component Adapters
diff --git a/crates/symmetric_executor/cpp-client/module_cpp.h b/crates/symmetric_executor/cpp-client/module_cpp.h
new file mode 100644
index 000000000..e4d9a7e9f
--- /dev/null
+++ b/crates/symmetric_executor/cpp-client/module_cpp.h
@@ -0,0 +1,128 @@
+// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT!
+#ifndef __CPP_GUEST_BINDINGS_MODULE_H
+#define __CPP_GUEST_BINDINGS_MODULE_H
+#define WIT_SYMMETRIC
+#include <cstdint>
+#include <utility>
+#include <optional>
+#include <cassert>
+#include <wit-guest.h>
+namespace symmetric {namespace runtime {namespace symmetric_executor {class CallbackFunction : public wit::ResourceImportBase{
+
+  public:
+
+  ~CallbackFunction();
+  CallbackFunction(wit::ResourceImportBase &&);
+  CallbackFunction(CallbackFunction&&) = default;
+  CallbackFunction& operator=(CallbackFunction&&) = default;
+};
+
+class CallbackData : public wit::ResourceImportBase{
+
+  public:
+
+  ~CallbackData();
+  CallbackData(wit::ResourceImportBase &&);
+  CallbackData(CallbackData&&) = default;
+  CallbackData& operator=(CallbackData&&) = default;
+};
+
+class EventSubscription : public wit::ResourceImportBase{
+
+  public:
+
+  ~EventSubscription();
+  bool Ready() const;
+  static EventSubscription FromTimeout(uint64_t nanoseconds);
+  EventSubscription Dup() const;
+  void Reset() const;
+  EventSubscription(wit::ResourceImportBase &&);
+  EventSubscription(EventSubscription&&) = default;
+  EventSubscription& operator=(EventSubscription&&) = default;
+};
+
+class EventGenerator : public wit::ResourceImportBase{
+
+  public:
+
+  ~EventGenerator();
+  EventGenerator();
+  EventSubscription Subscribe() const;
+  void Activate() const;
+  EventGenerator(wit::ResourceImportBase &&);
+  EventGenerator(EventGenerator&&) = default;
+  EventGenerator& operator=(EventGenerator&&) = default;
+};
+
+/// Return value of an async call, lowest bit encoding
+enum class CallStatus : uint8_t {
+  /// For symmetric this means that processing has started, parameters should still remain valid until null,
+  /// params-read = non-null, results-written,done = null
+  kStarted = 0,
+  /// For symmetric: Retry the call (temporarily out of memory)
+  kNotStarted = 1,
+};
+
+/// Return value of an event callback
+enum class CallbackState : uint8_t {
+  /// Call the function again
+  kPending = 0,
+  /// The function has completed, all results are written, data is freed,
+  /// calling the function again is not permitted as data became invalid!
+  kReady = 1,
+};
+
+void Run();
+void Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data);
+}
+namespace symmetric_stream {using EventSubscription = symmetric_executor::EventSubscription;
+class Address : public wit::ResourceImportBase{
+
+  public:
+
+  ~Address();
+  Address(wit::ResourceImportBase &&);
+  Address(Address&&) = default;
+  Address& operator=(Address&&) = default;
+};
+
+class Buffer : public wit::ResourceImportBase{
+
+  public:
+
+  ~Buffer();
+  Buffer(Address&& addr, uint64_t capacity);
+  Address GetAddress() const;
+  uint64_t GetSize() const;
+  void SetSize(uint64_t size) const;
+  uint64_t Capacity() const;
+  Buffer(wit::ResourceImportBase &&);
+  Buffer(Buffer&&) = default;
+  Buffer& operator=(Buffer&&) = default;
+};
+
+class StreamObj : public wit::ResourceImportBase{
+
+  public:
+
+  ~StreamObj();
+  StreamObj();
+  StreamObj Clone() const;
+  bool IsWriteClosed() const;
+  void StartReading(Buffer&& buffer) const;
+  void WriteReadyActivate() const;
+  symmetric_executor::EventSubscription ReadReadySubscribe() const;
+  std::optional<Buffer> ReadResult() const;
+  bool IsReadyToWrite() const;
+  symmetric_executor::EventSubscription WriteReadySubscribe() const;
+  Buffer StartWriting() const;
+  void FinishWriting(std::optional<Buffer> &&buffer) const;
+  void ReadReadyActivate() const;
+  StreamObj(wit::ResourceImportBase &&);
+  StreamObj(StreamObj&&) = default;
+  StreamObj& operator=(StreamObj&&) = default;
+};
+
+}}}
+
+#endif
diff --git a/crates/symmetric_executor/dummy-rt/Cargo.toml b/crates/symmetric_executor/dummy-rt/Cargo.toml
new file mode 100644
index 000000000..afebb3eee
--- /dev/null
+++ b/crates/symmetric_executor/dummy-rt/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "dummy-rt"
+version.workspace = true
+edition.workspace = true
+
+[dependencies]
diff --git a/crates/symmetric_executor/dummy-rt/src/lib.rs b/crates/symmetric_executor/dummy-rt/src/lib.rs
new file mode 100644
index 000000000..bf3a5a920
--- /dev/null
+++ b/crates/symmetric_executor/dummy-rt/src/lib.rs
@@ -0,0 +1,6 @@
+// this crate tries to minimize dependencies introduced by generated
+// bindings
+
+pub mod rt {
+    pub fn maybe_link_cabi_realloc() {}
+}
diff --git a/crates/symmetric_executor/generate.sh b/crates/symmetric_executor/generate.sh
new file mode 100755
index 000000000..8f8b24ab5
--- /dev/null
+++ b/crates/symmetric_executor/generate.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+(cd rust-client/src;../../../../target/debug/wit-bindgen rust ../../wit -w module --symmetric --async none)
+(cd src;../../../target/debug/wit-bindgen rust ../wit -w executor --symmetric --async none)
+(cd symmetric_stream/src;../../../../target/debug/wit-bindgen rust ../../wit -w stream-impl --symmetric --async none)
diff --git a/crates/symmetric_executor/rust-client/Cargo.lock b/crates/symmetric_executor/rust-client/Cargo.lock
new file mode 100644
index 000000000..4bd09ef9d
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/Cargo.lock
@@ -0,0 +1,585 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "anyhow"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "backtrace"
+version = "0.3.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "foldhash"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "id-arena"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
+
+[[package]]
+name = "indexmap"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+ "serde",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+
+[[package]]
+name = "leb128"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
+
+[[package]]
+name = "libc"
+version = "0.2.167"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "object"
+version = "0.36.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "semver"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
+name = "serde"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.215"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "spdx"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "wasm-encoder"
+version = "0.221.2"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e"
+dependencies = [
+ "leb128",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.221.2"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "spdx",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.221.2"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e"
+dependencies = [
+ "bitflags",
+ "hashbrown",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen"
+version = "0.36.0"
+dependencies = [
+ "wit-bindgen-rt",
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.36.0"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.36.0"
+dependencies = [
+ "bitflags",
+ "futures",
+ "once_cell",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.36.0"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.36.0"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+dependencies = [
+ "futures",
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.221.2"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.221.2"
+source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml
new file mode 100644
index 000000000..61981dda6
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/Cargo.toml
@@ -0,0 +1,18 @@
+#[workspace]
+
+[package]
+name = "wit-bindgen-symmetric-rt"
+version = "0.36.0"
+edition = "2021"
+
+[dependencies]
+futures = "0.3.31"
+#wit-bindgen = { path = "../../guest-rust" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "../dummy-rt"
+
+[features]
+# always off feature
+never = []
diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs
new file mode 100644
index 000000000..5f1f77cfb
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/src/async_support.rs
@@ -0,0 +1,140 @@
+use futures::{task::Waker, FutureExt};
+use std::{
+    future::Future,
+    pin::Pin,
+    task::{Context, Poll, RawWaker, RawWakerVTable},
+};
+
+use crate::module::symmetric::runtime::symmetric_executor::{
+    self, CallbackState, EventGenerator, EventSubscription,
+};
+
+pub use future_support::{FutureReader, FutureWriter};
+pub use stream_support::{results, Stream, StreamReader, StreamWriter};
+
+pub mod future_support;
+// later make it non-pub
+pub mod stream_support;
+
+// See https://github.com/rust-lang/rust/issues/13231 for the limitation
+// / Send constraint on futures for spawn, loosen later
+// pub unsafe auto trait MaybeSend : Send {}
+// unsafe impl<T> MaybeSend for T where T: Send {}
+
+type BoxFuture = Pin<Box<dyn Future<Output = ()> + 'static>>;
+
+struct FutureState {
+    future: BoxFuture,
+    // signal to activate once the current async future has finished
+    completion_event: Option<EventGenerator>,
+    // the event this future should wake on
+    waiting_for: Option<EventSubscription>,
+}
+
+static VTABLE: RawWakerVTable = RawWakerVTable::new(
+    |_| RawWaker::new(core::ptr::null(), &VTABLE),
+    // `wake` does nothing
+    |_| {},
+    // `wake_by_ref` does nothing
+    |_| {},
+    // Dropping does nothing as we don't allocate anything
+    |_| {},
+);
+
+pub fn new_waker(waiting_for_ptr: *mut Option<EventSubscription>) -> Waker {
+    unsafe { Waker::from_raw(RawWaker::new(waiting_for_ptr.cast(), &VTABLE)) }
+}
+
+unsafe fn poll(state: *mut FutureState) -> Poll<()> {
+    let mut pinned = std::pin::pin!(&mut (*state).future);
+    let waker = new_waker(&mut (&mut *state).waiting_for as *mut Option<EventSubscription>);
+    pinned
+        .as_mut()
+        .poll(&mut Context::from_waker(&waker))
+        .map(|()| {
+            let state_owned = Box::from_raw(state);
+            if let Some(waker) = &state_owned.completion_event {
+                waker.activate();
+            }
+            drop(state_owned);
+        })
+}
+
+pub async fn wait_on(wait_for: EventSubscription) {
+    std::future::poll_fn(move |cx| {
+        if wait_for.ready() {
+            Poll::Ready(())
+        } else {
+            // remember this eventsubscription in the context
+            let data = cx.waker().data();
+            let mut copy = Some(wait_for.dup());
+            std::mem::swap(
+                unsafe { &mut *(data.cast::<Option<EventSubscription>>().cast_mut()) },
+                &mut copy,
+            );
+            Poll::Pending
+        }
+    })
+    .await
+}
+
+extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackState {
+    match unsafe { poll(obj.cast()) } {
+        Poll::Ready(_) => CallbackState::Ready,
+        Poll::Pending => {
+            let state = obj.cast::<FutureState>();
+            let waiting_for = unsafe { &mut *state }.waiting_for.take();
+            super::register(waiting_for.unwrap(), symmetric_callback, obj);
+            // as we registered this callback on a new event stop calling
+            // from the old event
+            CallbackState::Ready
+        }
+    }
+}
+
+/// Poll the future generated by a call to an async-lifted export once, calling
+/// the specified closure (presumably backed by a call to `task.return`) when it
+/// generates a value.
+///
+/// This will return a non-null pointer representing the task if it hasn't
+/// completed immediately; otherwise it returns null.
+#[doc(hidden)]
+pub fn first_poll<T: 'static>(
+    future: impl Future<Output = T> + 'static,
+    fun: impl FnOnce(T) + 'static,
+) -> *mut () {
+    let state = Box::into_raw(Box::new(FutureState {
+        future: Box::pin(future.map(fun)),
+        completion_event: None,
+        waiting_for: None,
+    }));
+    match unsafe { poll(state) } {
+        Poll::Ready(()) => core::ptr::null_mut(),
+        Poll::Pending => {
+            let completion_event = EventGenerator::new();
+            let wait_chain = completion_event.subscribe().take_handle() as *mut ();
+            unsafe { &mut *state }
+                .completion_event
+                .replace(completion_event);
+            let waiting_for = unsafe { &mut *state }.waiting_for.take();
+            super::register(waiting_for.unwrap(), symmetric_callback, state.cast());
+            wait_chain
+        }
+    }
+}
+
+/// Await the completion of a call to an async-lowered import.
+#[doc(hidden)]
+pub async unsafe fn await_result(function: impl Fn() -> *mut u8) {
+    let wait_for = function();
+    if !wait_for.is_null() {
+        let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) };
+        wait_on(wait_for).await;
+    }
+}
+
+pub fn spawn(future: impl Future<Output = ()> + 'static + Send) {
+    let wait_for = first_poll(future, |()| ());
+    let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) };
+    drop(wait_for);
+}
diff --git a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs
new file mode 100644
index 000000000..6be83b49a
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs
@@ -0,0 +1,180 @@
+use std::{
+    future::{Future, IntoFuture},
+    marker::PhantomData,
+    mem::MaybeUninit,
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+use futures::FutureExt;
+
+use crate::symmetric_stream::{Address, Buffer};
+
+use super::{wait_on, Stream};
+
+//use super::Future;
+
+pub struct FutureWriter<T: 'static> {
+    handle: Stream,
+    future: Option<Pin<Box<dyn Future<Output = ()> + 'static + Send>>>,
+    _phantom: PhantomData<T>,
+}
+
+impl<T> FutureWriter<T> {
+    pub fn new(handle: Stream) -> Self {
+        Self {
+            handle,
+            future: None,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn write(self, data: T) -> CancelableWrite<T> {
+        CancelableWrite {
+            writer: self,
+            future: None,
+            data: Some(data),
+        }
+    }
+}
+
+/// Represents a write operation which may be canceled prior to completion.
+pub struct CancelableWrite<T: 'static> {
+    writer: FutureWriter<T>,
+    future: Option<Pin<Box<dyn Future<Output = ()> + 'static + Send>>>,
+    data: Option<T>,
+}
+
+impl<T: Unpin + Send> Future for CancelableWrite<T> {
+    type Output = ();
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
+        let me = self.get_mut();
+
+        // let ready = me.writer.handle.is_ready_to_write();
+
+        if me.future.is_none() {
+            let handle = me.writer.handle.clone();
+            let data = me.data.take().unwrap();
+            me.future = Some(Box::pin(async move {
+                if !handle.is_ready_to_write() {
+                    let subsc = handle.write_ready_subscribe();
+                    wait_on(subsc).await;
+                }
+                let buffer = handle.start_writing();
+                let addr = buffer.get_address().take_handle() as *mut MaybeUninit<T>;
+                unsafe { (*addr).write(data) };
+                buffer.set_size(1);
+                handle.finish_writing(Some(buffer));
+            }) as Pin<Box<dyn Future<Output = _> + Send>>);
+        }
+        match me.future.as_mut().unwrap().poll_unpin(cx) {
+            Poll::Ready(()) => {
+                // me.writer = None;
+                Poll::Ready(())
+            }
+            Poll::Pending => Poll::Pending,
+        }
+    }
+}
+
+/// Represents a read operation which may be canceled prior to completion.
+pub struct CancelableRead<T: 'static> {
+    reader: FutureReader<T>,
+    future: Option<Pin<Box<dyn Future<Output = Option<T>> + 'static + Send>>>,
+}
+
+pub struct FutureReader<T: 'static> {
+    handle: Stream,
+    // future: Option<Pin<Box<dyn Future<Output = Option<Vec<T>>> + 'static + Send>>>,
+    _phantom: PhantomData<T>,
+}
+
+impl<T> FutureReader<T> {
+    pub fn new(handle: Stream) -> Self {
+        Self {
+            handle,
+            // future: None,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn read(self) -> CancelableRead<T> {
+        CancelableRead {
+            reader: self,
+            future: None,
+        }
+    }
+
+    pub fn take_handle(&self) -> *mut () {
+        self.handle.take_handle() as *mut ()
+    }
+}
+
+impl<T: Unpin + Sized + Send> Future for CancelableRead<T> {
+    type Output = Option<T>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<T>> {
+        let me = self.get_mut();
+
+        if me.future.is_none() {
+            let handle = me.reader.handle.clone();
+            me.future = Some(Box::pin(async move {
+                let mut buffer0 = MaybeUninit::<T>::uninit();
+                let address = unsafe { Address::from_handle(&mut buffer0 as *mut _ as usize) };
+                let buffer = Buffer::new(address, 1);
+                handle.start_reading(buffer);
+                let subsc = handle.read_ready_subscribe();
+                subsc.reset();
+                wait_on(subsc).await;
+                let buffer2 = handle.read_result();
+                if let Some(buffer2) = buffer2 {
+                    let count = buffer2.get_size();
+                    if count > 0 {
+                        Some(unsafe { buffer0.assume_init() })
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
+            }) as Pin<Box<dyn Future<Output = _> + Send>>);
+        }
+
+        match me.future.as_mut().unwrap().as_mut().poll(cx) {
+            Poll::Ready(v) => {
+                me.future = None;
+                Poll::Ready(v)
+            }
+            Poll::Pending => Poll::Pending,
+        }
+    }
+}
+
+impl<T> CancelableRead<T> {
+    pub fn cancel(mut self) -> FutureReader<T> {
+        self.cancel_mut()
+    }
+
+    fn cancel_mut(&mut self) -> FutureReader<T> {
+        todo!()
+    }
+}
+
+impl<T: Send + Unpin + Sized> IntoFuture for FutureReader<T> {
+    type Output = Option<T>;
+    type IntoFuture = CancelableRead<T>;
+
+    /// Convert this object into a `Future` which will resolve when a value is
+    /// written to the writable end of this `future` (yielding a `Some` result)
+    /// or when the writable end is dropped (yielding a `None` result).
+    fn into_future(self) -> Self::IntoFuture {
+        self.read()
+    }
+}
+
+pub fn new_future<T: 'static>() -> (FutureWriter<T>, FutureReader<T>) {
+    let handle = Stream::new();
+    let handle2 = handle.clone();
+    (FutureWriter::new(handle), FutureReader::new(handle2))
+}
diff --git a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs
new file mode 100644
index 000000000..a4c15d786
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs
@@ -0,0 +1,228 @@
+pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream;
+use crate::{
+    async_support::wait_on,
+    symmetric_stream::{Address, Buffer},
+};
+use {
+    futures::sink::Sink,
+    std::{
+        convert::Infallible,
+        fmt,
+        future::Future,
+        iter,
+        marker::PhantomData,
+        mem::{self, MaybeUninit},
+        pin::Pin,
+        task::{Context, Poll},
+    },
+};
+
+fn ceiling(x: usize, y: usize) -> usize {
+    (x / y) + if x % y == 0 { 0 } else { 1 }
+}
+
+pub mod results {
+    pub const BLOCKED: isize = -1;
+    pub const CLOSED: isize = isize::MIN;
+    pub const CANCELED: isize = 0;
+}
+
+pub struct StreamWriter<T: 'static> {
+    handle: Stream,
+    future: Option<Pin<Box<dyn Future<Output = ()> + 'static + Send>>>,
+    _phantom: PhantomData<T>,
+}
+
+impl<T> StreamWriter<T> {
+    #[doc(hidden)]
+    pub fn new(handle: Stream) -> Self {
+        Self {
+            handle,
+            future: None,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn cancel(&mut self) {
+        todo!()
+    }
+}
+
+impl<T> fmt::Debug for StreamWriter<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("StreamWriter")
+            .field("handle", &self.handle)
+            .finish()
+    }
+}
+
+impl<T: Unpin> Sink<Vec<T>> for StreamWriter<T> {
+    type Error = Infallible;
+
+    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+        let me = self.get_mut();
+
+        let ready = me.handle.is_ready_to_write();
+
+        // see also StreamReader::poll_next
+        if !ready && me.future.is_none() {
+            let handle = me.handle.clone();
+            me.future = Some(Box::pin(async move {
+                let handle_local = handle;
+                let subscr = handle_local.write_ready_subscribe();
+                subscr.reset();
+                wait_on(subscr).await;
+            }) as Pin<Box<dyn Future<Output = _> + Send>>);
+        }
+
+        if let Some(future) = &mut me.future {
+            match future.as_mut().poll(cx) {
+                Poll::Ready(_) => {
+                    me.future = None;
+                    Poll::Ready(Ok(()))
+                }
+                Poll::Pending => Poll::Pending,
+            }
+        } else {
+            Poll::Ready(Ok(()))
+        }
+    }
+
+    fn start_send(self: Pin<&mut Self>, mut item: Vec<T>) -> Result<(), Self::Error> {
+        let item_len = item.len();
+        let me = self.get_mut();
+        let stream = &me.handle;
+        let buffer = stream.start_writing();
+        let addr = buffer.get_address().take_handle() as *mut u8;
+        let size = buffer.capacity() as usize;
+        assert!(size >= item_len);
+        let slice =
+            unsafe { std::slice::from_raw_parts_mut(addr.cast::<MaybeUninit<T>>(), item_len) };
+        for (a, b) in slice.iter_mut().zip(item.drain(..)) {
+            a.write(b);
+        }
+        buffer.set_size(item_len as u64);
+        stream.finish_writing(Some(buffer));
+        Ok(())
+    }
+
+    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+        self.poll_ready(cx)
+    }
+
+    fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+        self.poll_ready(cx)
+    }
+}
+
+impl<T> Drop for StreamWriter<T> {
+    fn drop(&mut self) {
+        if !self.handle.is_write_closed() {
+            self.handle.finish_writing(None);
+        }
+    }
+}
+
+/// Represents the readable end of a Component Model `stream`.
+pub struct StreamReader<T: 'static> {
+    handle: Stream,
+    future: Option<Pin<Box<dyn Future<Output = Option<Vec<T>>> + 'static + Send>>>,
+    _phantom: PhantomData<T>,
+}
+
+impl<T> fmt::Debug for StreamReader<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("StreamReader")
+            .field("handle", &self.handle)
+            .finish()
+    }
+}
+
+impl<T> StreamReader<T> {
+    #[doc(hidden)]
+    pub fn new(handle: Stream) -> Self {
+        Self {
+            handle,
+            future: None,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub unsafe fn from_handle(handle: *mut u8) -> Self {
+        Self::new(unsafe { Stream::from_handle(handle as usize) })
+    }
+
+    /// Cancel the current pending read operation.
+    ///
+    /// This will panic if no such operation is pending.
+    pub fn cancel(&mut self) {
+        assert!(self.future.is_some());
+        self.future = None;
+    }
+
+    #[doc(hidden)]
+    pub fn take_handle(&self) -> usize {
+        self.handle.take_handle()
+    }
+
+    #[doc(hidden)]
+    // remove this as it is weirder than take_handle
+    pub fn into_handle(self) -> *mut () {
+        self.handle.take_handle() as *mut ()
+    }
+}
+
+impl<T: Unpin + Send> futures::stream::Stream for StreamReader<T> {
+    type Item = Vec<T>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
+        let me = self.get_mut();
+
+        if me.future.is_none() {
+            let handle = me.handle.clone();
+            me.future = Some(Box::pin(async move {
+                let mut buffer0 = iter::repeat_with(MaybeUninit::uninit)
+                    .take(ceiling(4 * 1024, mem::size_of::<T>()))
+                    .collect::<Vec<_>>();
+                let address = unsafe { Address::from_handle(buffer0.as_mut_ptr() as usize) };
+                let buffer = Buffer::new(address, buffer0.len() as u64);
+                handle.start_reading(buffer);
+                let subsc = handle.read_ready_subscribe();
+                subsc.reset();
+                wait_on(subsc).await;
+                let buffer2 = handle.read_result();
+                if let Some(buffer2) = buffer2 {
+                    let count = buffer2.get_size();
+                    buffer0.truncate(count as usize);
+                    Some(unsafe { mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(buffer0) })
+                } else {
+                    None
+                }
+            }) as Pin<Box<dyn Future<Output = _> + Send>>);
+        }
+
+        match me.future.as_mut().unwrap().as_mut().poll(cx) {
+            Poll::Ready(v) => {
+                me.future = None;
+                Poll::Ready(v)
+            }
+            Poll::Pending => Poll::Pending,
+        }
+    }
+}
+
+impl<T> Drop for StreamReader<T> {
+    fn drop(&mut self) {
+        if self.handle.handle() != 0 {
+            self.handle.write_ready_activate();
+        }
+    }
+}
+
+// pub type StreamHandle2 = Stream;
+
+pub fn new_stream<T: 'static>() -> (StreamWriter<T>, StreamReader<T>) {
+    let handle = Stream::new();
+    let handle2 = handle.clone();
+    (StreamWriter::new(handle), StreamReader::new(handle2))
+}
diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs
new file mode 100644
index 000000000..bd9ecc496
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/src/lib.rs
@@ -0,0 +1,44 @@
+use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction};
+pub use module::symmetric::runtime::symmetric_executor::{
+    run, CallbackState, EventGenerator, EventSubscription,
+};
+pub use module::symmetric::runtime::symmetric_stream;
+
+pub mod async_support;
+mod module;
+
+pub fn register(
+    event: EventSubscription,
+    f: extern "C" fn(*mut ()) -> CallbackState,
+    data: *mut (),
+) {
+    let callback = unsafe { CallbackFunction::from_handle(f as *const () as usize) };
+    let cb_data = unsafe { CallbackData::from_handle(data as usize) };
+    symmetric_executor::register(event, callback, cb_data);
+}
+
+#[no_mangle]
+fn cabi_realloc_wit_bindgen_0_37_0(
+    _old_ptr: *mut u8,
+    _old_len: usize,
+    _align: usize,
+    _new_len: usize,
+) -> *mut u8 {
+    todo!()
+}
+
+pub unsafe fn subscribe_event_send_ptr(event_send: *mut ()) -> EventSubscription {
+    let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) };
+    // (unsafe {Arc::from_raw(event_send.cast()) });
+    let subscription = gen.subscribe();
+    // avoid consuming the generator
+    std::mem::forget(gen);
+    subscription
+}
+
+pub unsafe fn activate_event_send_ptr(event_send: *mut ()) {
+    let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) };
+    gen.activate();
+    // avoid consuming the generator
+    std::mem::forget(gen);
+}
diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs
new file mode 100644
index 000000000..3f47e9e41
--- /dev/null
+++ b/crates/symmetric_executor/rust-client/src/module.rs
@@ -0,0 +1,1147 @@
+// Generated by `wit-bindgen` 0.37.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod symmetric {
+    pub mod runtime {
+        /// This interface will only work with symmetric ABI (shared everything),
+        /// it can't be composed with the canonical ABI
+        /// Asynchronous executor functionality for symmetric ABI
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod symmetric_executor {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            /// These pseudo-resources are just used to
+            /// pass pointers to register
+            /// This wraps a user provided function of type
+            /// `fn (callback-data) -> callback-state`
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct CallbackFunction {
+                handle: _rt::Resource<CallbackFunction>,
+            }
+
+            impl CallbackFunction {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for CallbackFunction {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]callback-function"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle);
+                    }
+                }
+            }
+
+            /// This wraps opaque user data, freed by the callback once
+            /// it returns ready
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct CallbackData {
+                handle: _rt::Resource<CallbackData>,
+            }
+
+            impl CallbackData {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for CallbackData {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]callback-data"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle);
+                    }
+                }
+            }
+
+            /// The receiving side of an event
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct EventSubscription {
+                handle: _rt::Resource<EventSubscription>,
+            }
+
+            impl EventSubscription {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for EventSubscription {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]event-subscription"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle);
+                    }
+                }
+            }
+
+            /// A user controlled event
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct EventGenerator {
+                handle: _rt::Resource<EventGenerator>,
+            }
+
+            impl EventGenerator {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for EventGenerator {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]event-generator"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle);
+                    }
+                }
+            }
+
+            /// Return value of an async call, lowest bit encoding
+            #[repr(u8)]
+            #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+            pub enum CallStatus {
+                /// For symmetric this means that processing has started, parameters should still remain valid until null,
+                /// params-read = non-null, results-written,done = null
+                Started,
+                /// For symmetric: Retry the call (temporarily out of memory)
+                NotStarted,
+            }
+            impl ::core::fmt::Debug for CallStatus {
+                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                    match self {
+                        CallStatus::Started => f.debug_tuple("CallStatus::Started").finish(),
+                        CallStatus::NotStarted => f.debug_tuple("CallStatus::NotStarted").finish(),
+                    }
+                }
+            }
+
+            impl CallStatus {
+                #[doc(hidden)]
+                pub unsafe fn _lift(val: u8) -> CallStatus {
+                    if !cfg!(debug_assertions) {
+                        return ::core::mem::transmute(val);
+                    }
+
+                    match val {
+                        0 => CallStatus::Started,
+                        1 => CallStatus::NotStarted,
+
+                        _ => panic!("invalid enum discriminant"),
+                    }
+                }
+            }
+
+            /// Return value of an event callback
+            #[repr(u8)]
+            #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+            pub enum CallbackState {
+                /// Call the function again
+                Pending,
+                /// The function has completed, all results are written, data is freed,
+                /// calling the function again is not permitted as data became invalid!
+                Ready,
+            }
+            impl ::core::fmt::Debug for CallbackState {
+                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                    match self {
+                        CallbackState::Pending => f.debug_tuple("CallbackState::Pending").finish(),
+                        CallbackState::Ready => f.debug_tuple("CallbackState::Ready").finish(),
+                    }
+                }
+            }
+
+            impl CallbackState {
+                #[doc(hidden)]
+                pub unsafe fn _lift(val: u8) -> CallbackState {
+                    if !cfg!(debug_assertions) {
+                        return ::core::mem::transmute(val);
+                    }
+
+                    match val {
+                        0 => CallbackState::Pending,
+                        1 => CallbackState::Ready,
+
+                        _ => panic!("invalid enum discriminant"),
+                    }
+                }
+            }
+
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Whether the event is active (used by poll implementation)
+                pub fn ready(&self) -> bool {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.ready"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready(
+                                _: *mut u8,
+                            ) -> i32;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8);
+                        _rt::bool_lift(ret as u8)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Create a timeout event
+                pub fn from_timeout(nanoseconds: u64) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[static]event-subscription.from-timeout"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(
+                                _: i64,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds));
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe)
+                pub fn dup(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.dup"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8);
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Reset subscription to be inactive, only next trigger will ready it
+                pub fn reset(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.reset"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8);
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn new() -> Self {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[constructor]event-generator"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator(
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator();
+                        EventGenerator::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Get the receiving side (to pass to other parts of the program)
+                pub fn subscribe(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-generator.subscribe"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8);
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Trigger all subscribers
+                pub fn activate(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_executor"))]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-generator.activate"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8);
+                    }
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            /// Wait until all registered events have completed
+            pub fn run() -> () {
+                unsafe {
+                    #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "run")]
+                        fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+                    }
+                    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            /// Register a callback for an event
+            pub fn register(
+                trigger: EventSubscription,
+                callback: CallbackFunction,
+                data: CallbackData,
+            ) -> () {
+                unsafe {
+                    #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "register")]
+                        fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(
+                            _: *mut u8,
+                            _: *mut u8,
+                            _: *mut u8,
+                        );
+                    }
+                    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(
+                        (&trigger).take_handle() as *mut u8,
+                        (&callback).take_handle() as *mut u8,
+                        (&data).take_handle() as *mut u8,
+                    );
+                }
+            }
+        }
+
+        /// language neutral stream implementation
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod symmetric_stream {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            pub type EventSubscription =
+                super::super::super::symmetric::runtime::symmetric_executor::EventSubscription;
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct Address {
+                handle: _rt::Resource<Address>,
+            }
+
+            impl Address {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for Address {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]address"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(_handle);
+                    }
+                }
+            }
+
+            /// special zero allocation/copy data type (caller provided buffer)
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct Buffer {
+                handle: _rt::Resource<Buffer>,
+            }
+
+            impl Buffer {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for Buffer {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]buffer")]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(_handle);
+                    }
+                }
+            }
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct StreamObj {
+                handle: _rt::Resource<StreamObj>,
+            }
+
+            impl StreamObj {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for StreamObj {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_stream"))]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]stream-obj"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(_handle);
+                    }
+                }
+            }
+
+            impl Buffer {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn new(addr: Address, capacity: u64) -> Self {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]buffer")]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer(
+                                _: *mut u8,
+                                _: i64,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer((&addr).take_handle() as *mut u8, _rt::as_i64(&capacity));
+                        Buffer::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl Buffer {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn get_address(&self) -> Address {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]buffer.get-address"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address((self).handle() as *mut u8);
+                        Address::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl Buffer {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn get_size(&self) -> u64 {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]buffer.get-size"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size(
+                                _: *mut u8,
+                            ) -> i64;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size((self).handle() as *mut u8);
+                        ret as u64
+                    }
+                }
+            }
+            impl Buffer {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn set_size(&self, size: u64) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]buffer.set-size"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size(
+                                _: *mut u8,
+                                _: i64,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size((self).handle() as *mut u8, _rt::as_i64(&size));
+                    }
+                }
+            }
+            impl Buffer {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn capacity(&self) -> u64 {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]buffer.capacity"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity(
+                                _: *mut u8,
+                            ) -> i64;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity((self).handle() as *mut u8);
+                        ret as u64
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn new() -> Self {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[constructor]stream-obj"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj(
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj();
+                        StreamObj::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                /// create a new instance e.g. for reading or tasks
+                pub fn clone(&self) -> StreamObj {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.clone"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone((self).handle() as *mut u8);
+                        StreamObj::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                /// reading (in roughly chronological order)
+                pub fn is_write_closed(&self) -> bool {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.is-write-closed"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed(
+                                _: *mut u8,
+                            ) -> i32;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed((self).handle() as *mut u8);
+                        _rt::bool_lift(ret as u8)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn start_reading(&self, buffer: Buffer) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.start-reading"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading(
+                                _: *mut u8,
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8);
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn write_ready_activate(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.write-ready-activate"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((self).handle() as *mut u8);
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn read_ready_subscribe(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.read-ready-subscribe"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((self).handle() as *mut u8);
+                        super::super::super::symmetric::runtime::symmetric_executor::EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                /// none is EOF
+                pub fn read_result(&self) -> Option<Buffer> {
+                    unsafe {
+                        #[cfg_attr(target_pointer_width = "64", repr(align(8)))]
+                        #[cfg_attr(target_pointer_width = "32", repr(align(4)))]
+                        struct RetArea(
+                            [::core::mem::MaybeUninit<u8>; 2 * core::mem::size_of::<*const u8>()],
+                        );
+                        let mut ret_area = RetArea(
+                            [::core::mem::MaybeUninit::uninit();
+                                2 * core::mem::size_of::<*const u8>()],
+                        );
+                        let ptr0 = ret_area.0.as_mut_ptr().cast::<u8>();
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.read-result"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result(
+                                _: *mut u8,
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0);
+                        let l1 = i32::from(*ptr0.add(0).cast::<u8>());
+                        match l1 {
+                            0 => None,
+                            1 => {
+                                let e = {
+                                    let l2 = *ptr0
+                                        .add(core::mem::size_of::<*const u8>())
+                                        .cast::<*mut u8>();
+
+                                    Buffer::from_handle(l2 as usize)
+                                };
+                                Some(e)
+                            }
+                            _ => _rt::invalid_enum_discriminant(),
+                        }
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                /// writing
+                pub fn is_ready_to_write(&self) -> bool {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.is-ready-to-write"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write(
+                                _: *mut u8,
+                            ) -> i32;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((self).handle() as *mut u8);
+                        _rt::bool_lift(ret as u8)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn write_ready_subscribe(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.write-ready-subscribe"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe((self).handle() as *mut u8);
+                        super::super::super::symmetric::runtime::symmetric_executor::EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn start_writing(&self) -> Buffer {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.start-writing"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing((self).handle() as *mut u8);
+                        Buffer::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                /// none is EOF
+                pub fn finish_writing(&self, buffer: Option<Buffer>) -> () {
+                    unsafe {
+                        let (result0_0, result0_1) = match &buffer {
+                            Some(e) => (1i32, (e).take_handle() as *mut u8),
+                            None => (0i32, core::ptr::null_mut()),
+                        };
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.finish-writing"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(
+                                _: *mut u8,
+                                _: i32,
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1);
+                    }
+                }
+            }
+            impl StreamObj {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn read_ready_activate(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]stream-obj.read-ready-activate"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((self).handle() as *mut u8);
+                    }
+                }
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+
+    use core::fmt;
+    use core::marker;
+    use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+
+    /// A type which represents a component model resource, either imported or
+    /// exported into this component.
+    ///
+    /// This is a low-level wrapper which handles the lifetime of the resource
+    /// (namely this has a destructor). The `T` provided defines the component model
+    /// intrinsics that this wrapper uses.
+    ///
+    /// One of the chief purposes of this type is to provide `Deref` implementations
+    /// to access the underlying data when it is owned.
+    ///
+    /// This type is primarily used in generated code for exported and imported
+    /// resources.
+    #[repr(transparent)]
+    pub struct Resource<T: WasmResource> {
+        // NB: This would ideally be `usize` but it is not. The fact that this has
+        // interior mutability is not exposed in the API of this type except for the
+        // `take_handle` method which is supposed to in theory be private.
+        //
+        // This represents, almost all the time, a valid handle value. When it's
+        // invalid it's stored as `0`.
+        handle: AtomicUsize,
+        _marker: marker::PhantomData<T>,
+    }
+
+    /// A trait which all wasm resources implement, namely providing the ability to
+    /// drop a resource.
+    ///
+    /// This generally is implemented by generated code, not user-facing code.
+    #[allow(clippy::missing_safety_doc)]
+    pub unsafe trait WasmResource {
+        /// Invokes the `[resource-drop]...` intrinsic.
+        unsafe fn drop(handle: usize);
+    }
+
+    impl<T: WasmResource> Resource<T> {
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: usize) -> Self {
+            debug_assert!(handle != 0);
+            Self {
+                handle: AtomicUsize::new(handle),
+                _marker: marker::PhantomData,
+            }
+        }
+
+        /// Takes ownership of the handle owned by `resource`.
+        ///
+        /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+        /// ownership. The code generator does not enable that in all situations,
+        /// unfortunately, so this is provided instead.
+        ///
+        /// Also note that `take_handle` is in theory only ever called on values
+        /// owned by a generated function. For example a generated function might
+        /// take `Resource<T>` as an argument but then call `take_handle` on a
+        /// reference to that argument. In that sense the dynamic nature of
+        /// `take_handle` should only be exposed internally to generated code, not
+        /// to user code.
+        #[doc(hidden)]
+        pub fn take_handle(resource: &Resource<T>) -> usize {
+            resource.handle.swap(0, Relaxed)
+        }
+
+        #[doc(hidden)]
+        pub fn handle(resource: &Resource<T>) -> usize {
+            resource.handle.load(Relaxed)
+        }
+    }
+
+    impl<T: WasmResource> fmt::Debug for Resource<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Resource")
+                .field("handle", &self.handle)
+                .finish()
+        }
+    }
+
+    impl<T: WasmResource> Drop for Resource<T> {
+        fn drop(&mut self) {
+            unsafe {
+                match self.handle.load(Relaxed) {
+                    // If this handle was "taken" then don't do anything in the
+                    // destructor.
+                    0 => {}
+
+                    // ... but otherwise do actually destroy it with the imported
+                    // component model intrinsic as defined through `T`.
+                    other => T::drop(other),
+                }
+            }
+        }
+    }
+    pub unsafe fn bool_lift(val: u8) -> bool {
+        if cfg!(debug_assertions) {
+            match val {
+                0 => false,
+                1 => true,
+                _ => panic!("invalid bool discriminant"),
+            }
+        } else {
+            val != 0
+        }
+    }
+
+    pub fn as_i64<T: AsI64>(t: T) -> i64 {
+        t.as_i64()
+    }
+
+    pub trait AsI64 {
+        fn as_i64(self) -> i64;
+    }
+
+    impl<'a, T: Copy + AsI64> AsI64 for &'a T {
+        fn as_i64(self) -> i64 {
+            (*self).as_i64()
+        }
+    }
+
+    impl AsI64 for i64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+
+    impl AsI64 for u64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+    pub unsafe fn invalid_enum_discriminant<T>() -> T {
+        if cfg!(debug_assertions) {
+            panic!("invalid enum discriminant")
+        } else {
+            core::hint::unreachable_unchecked()
+        }
+    }
+}
+
+#[cfg(target_arch = "wasm32")]
+#[unsafe(link_section = "component-type:wit-bindgen:0.37.0:symmetric:runtime@0.1.0:module:encoded world")]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1716] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xb7\x0c\x01A\x02\x01\
+A\x05\x01B\x20\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\
+\x04\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x01m\x02\x07\
+started\x0bnot-started\x04\0\x0bcall-status\x03\0\x04\x01m\x02\x07pending\x05rea\
+dy\x04\0\x0ecallback-state\x03\0\x06\x01h\x02\x01@\x01\x04self\x08\0\x7f\x04\0\x20\
+[method]event-subscription.ready\x01\x09\x01i\x02\x01@\x01\x0bnanosecondsw\0\x0a\
+\x04\0'[static]event-subscription.from-timeout\x01\x0b\x01@\x01\x04self\x08\0\x0a\
+\x04\0\x1e[method]event-subscription.dup\x01\x0c\x01@\x01\x04self\x08\x01\0\x04\0\
+\x20[method]event-subscription.reset\x01\x0d\x01i\x03\x01@\0\0\x0e\x04\0\x1c[con\
+structor]event-generator\x01\x0f\x01h\x03\x01@\x01\x04self\x10\0\x0a\x04\0![meth\
+od]event-generator.subscribe\x01\x11\x01@\x01\x04self\x10\x01\0\x04\0\x20[method\
+]event-generator.activate\x01\x12\x01@\0\x01\0\x04\0\x03run\x01\x13\x01i\0\x01i\x01\
+\x01@\x03\x07trigger\x0a\x08callback\x14\x04data\x15\x01\0\x04\0\x08register\x01\
+\x16\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x02\x03\0\0\x12event\
+-subscription\x01B*\x02\x03\x02\x01\x01\x04\0\x12event-subscription\x03\0\0\x04\0\
+\x07address\x03\x01\x04\0\x06buffer\x03\x01\x04\0\x0astream-obj\x03\x01\x01i\x02\
+\x01i\x03\x01@\x02\x04addr\x05\x08capacityw\0\x06\x04\0\x13[constructor]buffer\x01\
+\x07\x01h\x03\x01@\x01\x04self\x08\0\x05\x04\0\x1a[method]buffer.get-address\x01\
+\x09\x01@\x01\x04self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04\
+self\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[metho\
+d]buffer.capacity\x01\x0a\x01i\x04\x01@\0\0\x0c\x04\0\x17[constructor]stream-obj\
+\x01\x0d\x01h\x04\x01@\x01\x04self\x0e\0\x0c\x04\0\x18[method]stream-obj.clone\x01\
+\x0f\x01@\x01\x04self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\
+\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-read\
+ing\x01\x11\x01@\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-acti\
+vate\x01\x12\x01i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-r\
+eady-subscribe\x01\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stre\
+am-obj.read-result\x01\x16\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\
+\0([method]stream-obj.write-ready-subscribe\x01\x14\x01@\x01\x04self\x0e\0\x06\x04\
+\0\x20[method]stream-obj.start-writing\x01\x17\x01@\x02\x04self\x0e\x06buffer\x15\
+\x01\0\x04\0![method]stream-obj.finish-writing\x01\x18\x04\0&[method]stream-obj.\
+read-ready-activate\x01\x12\x03\0(symmetric:runtime/symmetric-stream@0.1.0\x05\x02\
+\x04\0\x1esymmetric:runtime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0\
+G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.223.0\x10wit-bindge\
+n-rust\x060.37.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs
new file mode 100644
index 000000000..5a827f5aa
--- /dev/null
+++ b/crates/symmetric_executor/src/executor.rs
@@ -0,0 +1,1941 @@
+// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod exports {
+    pub mod symmetric {
+        pub mod runtime {
+            /// This interface will only work with symmetric ABI (shared everything),
+            /// it can't be composed with the canonical ABI
+            /// Asynchronous executor functionality for symmetric ABI
+            #[allow(dead_code, clippy::all)]
+            pub mod symmetric_executor {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+
+                use super::super::super::super::_rt;
+                /// These pseudo-resources are just used to
+                /// pass pointers to register
+                /// This wraps a user provided function of type
+                /// `fn (callback-data) -> callback-state`
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct CallbackFunction {
+                    handle: _rt::Resource<CallbackFunction>,
+                }
+
+                type _CallbackFunctionRep<T> = Option<T>;
+
+                impl CallbackFunction {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `CallbackFunction`.
+                    pub fn new<T: GuestCallbackFunction>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _CallbackFunctionRep<T> = Some(val);
+                        let ptr: *mut _CallbackFunctionRep<T> =
+                            _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestCallbackFunction>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestCallbackFunction>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestCallbackFunction>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestCallbackFunction` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _CallbackFunctionRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestCallbackFunction>(&self) -> *mut _CallbackFunctionRep<T> {
+                        CallbackFunction::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`CallbackFunction`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct CallbackFunctionBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a CallbackFunction>,
+                }
+
+                impl<'a> CallbackFunctionBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestCallbackFunction>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _CallbackFunctionRep<T> {
+                        CallbackFunction::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for CallbackFunction {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(
+                                wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0"
+                            )]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]callback-function"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle);
+                        }
+                    }
+                }
+
+                /// This wraps opaque user data, freed by the callback once
+                /// it returns ready
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct CallbackData {
+                    handle: _rt::Resource<CallbackData>,
+                }
+
+                type _CallbackDataRep<T> = Option<T>;
+
+                impl CallbackData {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `CallbackData`.
+                    pub fn new<T: GuestCallbackData>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _CallbackDataRep<T> = Some(val);
+                        let ptr: *mut _CallbackDataRep<T> = _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestCallbackData>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestCallbackData>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestCallbackData>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestCallbackData` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _CallbackDataRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestCallbackData>(&self) -> *mut _CallbackDataRep<T> {
+                        CallbackData::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`CallbackData`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct CallbackDataBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a CallbackData>,
+                }
+
+                impl<'a> CallbackDataBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestCallbackData>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _CallbackDataRep<T> {
+                        CallbackData::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for CallbackData {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(
+                                wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0"
+                            )]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]callback-data"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle);
+                        }
+                    }
+                }
+
+                /// The receiving side of an event
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct EventSubscription {
+                    handle: _rt::Resource<EventSubscription>,
+                }
+
+                type _EventSubscriptionRep<T> = Option<T>;
+
+                impl EventSubscription {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `EventSubscription`.
+                    pub fn new<T: GuestEventSubscription>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _EventSubscriptionRep<T> = Some(val);
+                        let ptr: *mut _EventSubscriptionRep<T> =
+                            _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestEventSubscription>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestEventSubscription>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestEventSubscription>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestEventSubscription` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _EventSubscriptionRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestEventSubscription>(&self) -> *mut _EventSubscriptionRep<T> {
+                        EventSubscription::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`EventSubscription`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct EventSubscriptionBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a EventSubscription>,
+                }
+
+                impl<'a> EventSubscriptionBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestEventSubscription>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _EventSubscriptionRep<T> {
+                        EventSubscription::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for EventSubscription {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(
+                                wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0"
+                            )]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]event-subscription"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle);
+                        }
+                    }
+                }
+
+                /// A user controlled event
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct EventGenerator {
+                    handle: _rt::Resource<EventGenerator>,
+                }
+
+                type _EventGeneratorRep<T> = Option<T>;
+
+                impl EventGenerator {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `EventGenerator`.
+                    pub fn new<T: GuestEventGenerator>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _EventGeneratorRep<T> = Some(val);
+                        let ptr: *mut _EventGeneratorRep<T> =
+                            _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestEventGenerator>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestEventGenerator>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestEventGenerator>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestEventGenerator` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _EventGeneratorRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestEventGenerator>(&self) -> *mut _EventGeneratorRep<T> {
+                        EventGenerator::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`EventGenerator`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct EventGeneratorBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a EventGenerator>,
+                }
+
+                impl<'a> EventGeneratorBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestEventGenerator>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _EventGeneratorRep<T> {
+                        EventGenerator::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for EventGenerator {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(
+                                wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0"
+                            )]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]event-generator"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle);
+                        }
+                    }
+                }
+
+                /// Return value of an async call, lowest bit encoding
+                #[repr(u8)]
+                #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+                pub enum CallStatus {
+                    /// For symmetric this means that processing has started, parameters should still remain valid until null,
+                    /// params-read = non-null, results-written,done = null
+                    Started,
+                    /// For symmetric: Retry the call (temporarily out of memory)
+                    NotStarted,
+                }
+                impl ::core::fmt::Debug for CallStatus {
+                    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                        match self {
+                            CallStatus::Started => f.debug_tuple("CallStatus::Started").finish(),
+                            CallStatus::NotStarted => {
+                                f.debug_tuple("CallStatus::NotStarted").finish()
+                            }
+                        }
+                    }
+                }
+
+                impl CallStatus {
+                    #[doc(hidden)]
+                    pub unsafe fn _lift(val: u8) -> CallStatus {
+                        if !cfg!(debug_assertions) {
+                            return ::core::mem::transmute(val);
+                        }
+
+                        match val {
+                            0 => CallStatus::Started,
+                            1 => CallStatus::NotStarted,
+
+                            _ => panic!("invalid enum discriminant"),
+                        }
+                    }
+                }
+
+                /// Return value of an event callback
+                #[repr(u8)]
+                #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+                pub enum CallbackState {
+                    /// Call the function again
+                    Pending,
+                    /// The function has completed, all results are written, data is freed,
+                    /// calling the function again is not permitted as data became invalid!
+                    Ready,
+                }
+                impl ::core::fmt::Debug for CallbackState {
+                    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                        match self {
+                            CallbackState::Pending => {
+                                f.debug_tuple("CallbackState::Pending").finish()
+                            }
+                            CallbackState::Ready => f.debug_tuple("CallbackState::Ready").finish(),
+                        }
+                    }
+                }
+
+                impl CallbackState {
+                    #[doc(hidden)]
+                    pub unsafe fn _lift(val: u8) -> CallbackState {
+                        if !cfg!(debug_assertions) {
+                            return ::core::mem::transmute(val);
+                        }
+
+                        match val {
+                            0 => CallbackState::Pending,
+                            1 => CallbackState::Ready,
+
+                            _ => panic!("invalid enum discriminant"),
+                        }
+                    }
+                }
+
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_event_subscription_ready_cabi<
+                    T: GuestEventSubscription,
+                >(
+                    arg0: *mut u8,
+                ) -> i32 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::ready(EventSubscriptionBorrow::lift(arg0 as usize).get());
+                    match result0 {
+                        true => 1,
+                        false => 0,
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_static_event_subscription_from_timeout_cabi<
+                    T: GuestEventSubscription,
+                >(
+                    arg0: i64,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::from_timeout(arg0 as u64);
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_event_subscription_dup_cabi<
+                    T: GuestEventSubscription,
+                >(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::dup(EventSubscriptionBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_event_subscription_reset_cabi<
+                    T: GuestEventSubscription,
+                >(
+                    arg0: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::reset(EventSubscriptionBorrow::lift(arg0 as usize).get());
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_constructor_event_generator_cabi<T: GuestEventGenerator>(
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = EventGenerator::new(T::new());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_event_generator_subscribe_cabi<
+                    T: GuestEventGenerator,
+                >(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::subscribe(EventGeneratorBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_event_generator_activate_cabi<
+                    T: GuestEventGenerator,
+                >(
+                    arg0: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::activate(EventGeneratorBorrow::lift(arg0 as usize).get());
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_run_cabi<T: Guest>() {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::run();
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_register_cabi<T: Guest>(
+                    arg0: *mut u8,
+                    arg1: *mut u8,
+                    arg2: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::register(
+                        EventSubscription::from_handle(arg0 as usize),
+                        CallbackFunction::from_handle(arg1 as usize),
+                        CallbackData::from_handle(arg2 as usize),
+                    );
+                }
+                pub trait Guest {
+                    type CallbackFunction: GuestCallbackFunction;
+                    type CallbackData: GuestCallbackData;
+                    type EventSubscription: GuestEventSubscription;
+                    type EventGenerator: GuestEventGenerator;
+                    /// Wait until all registered events have completed
+                    fn run() -> ();
+                    /// Register a callback for an event
+                    fn register(
+                        trigger: EventSubscription,
+                        callback: CallbackFunction,
+                        data: CallbackData,
+                    ) -> ();
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_callbackFunction_cabi<T: GuestCallbackFunction>(
+                    arg0: usize,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    CallbackFunction::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestCallbackFunction: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_callbackData_cabi<T: GuestCallbackData>(arg0: usize) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    CallbackData::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestCallbackData: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_eventSubscription_cabi<T: GuestEventSubscription>(
+                    arg0: usize,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    EventSubscription::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestEventSubscription: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+
+                    /// Whether the event is active (used by poll implementation)
+                    fn ready(&self) -> bool;
+                    /// Create a timeout event
+                    fn from_timeout(nanoseconds: u64) -> EventSubscription;
+                    /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe)
+                    fn dup(&self) -> EventSubscription;
+                    /// Reset subscription to be inactive, only next trigger will ready it
+                    fn reset(&self) -> ();
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_eventGenerator_cabi<T: GuestEventGenerator>(
+                    arg0: usize,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    EventGenerator::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestEventGenerator: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+
+                    fn new() -> Self;
+                    /// Get the receiving side (to pass to other parts of the program)
+                    fn subscribe(&self) -> EventSubscription;
+                    /// Trigger all subscribers
+                    fn activate(&self) -> ();
+                }
+                #[doc(hidden)]
+
+                macro_rules! __export_symmetric_runtime_symmetric_executor_0_1_0_cabi{
+  ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]event-subscription.ready")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready(arg0: *mut u8,) -> i32 {
+      $($path_to_types)*::_export_method_event_subscription_ready_cabi::<<$ty as $($path_to_types)*::Guest>::EventSubscription>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[static]event-subscription.from-timeout")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(arg0: i64,) -> *mut u8 {
+      $($path_to_types)*::_export_static_event_subscription_from_timeout_cabi::<<$ty as $($path_to_types)*::Guest>::EventSubscription>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]event-subscription.dup")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_event_subscription_dup_cabi::<<$ty as $($path_to_types)*::Guest>::EventSubscription>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]event-subscription.reset")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(arg0: *mut u8,) {
+      $($path_to_types)*::_export_method_event_subscription_reset_cabi::<<$ty as $($path_to_types)*::Guest>::EventSubscription>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[constructor]event-generator")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator() -> *mut u8 {
+      $($path_to_types)*::_export_constructor_event_generator_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>()
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]event-generator.subscribe")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_event_generator_subscribe_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]event-generator.activate")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate(arg0: *mut u8,) {
+      $($path_to_types)*::_export_method_event_generator_activate_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "run")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run() {
+      $($path_to_types)*::_export_run_cabi::<$ty>()
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "register")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(arg0: *mut u8,arg1: *mut u8,arg2: *mut u8,) {
+      $($path_to_types)*::_export_register_cabi::<$ty>(arg0, arg1, arg2)
+    }
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(arg0: usize) {
+      $($path_to_types)*::_export_drop_callbackFunction_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackFunction>(arg0)
+    }
+
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(arg0: usize) {
+      $($path_to_types)*::_export_drop_callbackData_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackData>(arg0)
+    }
+
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(arg0: usize) {
+      $($path_to_types)*::_export_drop_eventSubscription_cabi::<<$ty as $($path_to_types)*::Guest>::EventSubscription>(arg0)
+    }
+
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(arg0: usize) {
+      $($path_to_types)*::_export_drop_eventGenerator_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0)
+    }
+
+  };);
+}
+                #[doc(hidden)]
+                pub(crate) use __export_symmetric_runtime_symmetric_executor_0_1_0_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+
+    use core::fmt;
+    use core::marker;
+    use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+
+    /// A type which represents a component model resource, either imported or
+    /// exported into this component.
+    ///
+    /// This is a low-level wrapper which handles the lifetime of the resource
+    /// (namely this has a destructor). The `T` provided defines the component model
+    /// intrinsics that this wrapper uses.
+    ///
+    /// One of the chief purposes of this type is to provide `Deref` implementations
+    /// to access the underlying data when it is owned.
+    ///
+    /// This type is primarily used in generated code for exported and imported
+    /// resources.
+    #[repr(transparent)]
+    pub struct Resource<T: WasmResource> {
+        // NB: This would ideally be `usize` but it is not. The fact that this has
+        // interior mutability is not exposed in the API of this type except for the
+        // `take_handle` method which is supposed to in theory be private.
+        //
+        // This represents, almost all the time, a valid handle value. When it's
+        // invalid it's stored as `0`.
+        handle: AtomicUsize,
+        _marker: marker::PhantomData<T>,
+    }
+
+    /// A trait which all wasm resources implement, namely providing the ability to
+    /// drop a resource.
+    ///
+    /// This generally is implemented by generated code, not user-facing code.
+    #[allow(clippy::missing_safety_doc)]
+    pub unsafe trait WasmResource {
+        /// Invokes the `[resource-drop]...` intrinsic.
+        unsafe fn drop(handle: usize);
+    }
+
+    impl<T: WasmResource> Resource<T> {
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: usize) -> Self {
+            debug_assert!(handle != 0);
+            Self {
+                handle: AtomicUsize::new(handle),
+                _marker: marker::PhantomData,
+            }
+        }
+
+        /// Takes ownership of the handle owned by `resource`.
+        ///
+        /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+        /// ownership. The code generator does not enable that in all situations,
+        /// unfortunately, so this is provided instead.
+        ///
+        /// Also note that `take_handle` is in theory only ever called on values
+        /// owned by a generated function. For example a generated function might
+        /// take `Resource<T>` as an argument but then call `take_handle` on a
+        /// reference to that argument. In that sense the dynamic nature of
+        /// `take_handle` should only be exposed internally to generated code, not
+        /// to user code.
+        #[doc(hidden)]
+        pub fn take_handle(resource: &Resource<T>) -> usize {
+            resource.handle.swap(0, Relaxed)
+        }
+
+        #[doc(hidden)]
+        pub fn handle(resource: &Resource<T>) -> usize {
+            resource.handle.load(Relaxed)
+        }
+    }
+
+    impl<T: WasmResource> fmt::Debug for Resource<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Resource")
+                .field("handle", &self.handle)
+                .finish()
+        }
+    }
+
+    impl<T: WasmResource> Drop for Resource<T> {
+        fn drop(&mut self) {
+            unsafe {
+                match self.handle.load(Relaxed) {
+                    // If this handle was "taken" then don't do anything in the
+                    // destructor.
+                    0 => {}
+
+                    // ... but otherwise do actually destroy it with the imported
+                    // component model intrinsic as defined through `T`.
+                    other => T::drop(other),
+                }
+            }
+        }
+    }
+    pub use alloc_crate::boxed::Box;
+
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    #[cfg(feature = "never")]
+    pub mod stream_and_future_support {
+        use {
+            futures::{
+                channel::oneshot,
+                future::{self, FutureExt},
+                sink::Sink,
+                stream::Stream,
+            },
+            std::{
+                collections::hash_map::Entry,
+                convert::Infallible,
+                fmt,
+                future::{Future, IntoFuture},
+                iter,
+                marker::PhantomData,
+                mem::{self, ManuallyDrop, MaybeUninit},
+                pin::Pin,
+                task::{Context, Poll},
+            },
+            wit_bindgen_rt::async_support::{self, Handle},
+        };
+
+        #[doc(hidden)]
+        pub trait FuturePayload: Unpin + Sized + 'static {
+            fn new() -> u32;
+            async fn write(future: u32, value: Self) -> bool;
+            async fn read(future: u32) -> Option<Self>;
+            fn cancel_write(future: u32);
+            fn cancel_read(future: u32);
+            fn close_writable(future: u32);
+            fn close_readable(future: u32);
+        }
+
+        /// Represents the writable end of a Component Model `future`.
+        pub struct FutureWriter<T: FuturePayload> {
+            handle: u32,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: FuturePayload> fmt::Debug for FutureWriter<T> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_struct("FutureWriter")
+                    .field("handle", &self.handle)
+                    .finish()
+            }
+        }
+
+        /// Represents a write operation which may be canceled prior to completion.
+        pub struct CancelableWrite<T: FuturePayload> {
+            writer: Option<FutureWriter<T>>,
+            future: Pin<Box<dyn Future<Output = ()>>>,
+        }
+
+        impl<T: FuturePayload> Future for CancelableWrite<T> {
+            type Output = ();
+
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
+                let me = self.get_mut();
+                match me.future.poll_unpin(cx) {
+                    Poll::Ready(()) => {
+                        me.writer = None;
+                        Poll::Ready(())
+                    }
+                    Poll::Pending => Poll::Pending,
+                }
+            }
+        }
+
+        impl<T: FuturePayload> CancelableWrite<T> {
+            /// Cancel this write if it hasn't already completed, returning the original `FutureWriter`.
+            ///
+            /// This method will panic if the write has already completed.
+            pub fn cancel(mut self) -> FutureWriter<T> {
+                self.cancel_mut()
+            }
+
+            fn cancel_mut(&mut self) -> FutureWriter<T> {
+                let writer = self.writer.take().unwrap();
+                async_support::with_entry(writer.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::LocalOpen
+                        | Handle::LocalWaiting(_)
+                        | Handle::Read
+                        | Handle::LocalClosed => unreachable!(),
+                        Handle::LocalReady(..) => {
+                            entry.insert(Handle::LocalOpen);
+                        }
+                        Handle::Write => T::cancel_write(writer.handle),
+                    },
+                });
+                writer
+            }
+        }
+
+        impl<T: FuturePayload> Drop for CancelableWrite<T> {
+            fn drop(&mut self) {
+                if self.writer.is_some() {
+                    self.cancel_mut();
+                }
+            }
+        }
+
+        impl<T: FuturePayload> FutureWriter<T> {
+            /// Write the specified value to this `future`.
+            pub fn write(self, v: T) -> CancelableWrite<T> {
+                let handle = self.handle;
+                CancelableWrite {
+                    writer: Some(self),
+                    future: async_support::with_entry(handle, |entry| match entry {
+                        Entry::Vacant(_) => unreachable!(),
+                        Entry::Occupied(mut entry) => match entry.get() {
+                            Handle::LocalOpen => {
+                                let mut v = Some(v);
+                                Box::pin(future::poll_fn(move |cx| {
+                                    async_support::with_entry(handle, |entry| match entry {
+                                        Entry::Vacant(_) => unreachable!(),
+                                        Entry::Occupied(mut entry) => match entry.get() {
+                                            Handle::LocalOpen => {
+                                                entry.insert(Handle::LocalReady(
+                                                    Box::new(v.take().unwrap()),
+                                                    cx.waker().clone(),
+                                                ));
+                                                Poll::Pending
+                                            }
+                                            Handle::LocalReady(..) => Poll::Pending,
+                                            Handle::LocalClosed => Poll::Ready(()),
+                                            Handle::LocalWaiting(_)
+                                            | Handle::Read
+                                            | Handle::Write => {
+                                                unreachable!()
+                                            }
+                                        },
+                                    })
+                                }))
+                                    as Pin<Box<dyn Future<Output = _>>>
+                            }
+                            Handle::LocalWaiting(_) => {
+                                let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalClosed)
+                                else {
+                                    unreachable!()
+                                };
+                                _ = tx.send(Box::new(v));
+                                Box::pin(future::ready(()))
+                            }
+                            Handle::LocalClosed => Box::pin(future::ready(())),
+                            Handle::Read | Handle::LocalReady(..) => unreachable!(),
+                            Handle::Write => Box::pin(T::write(handle, v).map(drop)),
+                        },
+                    }),
+                }
+            }
+        }
+
+        impl<T: FuturePayload> Drop for FutureWriter<T> {
+            fn drop(&mut self) {
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get_mut() {
+                        Handle::LocalOpen | Handle::LocalWaiting(_) | Handle::LocalReady(..) => {
+                            entry.insert(Handle::LocalClosed);
+                        }
+                        Handle::Read => unreachable!(),
+                        Handle::Write | Handle::LocalClosed => {
+                            entry.remove();
+                            T::close_writable(self.handle);
+                        }
+                    },
+                });
+            }
+        }
+
+        /// Represents a read operation which may be canceled prior to completion.
+        pub struct CancelableRead<T: FuturePayload> {
+            reader: Option<FutureReader<T>>,
+            future: Pin<Box<dyn Future<Output = Option<T>>>>,
+        }
+
+        impl<T: FuturePayload> Future for CancelableRead<T> {
+            type Output = Option<T>;
+
+            fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<T>> {
+                let me = self.get_mut();
+                match me.future.poll_unpin(cx) {
+                    Poll::Ready(v) => {
+                        me.reader = None;
+                        Poll::Ready(v)
+                    }
+                    Poll::Pending => Poll::Pending,
+                }
+            }
+        }
+
+        impl<T: FuturePayload> CancelableRead<T> {
+            /// Cancel this read if it hasn't already completed, returning the original `FutureReader`.
+            ///
+            /// This method will panic if the read has already completed.
+            pub fn cancel(mut self) -> FutureReader<T> {
+                self.cancel_mut()
+            }
+
+            fn cancel_mut(&mut self) -> FutureReader<T> {
+                let reader = self.reader.take().unwrap();
+                async_support::with_entry(reader.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::LocalOpen
+                        | Handle::LocalReady(..)
+                        | Handle::Write
+                        | Handle::LocalClosed => unreachable!(),
+                        Handle::LocalWaiting(_) => {
+                            entry.insert(Handle::LocalOpen);
+                        }
+                        Handle::Read => T::cancel_read(reader.handle),
+                    },
+                });
+                reader
+            }
+        }
+
+        impl<T: FuturePayload> Drop for CancelableRead<T> {
+            fn drop(&mut self) {
+                if self.reader.is_some() {
+                    self.cancel_mut();
+                }
+            }
+        }
+
+        /// Represents the readable end of a Component Model `future`.
+        pub struct FutureReader<T: FuturePayload> {
+            handle: u32,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: FuturePayload> fmt::Debug for FutureReader<T> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_struct("FutureReader")
+                    .field("handle", &self.handle)
+                    .finish()
+            }
+        }
+
+        impl<T: FuturePayload> FutureReader<T> {
+            #[doc(hidden)]
+            pub fn from_handle(handle: u32) -> Self {
+                async_support::with_entry(handle, |entry| match entry {
+                    Entry::Vacant(entry) => {
+                        entry.insert(Handle::Read);
+                    }
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::Write => {
+                            entry.insert(Handle::LocalOpen);
+                        }
+                        Handle::Read
+                        | Handle::LocalOpen
+                        | Handle::LocalReady(..)
+                        | Handle::LocalWaiting(_)
+                        | Handle::LocalClosed => {
+                            unreachable!()
+                        }
+                    },
+                });
+
+                Self {
+                    handle,
+                    _phantom: PhantomData,
+                }
+            }
+
+            #[doc(hidden)]
+            pub fn into_handle(self) -> u32 {
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::LocalOpen => {
+                            entry.insert(Handle::Write);
+                        }
+                        Handle::Read | Handle::LocalClosed => {
+                            entry.remove();
+                        }
+                        Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => {
+                            unreachable!()
+                        }
+                    },
+                });
+
+                ManuallyDrop::new(self).handle
+            }
+        }
+
+        impl<T: FuturePayload> IntoFuture for FutureReader<T> {
+            type Output = Option<T>;
+            type IntoFuture = CancelableRead<T>;
+
+            /// Convert this object into a `Future` which will resolve when a value is
+            /// written to the writable end of this `future` (yielding a `Some` result)
+            /// or when the writable end is dropped (yielding a `None` result).
+            fn into_future(self) -> Self::IntoFuture {
+                let handle = self.handle;
+                CancelableRead {
+                    reader: Some(self),
+                    future: async_support::with_entry(handle, |entry| match entry {
+                        Entry::Vacant(_) => unreachable!(),
+                        Entry::Occupied(mut entry) => match entry.get() {
+                            Handle::Write | Handle::LocalWaiting(_) => unreachable!(),
+                            Handle::Read => Box::pin(async move { T::read(handle).await })
+                                as Pin<Box<dyn Future<Output = _>>>,
+                            Handle::LocalOpen => {
+                                let (tx, rx) = oneshot::channel();
+                                entry.insert(Handle::LocalWaiting(tx));
+                                Box::pin(
+                                    async move { rx.await.ok().map(|v| *v.downcast().unwrap()) },
+                                )
+                            }
+                            Handle::LocalClosed => Box::pin(future::ready(None)),
+                            Handle::LocalReady(..) => {
+                                let Handle::LocalReady(v, waker) =
+                                    entry.insert(Handle::LocalClosed)
+                                else {
+                                    unreachable!()
+                                };
+                                waker.wake();
+                                Box::pin(future::ready(Some(*v.downcast().unwrap())))
+                            }
+                        },
+                    }),
+                }
+            }
+        }
+
+        impl<T: FuturePayload> Drop for FutureReader<T> {
+            fn drop(&mut self) {
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get_mut() {
+                        Handle::LocalReady(..) => {
+                            let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed)
+                            else {
+                                unreachable!()
+                            };
+                            waker.wake();
+                        }
+                        Handle::LocalOpen | Handle::LocalWaiting(_) => {
+                            entry.insert(Handle::LocalClosed);
+                        }
+                        Handle::Read | Handle::LocalClosed => {
+                            entry.remove();
+                            T::close_readable(self.handle);
+                        }
+                        Handle::Write => unreachable!(),
+                    },
+                });
+            }
+        }
+
+        #[doc(hidden)]
+        pub trait StreamPayload: Unpin + Sized + 'static {
+            fn new() -> u32;
+            async fn write(stream: u32, values: &[Self]) -> Option<usize>;
+            async fn read(stream: u32, values: &mut [MaybeUninit<Self>]) -> Option<usize>;
+            fn cancel_write(stream: u32);
+            fn cancel_read(stream: u32);
+            fn close_writable(stream: u32);
+            fn close_readable(stream: u32);
+        }
+
+        struct CancelWriteOnDrop<T: StreamPayload> {
+            handle: Option<u32>,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: StreamPayload> Drop for CancelWriteOnDrop<T> {
+            fn drop(&mut self) {
+                if let Some(handle) = self.handle.take() {
+                    async_support::with_entry(handle, |entry| match entry {
+                        Entry::Vacant(_) => unreachable!(),
+                        Entry::Occupied(mut entry) => match entry.get() {
+                            Handle::LocalOpen
+                            | Handle::LocalWaiting(_)
+                            | Handle::Read
+                            | Handle::LocalClosed => unreachable!(),
+                            Handle::LocalReady(..) => {
+                                entry.insert(Handle::LocalOpen);
+                            }
+                            Handle::Write => T::cancel_write(handle),
+                        },
+                    });
+                }
+            }
+        }
+
+        /// Represents the writable end of a Component Model `stream`.
+        pub struct StreamWriter<T: StreamPayload> {
+            handle: u32,
+            future: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: StreamPayload> StreamWriter<T> {
+            /// Cancel the current pending write operation.
+            ///
+            /// This will panic if no such operation is pending.
+            pub fn cancel(&mut self) {
+                assert!(self.future.is_some());
+                self.future = None;
+            }
+        }
+
+        impl<T: StreamPayload> fmt::Debug for StreamWriter<T> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_struct("StreamWriter")
+                    .field("handle", &self.handle)
+                    .finish()
+            }
+        }
+
+        impl<T: StreamPayload> Sink<Vec<T>> for StreamWriter<T> {
+            type Error = Infallible;
+
+            fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+                let me = self.get_mut();
+
+                if let Some(future) = &mut me.future {
+                    match future.as_mut().poll(cx) {
+                        Poll::Ready(_) => {
+                            me.future = None;
+                            Poll::Ready(Ok(()))
+                        }
+                        Poll::Pending => Poll::Pending,
+                    }
+                } else {
+                    Poll::Ready(Ok(()))
+                }
+            }
+
+            fn start_send(self: Pin<&mut Self>, item: Vec<T>) -> Result<(), Self::Error> {
+                assert!(self.future.is_none());
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::LocalOpen => {
+                            let handle = self.handle;
+                            let mut item = Some(item);
+                            let mut cancel_on_drop = Some(CancelWriteOnDrop::<T> {
+                                handle: Some(handle),
+                                _phantom: PhantomData,
+                            });
+                            self.get_mut().future = Some(Box::pin(future::poll_fn(move |cx| {
+                                async_support::with_entry(handle, |entry| match entry {
+                                    Entry::Vacant(_) => unreachable!(),
+                                    Entry::Occupied(mut entry) => match entry.get() {
+                                        Handle::LocalOpen => {
+                                            if let Some(item) = item.take() {
+                                                entry.insert(Handle::LocalReady(
+                                                    Box::new(item),
+                                                    cx.waker().clone(),
+                                                ));
+                                                Poll::Pending
+                                            } else {
+                                                cancel_on_drop.take().unwrap().handle = None;
+                                                Poll::Ready(())
+                                            }
+                                        }
+                                        Handle::LocalReady(..) => Poll::Pending,
+                                        Handle::LocalClosed => {
+                                            cancel_on_drop.take().unwrap().handle = None;
+                                            Poll::Ready(())
+                                        }
+                                        Handle::LocalWaiting(_) | Handle::Read | Handle::Write => {
+                                            unreachable!()
+                                        }
+                                    },
+                                })
+                            })));
+                        }
+                        Handle::LocalWaiting(_) => {
+                            let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalOpen) else {
+                                unreachable!()
+                            };
+                            _ = tx.send(Box::new(item));
+                        }
+                        Handle::LocalClosed => (),
+                        Handle::Read | Handle::LocalReady(..) => unreachable!(),
+                        Handle::Write => {
+                            let handle = self.handle;
+                            let mut cancel_on_drop = CancelWriteOnDrop::<T> {
+                                handle: Some(handle),
+                                _phantom: PhantomData,
+                            };
+                            self.get_mut().future = Some(Box::pin(async move {
+                                let mut offset = 0;
+                                while offset < item.len() {
+                                    if let Some(count) = T::write(handle, &item[offset..]).await {
+                                        offset += count;
+                                    } else {
+                                        break;
+                                    }
+                                }
+                                cancel_on_drop.handle = None;
+                                drop(cancel_on_drop);
+                            }));
+                        }
+                    },
+                });
+                Ok(())
+            }
+
+            fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+                self.poll_ready(cx)
+            }
+
+            fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
+                self.poll_ready(cx)
+            }
+        }
+
+        impl<T: StreamPayload> Drop for StreamWriter<T> {
+            fn drop(&mut self) {
+                self.future = None;
+
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get_mut() {
+                        Handle::LocalOpen | Handle::LocalWaiting(_) | Handle::LocalReady(..) => {
+                            entry.insert(Handle::LocalClosed);
+                        }
+                        Handle::Read => unreachable!(),
+                        Handle::Write | Handle::LocalClosed => {
+                            entry.remove();
+                            T::close_writable(self.handle);
+                        }
+                    },
+                });
+            }
+        }
+
+        struct CancelReadOnDrop<T: StreamPayload> {
+            handle: Option<u32>,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: StreamPayload> Drop for CancelReadOnDrop<T> {
+            fn drop(&mut self) {
+                if let Some(handle) = self.handle.take() {
+                    async_support::with_entry(handle, |entry| match entry {
+                        Entry::Vacant(_) => unreachable!(),
+                        Entry::Occupied(mut entry) => match entry.get() {
+                            Handle::LocalOpen
+                            | Handle::LocalReady(..)
+                            | Handle::Write
+                            | Handle::LocalClosed => unreachable!(),
+                            Handle::LocalWaiting(_) => {
+                                entry.insert(Handle::LocalOpen);
+                            }
+                            Handle::Read => T::cancel_read(handle),
+                        },
+                    });
+                }
+            }
+        }
+
+        /// Represents the readable end of a Component Model `stream`.
+        pub struct StreamReader<T: StreamPayload> {
+            handle: u32,
+            future: Option<Pin<Box<dyn Future<Output = Option<Vec<T>>> + 'static>>>,
+            _phantom: PhantomData<T>,
+        }
+
+        impl<T: StreamPayload> StreamReader<T> {
+            /// Cancel the current pending read operation.
+            ///
+            /// This will panic if no such operation is pending.
+            pub fn cancel(&mut self) {
+                assert!(self.future.is_some());
+                self.future = None;
+            }
+        }
+
+        impl<T: StreamPayload> fmt::Debug for StreamReader<T> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_struct("StreamReader")
+                    .field("handle", &self.handle)
+                    .finish()
+            }
+        }
+
+        impl<T: StreamPayload> StreamReader<T> {
+            #[doc(hidden)]
+            pub fn from_handle(handle: u32) -> Self {
+                async_support::with_entry(handle, |entry| match entry {
+                    Entry::Vacant(entry) => {
+                        entry.insert(Handle::Read);
+                    }
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::Write => {
+                            entry.insert(Handle::LocalOpen);
+                        }
+                        Handle::Read
+                        | Handle::LocalOpen
+                        | Handle::LocalReady(..)
+                        | Handle::LocalWaiting(_)
+                        | Handle::LocalClosed => {
+                            unreachable!()
+                        }
+                    },
+                });
+
+                Self {
+                    handle,
+                    future: None,
+                    _phantom: PhantomData,
+                }
+            }
+
+            #[doc(hidden)]
+            pub fn into_handle(self) -> u32 {
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get() {
+                        Handle::LocalOpen => {
+                            entry.insert(Handle::Write);
+                        }
+                        Handle::Read | Handle::LocalClosed => {
+                            entry.remove();
+                        }
+                        Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => {
+                            unreachable!()
+                        }
+                    },
+                });
+
+                ManuallyDrop::new(self).handle
+            }
+        }
+
+        impl<T: StreamPayload> Stream for StreamReader<T> {
+            type Item = Vec<T>;
+
+            fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
+                let me = self.get_mut();
+
+                if me.future.is_none() {
+                    me.future = Some(async_support::with_entry(me.handle, |entry| match entry {
+                        Entry::Vacant(_) => unreachable!(),
+                        Entry::Occupied(mut entry) => match entry.get() {
+                            Handle::Write | Handle::LocalWaiting(_) => unreachable!(),
+                            Handle::Read => {
+                                let handle = me.handle;
+                                let mut cancel_on_drop = CancelReadOnDrop::<T> {
+                                    handle: Some(handle),
+                                    _phantom: PhantomData,
+                                };
+                                Box::pin(async move {
+                                    let mut buffer = iter::repeat_with(MaybeUninit::uninit)
+                                        .take(ceiling(64 * 1024, mem::size_of::<T>()))
+                                        .collect::<Vec<_>>();
+
+                                    let result = if let Some(count) =
+                                        T::read(handle, &mut buffer).await
+                                    {
+                                        buffer.truncate(count);
+                                        Some(unsafe {
+                                            mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(buffer)
+                                        })
+                                    } else {
+                                        None
+                                    };
+                                    cancel_on_drop.handle = None;
+                                    drop(cancel_on_drop);
+                                    result
+                                })
+                                    as Pin<Box<dyn Future<Output = _>>>
+                            }
+                            Handle::LocalOpen => {
+                                let (tx, rx) = oneshot::channel();
+                                entry.insert(Handle::LocalWaiting(tx));
+                                let mut cancel_on_drop = CancelReadOnDrop::<T> {
+                                    handle: Some(me.handle),
+                                    _phantom: PhantomData,
+                                };
+                                Box::pin(async move {
+                                    let result =
+                                        rx.map(|v| v.ok().map(|v| *v.downcast().unwrap())).await;
+                                    cancel_on_drop.handle = None;
+                                    drop(cancel_on_drop);
+                                    result
+                                })
+                            }
+                            Handle::LocalClosed => Box::pin(future::ready(None)),
+                            Handle::LocalReady(..) => {
+                                let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen)
+                                else {
+                                    unreachable!()
+                                };
+                                waker.wake();
+                                Box::pin(future::ready(Some(*v.downcast().unwrap())))
+                            }
+                        },
+                    }));
+                }
+
+                match me.future.as_mut().unwrap().as_mut().poll(cx) {
+                    Poll::Ready(v) => {
+                        me.future = None;
+                        Poll::Ready(v)
+                    }
+                    Poll::Pending => Poll::Pending,
+                }
+            }
+        }
+
+        impl<T: StreamPayload> Drop for StreamReader<T> {
+            fn drop(&mut self) {
+                self.future = None;
+
+                async_support::with_entry(self.handle, |entry| match entry {
+                    Entry::Vacant(_) => unreachable!(),
+                    Entry::Occupied(mut entry) => match entry.get_mut() {
+                        Handle::LocalReady(..) => {
+                            let Handle::LocalReady(_, waker) = entry.insert(Handle::LocalClosed)
+                            else {
+                                unreachable!()
+                            };
+                            waker.wake();
+                        }
+                        Handle::LocalOpen | Handle::LocalWaiting(_) => {
+                            entry.insert(Handle::LocalClosed);
+                        }
+                        Handle::Read | Handle::LocalClosed => {
+                            entry.remove();
+                            T::close_readable(self.handle);
+                        }
+                        Handle::Write => unreachable!(),
+                    },
+                });
+            }
+        }
+
+        /// Creates a new Component Model `future` with the specified payload type.
+        pub fn new_future<T: FuturePayload>() -> (FutureWriter<T>, FutureReader<T>) {
+            let handle = T::new();
+            async_support::with_entry(handle, |entry| match entry {
+                Entry::Vacant(entry) => {
+                    entry.insert(Handle::LocalOpen);
+                }
+                Entry::Occupied(_) => unreachable!(),
+            });
+            (
+                FutureWriter {
+                    handle,
+                    _phantom: PhantomData,
+                },
+                FutureReader {
+                    handle,
+                    _phantom: PhantomData,
+                },
+            )
+        }
+
+        /// Creates a new Component Model `stream` with the specified payload type.
+        pub fn new_stream<T: StreamPayload>() -> (StreamWriter<T>, StreamReader<T>) {
+            let handle = T::new();
+            async_support::with_entry(handle, |entry| match entry {
+                Entry::Vacant(entry) => {
+                    entry.insert(Handle::LocalOpen);
+                }
+                Entry::Occupied(_) => unreachable!(),
+            });
+            (
+                StreamWriter {
+                    handle,
+                    future: None,
+                    _phantom: PhantomData,
+                },
+                StreamReader {
+                    handle,
+                    future: None,
+                    _phantom: PhantomData,
+                },
+            )
+        }
+
+        fn ceiling(x: usize, y: usize) -> usize {
+            (x / y) + if x % y == 0 { 0 } else { 1 }
+        }
+    }
+    extern crate alloc as alloc_crate;
+}
+#[allow(unused_imports)]
+// pub use _rt::stream_and_future_support;
+
+/// Generates `#[no_mangle]` functions to export the specified type as the
+/// root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_executor_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::symmetric::runtime::symmetric_executor::__export_symmetric_runtime_symmetric_executor_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::symmetric::runtime::symmetric_executor);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_executor_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:executor:encoded world"]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 793] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x9a\x05\x01A\x02\x01\
+A\x02\x01B\x20\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\
+\x04\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x01m\x02\x07\
+started\x0bnot-started\x04\0\x0bcall-status\x03\0\x04\x01m\x02\x07pending\x05rea\
+dy\x04\0\x0ecallback-state\x03\0\x06\x01h\x02\x01@\x01\x04self\x08\0\x7f\x04\0\x20\
+[method]event-subscription.ready\x01\x09\x01i\x02\x01@\x01\x0bnanosecondsw\0\x0a\
+\x04\0'[static]event-subscription.from-timeout\x01\x0b\x01@\x01\x04self\x08\0\x0a\
+\x04\0\x1e[method]event-subscription.dup\x01\x0c\x01@\x01\x04self\x08\x01\0\x04\0\
+\x20[method]event-subscription.reset\x01\x0d\x01i\x03\x01@\0\0\x0e\x04\0\x1c[con\
+structor]event-generator\x01\x0f\x01h\x03\x01@\x01\x04self\x10\0\x0a\x04\0![meth\
+od]event-generator.subscribe\x01\x11\x01@\x01\x04self\x10\x01\0\x04\0\x20[method\
+]event-generator.activate\x01\x12\x01@\0\x01\0\x04\0\x03run\x01\x13\x01i\0\x01i\x01\
+\x01@\x03\x07trigger\x0a\x08callback\x14\x04data\x15\x01\0\x04\0\x08register\x01\
+\x16\x04\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x20symmetric:r\
+untime/executor@0.1.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09producers\x01\
+\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.36.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs
new file mode 100644
index 000000000..67defa149
--- /dev/null
+++ b/crates/symmetric_executor/src/lib.rs
@@ -0,0 +1,460 @@
+use std::{
+    ffi::c_int,
+    mem::transmute,
+    sync::{
+        atomic::{AtomicBool, AtomicU32, Ordering},
+        Arc, Mutex, OnceLock,
+    },
+    time::{Duration, SystemTime},
+};
+
+use executor::exports::symmetric::runtime::symmetric_executor::{self, CallbackState};
+
+const DEBUGGING: bool = cfg!(feature = "trace");
+const INVALID_FD: EventFd = -1;
+
+mod executor;
+
+struct Guest;
+
+executor::export!(Guest with_types_in executor);
+
+struct Ignore;
+impl symmetric_executor::GuestCallbackFunction for Ignore {}
+impl symmetric_executor::GuestCallbackData for Ignore {}
+
+impl symmetric_executor::GuestEventSubscription for EventSubscription {
+    fn ready(&self) -> bool {
+        self.inner.ready()
+    }
+
+    fn from_timeout(nanoseconds: u64) -> symmetric_executor::EventSubscription {
+        let when = SystemTime::now() + Duration::from_nanos(nanoseconds);
+        symmetric_executor::EventSubscription::new(EventSubscription {
+            inner: EventType::SystemTime(when),
+        })
+    }
+
+    fn dup(&self) -> symmetric_executor::EventSubscription {
+        symmetric_executor::EventSubscription::new(self.dup())
+    }
+
+    fn reset(&self) {
+        match &self.inner {
+            EventType::Triggered {
+                last_counter,
+                event,
+            } => {
+                last_counter.store(event.lock().unwrap().counter, Ordering::Relaxed);
+            }
+            EventType::SystemTime(_system_time) => (),
+        }
+    }
+}
+
+impl symmetric_executor::GuestEventGenerator for EventGenerator {
+    fn new() -> Self {
+        Self(Arc::new(Mutex::new(EventInner {
+            counter: 0,
+            waiting: Default::default(),
+        })))
+    }
+
+    fn subscribe(&self) -> symmetric_executor::EventSubscription {
+        if DEBUGGING {
+            println!("subscribe({:x})", Arc::as_ptr(&self.0) as usize);
+        }
+        symmetric_executor::EventSubscription::new(EventSubscription {
+            inner: EventType::Triggered {
+                last_counter: AtomicU32::new(0),
+                event: Arc::clone(&self.0),
+            },
+        })
+    }
+
+    fn activate(&self) {
+        if let Ok(mut event) = self.0.lock() {
+            event.counter += 1;
+            if DEBUGGING {
+                println!(
+                    "activate({:x}) counter={}",
+                    Arc::as_ptr(&self.0) as usize,
+                    event.counter
+                );
+            }
+            let file_signal: u64 = 1;
+            event.waiting.iter().for_each(|fd| {
+                if DEBUGGING {
+                    println!("activate(fd {fd})");
+                }
+                let result = unsafe {
+                    libc::write(
+                        *fd,
+                        core::ptr::from_ref(&file_signal).cast(),
+                        core::mem::size_of_val(&file_signal),
+                    )
+                };
+                if result >= 0 {
+                    assert_eq!(
+                        result,
+                        core::mem::size_of_val(&file_signal).try_into().unwrap()
+                    );
+                }
+            });
+        } else if DEBUGGING {
+            println!("activate failure");
+        }
+    }
+}
+
+struct Executor {
+    active_tasks: Vec<QueuedEvent>,
+    change_event: Option<EventFd>,
+}
+
+static EXECUTOR: Mutex<Executor> = Mutex::new(Executor {
+    active_tasks: Vec::new(),
+    change_event: None,
+});
+// while executing tasks from the loop we can't directly queue new ones
+static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false);
+static NEW_TASKS: Mutex<Vec<QueuedEvent>> = Mutex::new(Vec::new());
+
+impl symmetric_executor::Guest for Guest {
+    type CallbackFunction = Ignore;
+    type CallbackData = Ignore;
+    type EventSubscription = EventSubscription;
+    type EventGenerator = EventGenerator;
+
+    fn run() {
+        let change_event = *EXECUTOR
+            .lock()
+            .unwrap()
+            .change_event
+            .get_or_insert_with(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) });
+        loop {
+            let mut wait = libc::timeval {
+                tv_sec: i64::MAX,
+                tv_usec: 999999,
+            };
+            let mut tvptr = core::ptr::null_mut();
+            let mut maxfd = change_event + 1;
+            let now = SystemTime::now();
+            let mut rfds = core::mem::MaybeUninit::<libc::fd_set>::uninit();
+            let rfd_ptr = rfds.as_mut_ptr();
+            unsafe { libc::FD_ZERO(rfd_ptr) };
+            unsafe {
+                libc::FD_SET(change_event, rfd_ptr);
+            }
+            {
+                let mut ex = EXECUTOR.lock().unwrap();
+                let old_busy = EXECUTOR_BUSY.swap(true, Ordering::Acquire);
+                assert!(!old_busy);
+                ex.active_tasks.iter_mut().for_each(|task| {
+                    if task.inner.ready() {
+                        if DEBUGGING {
+                            println!(
+                                "task ready {:x} {:x}",
+                                task.callback.as_ref().unwrap().0 as usize,
+                                task.callback.as_ref().unwrap().1 as usize
+                            );
+                        }
+                        task.callback.take_if(|CallbackEntry(f, data)| {
+                            matches!((f)(*data), CallbackState::Ready)
+                        });
+                    } else {
+                        match &task.inner {
+                            EventType::Triggered {
+                                last_counter: _,
+                                event: _,
+                            } => {
+                                unsafe { libc::FD_SET(task.event_fd, rfd_ptr) };
+                                if task.event_fd >= maxfd {
+                                    maxfd = task.event_fd + 1;
+                                }
+                            }
+                            EventType::SystemTime(system_time) => {
+                                if *system_time > now {
+                                    let diff = system_time.duration_since(now).unwrap_or_default();
+                                    let secs = diff.as_secs() as i64;
+                                    let usecs = diff.subsec_micros() as i64;
+                                    if secs < wait.tv_sec
+                                        || (secs == wait.tv_sec && usecs < wait.tv_usec)
+                                    {
+                                        wait.tv_sec = secs;
+                                        wait.tv_usec = usecs;
+                                    }
+                                    tvptr = core::ptr::from_mut(&mut wait);
+                                } else {
+                                    task.callback.take_if(|CallbackEntry(f, data)| {
+                                        matches!((f)(*data), CallbackState::Ready)
+                                    });
+                                }
+                            }
+                        }
+                    }
+                });
+                let old_busy = EXECUTOR_BUSY.swap(false, Ordering::Release);
+                assert!(old_busy);
+                ex.active_tasks.retain(|task| task.callback.is_some());
+                {
+                    let mut new_tasks = NEW_TASKS.lock().unwrap();
+                    if !new_tasks.is_empty() {
+                        ex.active_tasks.append(&mut new_tasks);
+                        // collect callbacks and timeouts again
+                        continue;
+                    }
+                }
+                if ex.active_tasks.is_empty() {
+                    break;
+                }
+            }
+            if tvptr.is_null()
+                && maxfd == change_event + 1
+                && !(0..change_event).any(|f| unsafe { libc::FD_ISSET(f, rfd_ptr) })
+            {
+                // probably only active tasks, all returned pending, try again
+                if DEBUGGING {
+                    println!(
+                        "Relooping with {} tasks",
+                        EXECUTOR.lock().unwrap().active_tasks.len()
+                    );
+                }
+                continue;
+            }
+            // with no work left the break should have occured
+            // assert!(!tvptr.is_null() || maxfd > 0);
+            if DEBUGGING {
+                if tvptr.is_null() {
+                    println!("select({maxfd}, {:x}, null)", unsafe {
+                        *rfd_ptr.cast::<u32>()
+                    },);
+                } else {
+                    println!(
+                        "select({maxfd}, {:x}, {}.{})",
+                        unsafe { *rfd_ptr.cast::<u32>() },
+                        wait.tv_sec,
+                        wait.tv_usec
+                    );
+                }
+            }
+            let selectresult = unsafe {
+                libc::select(
+                    maxfd,
+                    rfd_ptr,
+                    std::ptr::null_mut(),
+                    std::ptr::null_mut(),
+                    tvptr,
+                )
+            };
+            // we could look directly for the timeout
+            if selectresult > 0 {
+                let mut dummy: u64 = 0;
+                // reset active file descriptors
+                for i in 0..maxfd {
+                    if unsafe { libc::FD_ISSET(i, rfd_ptr) } {
+                        let readresult = unsafe {
+                            libc::read(
+                                i,
+                                core::ptr::from_mut(&mut dummy).cast(),
+                                core::mem::size_of_val(&dummy),
+                            )
+                        };
+                        assert!(
+                            readresult <= 0
+                                || readresult
+                                    == isize::try_from(core::mem::size_of_val(&dummy)).unwrap()
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn register(
+        trigger: symmetric_executor::EventSubscription,
+        callback: symmetric_executor::CallbackFunction,
+        data: symmetric_executor::CallbackData,
+    ) -> () {
+        let trigger: EventSubscription = trigger.into_inner();
+        let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) };
+        let data = data.take_handle() as *mut ();
+        let event_fd = match &trigger.inner {
+            EventType::Triggered {
+                last_counter: _,
+                event,
+            } => {
+                let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) };
+                event.lock().unwrap().waiting.push(fd);
+                fd
+            }
+            EventType::SystemTime(_system_time) => INVALID_FD,
+        };
+        let subscr = QueuedEvent {
+            inner: trigger.inner,
+            callback: Some(CallbackEntry(cb, data)),
+            event_fd,
+        };
+        if DEBUGGING {
+            match &subscr.inner {
+                EventType::Triggered {
+                    last_counter: _,
+                    event,
+                } => println!(
+                    "register(Trigger {:x} fd {event_fd}, {:x},{:x})",
+                    Arc::as_ptr(event) as usize,
+                    cb as usize,
+                    data as usize
+                ),
+                EventType::SystemTime(system_time) => {
+                    let diff = system_time.duration_since(SystemTime::now()).unwrap();
+                    println!(
+                        "register(Time {}.{}, {:x},{:x})",
+                        diff.as_secs(),
+                        diff.subsec_nanos(),
+                        cb as usize,
+                        data as usize
+                    );
+                }
+            }
+        }
+        match EXECUTOR.try_lock() {
+            Ok(mut lock) => {
+                lock.active_tasks.push(subscr);
+                let file_signal: u64 = 1;
+                let fd = *lock
+                    .change_event
+                    .get_or_insert_with(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) });
+                let result = unsafe {
+                    libc::write(
+                        fd,
+                        core::ptr::from_ref(&file_signal).cast(),
+                        core::mem::size_of_val(&file_signal),
+                    )
+                };
+                if result >= 0 {
+                    assert_eq!(
+                        result,
+                        core::mem::size_of_val(&file_signal).try_into().unwrap()
+                    );
+                }
+            }
+            Err(_err) => {
+                if EXECUTOR_BUSY.load(Ordering::Acquire) {
+                    NEW_TASKS.lock().unwrap().push(subscr);
+                } else {
+                    // actually this is unlikely, but give it a try
+                    EXECUTOR.lock().unwrap().active_tasks.push(subscr);
+                }
+            }
+        }
+    }
+}
+
+type EventFd = c_int;
+type Count = u32;
+
+struct EventInner {
+    counter: Count,
+    waiting: Vec<EventFd>,
+}
+
+struct EventGenerator(Arc<Mutex<EventInner>>);
+
+struct CallbackEntry(fn(*mut ()) -> CallbackState, *mut ());
+
+unsafe impl Send for CallbackEntry {}
+
+struct EventSubscription {
+    inner: EventType,
+}
+
+struct QueuedEvent {
+    inner: EventType,
+    event_fd: EventFd,
+    callback: Option<CallbackEntry>,
+}
+
+impl EventSubscription {
+    fn dup(&self) -> Self {
+        let inner = match &self.inner {
+            EventType::Triggered {
+                last_counter: last_counter_old,
+                // event_fd,
+                event,
+            } => {
+                let new_event = Arc::clone(event);
+                let last_counter = last_counter_old.load(Ordering::Relaxed);
+                if DEBUGGING {
+                    println!(
+                        "dup(subscr {last_counter} {:x})",
+                        Arc::as_ptr(&event) as usize
+                    );
+                }
+                EventType::Triggered {
+                    last_counter: AtomicU32::new(last_counter),
+                    event: new_event,
+                }
+            }
+            EventType::SystemTime(system_time) => EventType::SystemTime(*system_time),
+        };
+        EventSubscription { inner }
+    }
+}
+
+impl Drop for QueuedEvent {
+    fn drop(&mut self) {
+        if let Some(cb) = &self.callback {
+            if DEBUGGING {
+                println!(
+                    "drop() with active callback {:x},{:x}",
+                    cb.0 as usize, cb.1 as usize
+                );
+            }
+        }
+        match &self.inner {
+            EventType::Triggered {
+                last_counter: _,
+                event,
+            } => {
+                if DEBUGGING {
+                    println!("drop(queued fd {})", self.event_fd);
+                }
+                event
+                    .lock()
+                    .unwrap()
+                    .waiting
+                    .retain(|&e| e != self.event_fd);
+                unsafe { libc::close(self.event_fd) };
+            }
+            EventType::SystemTime(_system_time) => (),
+        }
+    }
+}
+
+enum EventType {
+    Triggered {
+        last_counter: AtomicU32,
+        event: Arc<Mutex<EventInner>>,
+    },
+    SystemTime(SystemTime),
+}
+
+impl EventType {
+    pub fn ready(&self) -> bool {
+        match self {
+            EventType::Triggered {
+                last_counter,
+                event,
+            } => {
+                let current_counter = event.lock().unwrap().counter;
+                let active = current_counter != last_counter.load(Ordering::Acquire);
+                if active {
+                    last_counter.store(current_counter, Ordering::Release);
+                }
+                active
+            }
+            EventType::SystemTime(system_time) => *system_time <= SystemTime::now(),
+        }
+    }
+}
diff --git a/crates/symmetric_executor/symmetric_stream/Cargo.toml b/crates/symmetric_executor/symmetric_stream/Cargo.toml
new file mode 100644
index 000000000..6f5d50aa2
--- /dev/null
+++ b/crates/symmetric_executor/symmetric_stream/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "symmetric_stream"
+version.workspace = true
+edition.workspace = true
+
+[dependencies]
+symmetric_executor = { path = ".." }
+wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../rust-client" }
+
+[dependencies.wit-bindgen]
+package = "dummy-rt"
+path = "../dummy-rt"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/crates/symmetric_executor/symmetric_stream/build.rs b/crates/symmetric_executor/symmetric_stream/build.rs
new file mode 100644
index 000000000..3096c1795
--- /dev/null
+++ b/crates/symmetric_executor/symmetric_stream/build.rs
@@ -0,0 +1,9 @@
+use std::env;
+
+fn main() {
+    let out = env::var_os("OUT_DIR").unwrap();
+    println!(
+        r"cargo:rustc-link-search={}/../../../deps",
+        out.into_string().unwrap()
+    );
+}
diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs
new file mode 100644
index 000000000..b4592d757
--- /dev/null
+++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs
@@ -0,0 +1,182 @@
+use std::{
+    ptr::null_mut,
+    sync::{
+        atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering},
+        Arc,
+    },
+};
+
+use stream_impl::exports::symmetric::runtime::symmetric_stream::{
+    self, Address, GuestAddress, GuestBuffer, GuestStreamObj,
+};
+use stream_impl::symmetric::runtime::symmetric_executor::EventGenerator;
+
+mod stream_impl;
+
+struct Guest;
+
+stream_impl::export!(Guest with_types_in stream_impl);
+
+struct Dummy;
+
+impl GuestAddress for Dummy {}
+
+struct Buffer {
+    addr: *mut (),
+    capacity: usize,
+    size: AtomicUsize,
+}
+
+impl GuestBuffer for Buffer {
+    fn new(addr: symmetric_stream::Address, capacity: u64) -> Self {
+        Self {
+            addr: addr.take_handle() as *mut (),
+            size: AtomicUsize::new(0),
+            capacity: capacity as usize,
+        }
+    }
+
+    fn get_address(&self) -> symmetric_stream::Address {
+        unsafe { Address::from_handle(self.addr as usize) }
+    }
+
+    fn get_size(&self) -> u64 {
+        self.size.load(Ordering::Relaxed) as u64
+    }
+
+    fn set_size(&self, size: u64) -> () {
+        self.size.store(size as usize, Ordering::Relaxed)
+    }
+
+    fn capacity(&self) -> u64 {
+        self.capacity as u64
+    }
+}
+
+mod results {
+    pub const BLOCKED: isize = -1;
+}
+
+struct StreamInner {
+    read_ready_event_send: EventGenerator,
+    write_ready_event_send: EventGenerator,
+    read_addr: AtomicPtr<()>,
+    read_size: AtomicUsize,
+    ready_addr: AtomicPtr<()>,
+    ready_size: AtomicIsize,
+    ready_capacity: AtomicUsize,
+}
+
+struct StreamObj(Arc<StreamInner>);
+
+impl GuestStreamObj for StreamObj {
+    fn new() -> Self {
+        let inner = StreamInner {
+            read_ready_event_send: EventGenerator::new(),
+            write_ready_event_send: EventGenerator::new(),
+            read_addr: AtomicPtr::new(core::ptr::null_mut()),
+            read_size: AtomicUsize::new(0),
+            ready_addr: AtomicPtr::new(core::ptr::null_mut()),
+            ready_size: AtomicIsize::new(results::BLOCKED),
+            ready_capacity: AtomicUsize::new(0),
+        };
+        Self(Arc::new(inner))
+    }
+
+    fn is_write_closed(&self) -> bool {
+        self.0.ready_addr.load(Ordering::Acquire) as usize == EOF_MARKER
+    }
+
+    fn start_reading(&self, buffer: symmetric_stream::Buffer) {
+        let buf = buffer.get::<Buffer>().get_address().take_handle() as *mut ();
+        let size = buffer.get::<Buffer>().capacity();
+        let old_readya = self.0.ready_addr.load(Ordering::Acquire);
+        let old_ready = self.0.ready_size.load(Ordering::Acquire);
+        if old_readya as usize == EOF_MARKER {
+            todo!();
+        }
+        assert!(old_ready == results::BLOCKED);
+        let old_size = self.0.read_size.swap(size as usize, Ordering::Acquire);
+        assert_eq!(old_size, 0);
+        let old_ptr = self.0.read_addr.swap(buf, Ordering::Release);
+        assert_eq!(old_ptr, std::ptr::null_mut());
+        self.write_ready_activate();
+    }
+
+    fn read_result(&self) -> Option<symmetric_stream::Buffer> {
+        let size = self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire);
+        let addr = self.0.ready_addr.swap(null_mut(), Ordering::Relaxed);
+        let capacity = self.0.ready_capacity.swap(0, Ordering::Relaxed);
+        if addr as usize == EOF_MARKER {
+            None
+        } else {
+            Some(symmetric_stream::Buffer::new(Buffer {
+                addr,
+                capacity,
+                size: AtomicUsize::new(size as usize),
+            }))
+        }
+    }
+
+    fn is_ready_to_write(&self) -> bool {
+        !self.0.read_addr.load(Ordering::Acquire).is_null()
+    }
+
+    fn start_writing(&self) -> symmetric_stream::Buffer {
+        let size = self.0.read_size.swap(0, Ordering::Acquire);
+        let addr = self
+            .0
+            .read_addr
+            .swap(core::ptr::null_mut(), Ordering::Relaxed);
+        self.0.ready_capacity.store(size, Ordering::Release);
+        symmetric_stream::Buffer::new(Buffer {
+            addr,
+            capacity: size,
+            size: AtomicUsize::new(0),
+        })
+    }
+
+    fn finish_writing(&self, buffer: Option<symmetric_stream::Buffer>) -> () {
+        let (elements, addr) = if let Some(buffer) = buffer {
+            let elements = buffer.get::<Buffer>().get_size() as isize;
+            let addr = buffer.get::<Buffer>().get_address().take_handle() as *mut ();
+            (elements, addr)
+        } else {
+            (0, EOF_MARKER as usize as *mut ())
+        };
+        let old_ready = self.0.ready_size.swap(elements as isize, Ordering::Relaxed);
+        let _old_readya = self.0.ready_addr.swap(addr, Ordering::Release);
+        assert_eq!(old_ready, results::BLOCKED);
+        self.read_ready_activate();
+    }
+
+    fn clone(&self) -> symmetric_stream::StreamObj {
+        symmetric_stream::StreamObj::new(StreamObj(Arc::clone(&self.0)))
+    }
+
+    fn write_ready_activate(&self) {
+        self.0.write_ready_event_send.activate();
+    }
+
+    fn read_ready_subscribe(&self) -> symmetric_stream::EventSubscription {
+        self.0.read_ready_event_send.subscribe()
+    }
+
+    fn write_ready_subscribe(&self) -> symmetric_stream::EventSubscription {
+        self.0.write_ready_event_send.subscribe()
+    }
+
+    fn read_ready_activate(&self) {
+        self.0.read_ready_event_send.activate();
+    }
+}
+
+const EOF_MARKER: usize = 1;
+
+impl symmetric_stream::Guest for Guest {
+    type Address = Dummy;
+
+    type Buffer = Buffer;
+
+    type StreamObj = StreamObj;
+}
diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs
new file mode 100644
index 000000000..9f6e88524
--- /dev/null
+++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs
@@ -0,0 +1,1544 @@
+// Generated by `wit-bindgen` 0.37.0. DO NOT EDIT!
+// Options used:
+#[allow(dead_code, clippy::all)]
+pub mod symmetric {
+    pub mod runtime {
+        /// This interface will only work with symmetric ABI (shared everything),
+        /// it can't be composed with the canonical ABI
+        /// Asynchronous executor functionality for symmetric ABI
+        #[allow(dead_code, unused_imports, clippy::all)]
+        pub mod symmetric_executor {
+            #[used]
+            #[doc(hidden)]
+            static __FORCE_SECTION_REF: fn() =
+                super::super::super::__link_custom_section_describing_imports;
+
+            use super::super::super::_rt;
+            /// These pseudo-resources are just used to
+            /// pass pointers to register
+            /// This wraps a user provided function of type
+            /// `fn (callback-data) -> callback-state`
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct CallbackFunction {
+                handle: _rt::Resource<CallbackFunction>,
+            }
+
+            impl CallbackFunction {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for CallbackFunction {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]callback-function"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle);
+                    }
+                }
+            }
+
+            /// This wraps opaque user data, freed by the callback once
+            /// it returns ready
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct CallbackData {
+                handle: _rt::Resource<CallbackData>,
+            }
+
+            impl CallbackData {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for CallbackData {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]callback-data"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle);
+                    }
+                }
+            }
+
+            /// The receiving side of an event
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct EventSubscription {
+                handle: _rt::Resource<EventSubscription>,
+            }
+
+            impl EventSubscription {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for EventSubscription {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]event-subscription"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle);
+                    }
+                }
+            }
+
+            /// A user controlled event
+
+            #[derive(Debug)]
+            #[repr(transparent)]
+            pub struct EventGenerator {
+                handle: _rt::Resource<EventGenerator>,
+            }
+
+            impl EventGenerator {
+                #[doc(hidden)]
+                pub unsafe fn from_handle(handle: usize) -> Self {
+                    Self {
+                        handle: _rt::Resource::from_handle(handle),
+                    }
+                }
+
+                #[doc(hidden)]
+                pub fn take_handle(&self) -> usize {
+                    _rt::Resource::take_handle(&self.handle)
+                }
+
+                #[doc(hidden)]
+                pub fn handle(&self) -> usize {
+                    _rt::Resource::handle(&self.handle)
+                }
+            }
+
+            unsafe impl _rt::WasmResource for EventGenerator {
+                #[inline]
+                unsafe fn drop(_handle: usize) {
+                    {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[resource-drop]event-generator"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(
+                                _: usize,
+                            );
+                        }
+
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle);
+                    }
+                }
+            }
+
+            /// Return value of an async call, lowest bit encoding
+            #[repr(u8)]
+            #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+            pub enum CallStatus {
+                /// For symmetric this means that processing has started, parameters should still remain valid until null,
+                /// params-read = non-null, results-written,done = null
+                Started,
+                /// For symmetric: Retry the call (temporarily out of memory)
+                NotStarted,
+            }
+            impl ::core::fmt::Debug for CallStatus {
+                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                    match self {
+                        CallStatus::Started => f.debug_tuple("CallStatus::Started").finish(),
+                        CallStatus::NotStarted => f.debug_tuple("CallStatus::NotStarted").finish(),
+                    }
+                }
+            }
+
+            impl CallStatus {
+                #[doc(hidden)]
+                pub unsafe fn _lift(val: u8) -> CallStatus {
+                    if !cfg!(debug_assertions) {
+                        return ::core::mem::transmute(val);
+                    }
+
+                    match val {
+                        0 => CallStatus::Started,
+                        1 => CallStatus::NotStarted,
+
+                        _ => panic!("invalid enum discriminant"),
+                    }
+                }
+            }
+
+            /// Return value of an event callback
+            #[repr(u8)]
+            #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
+            pub enum CallbackState {
+                /// Call the function again
+                Pending,
+                /// The function has completed, all results are written, data is freed,
+                /// calling the function again is not permitted as data became invalid!
+                Ready,
+            }
+            impl ::core::fmt::Debug for CallbackState {
+                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+                    match self {
+                        CallbackState::Pending => f.debug_tuple("CallbackState::Pending").finish(),
+                        CallbackState::Ready => f.debug_tuple("CallbackState::Ready").finish(),
+                    }
+                }
+            }
+
+            impl CallbackState {
+                #[doc(hidden)]
+                pub unsafe fn _lift(val: u8) -> CallbackState {
+                    if !cfg!(debug_assertions) {
+                        return ::core::mem::transmute(val);
+                    }
+
+                    match val {
+                        0 => CallbackState::Pending,
+                        1 => CallbackState::Ready,
+
+                        _ => panic!("invalid enum discriminant"),
+                    }
+                }
+            }
+
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Whether the event is active (used by poll implementation)
+                pub fn ready(&self) -> bool {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.ready"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready(
+                                _: *mut u8,
+                            ) -> i32;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8);
+                        _rt::bool_lift(ret as u8)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Create a timeout event
+                pub fn from_timeout(nanoseconds: u64) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[static]event-subscription.from-timeout"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(
+                                _: i64,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds));
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe)
+                pub fn dup(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.dup"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8);
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventSubscription {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Reset subscription to be inactive, only next trigger will ready it
+                pub fn reset(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-subscription.reset"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8);
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                pub fn new() -> Self {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[constructor]event-generator"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator(
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator();
+                        EventGenerator::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Get the receiving side (to pass to other parts of the program)
+                pub fn subscribe(&self) -> EventSubscription {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-generator.subscribe"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(
+                                _: *mut u8,
+                            ) -> *mut u8;
+                        }
+                        let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8);
+                        EventSubscription::from_handle(ret as usize)
+                    }
+                }
+            }
+            impl EventGenerator {
+                #[allow(unused_unsafe, clippy::all)]
+                /// Trigger all subscribers
+                pub fn activate(&self) -> () {
+                    unsafe {
+                        #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                        extern "C" {
+                            #[cfg_attr(
+                                target_arch = "wasm32",
+                                link_name = "[method]event-generator.activate"
+                            )]
+                            fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate(
+                                _: *mut u8,
+                            );
+                        }
+                        symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8);
+                    }
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            /// Wait until all registered events have completed
+            pub fn run() -> () {
+                unsafe {
+                    #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "run")]
+                        fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+                    }
+                    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run();
+                }
+            }
+            #[allow(unused_unsafe, clippy::all)]
+            /// Register a callback for an event
+            pub fn register(
+                trigger: EventSubscription,
+                callback: CallbackFunction,
+                data: CallbackData,
+            ) -> () {
+                unsafe {
+                    #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")]
+                    extern "C" {
+                        #[cfg_attr(target_arch = "wasm32", link_name = "register")]
+                        fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(
+                            _: *mut u8,
+                            _: *mut u8,
+                            _: *mut u8,
+                        );
+                    }
+                    symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(
+                        (&trigger).take_handle() as *mut u8,
+                        (&callback).take_handle() as *mut u8,
+                        (&data).take_handle() as *mut u8,
+                    );
+                }
+            }
+        }
+    }
+}
+#[allow(dead_code, clippy::all)]
+pub mod exports {
+    pub mod symmetric {
+        pub mod runtime {
+            /// language neutral stream implementation
+            #[allow(dead_code, unused_imports, clippy::all)]
+            pub mod symmetric_stream {
+                #[used]
+                #[doc(hidden)]
+                static __FORCE_SECTION_REF: fn() =
+                    super::super::super::super::__link_custom_section_describing_imports;
+
+                use super::super::super::super::_rt;
+                pub type EventSubscription = super::super::super::super::symmetric::runtime::symmetric_executor::EventSubscription;
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct Address {
+                    handle: _rt::Resource<Address>,
+                }
+
+                type _AddressRep<T> = Option<T>;
+
+                impl Address {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `Address`.
+                    pub fn new<T: GuestAddress>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _AddressRep<T> = Some(val);
+                        let ptr: *mut _AddressRep<T> = _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestAddress>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestAddress>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestAddress>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestAddress` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _AddressRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestAddress>(&self) -> *mut _AddressRep<T> {
+                        Address::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`Address`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct AddressBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a Address>,
+                }
+
+                impl<'a> AddressBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestAddress>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _AddressRep<T> {
+                        Address::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for Address {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]address"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(_handle);
+                        }
+                    }
+                }
+
+                /// special zero allocation/copy data type (caller provided buffer)
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct Buffer {
+                    handle: _rt::Resource<Buffer>,
+                }
+
+                type _BufferRep<T> = Option<T>;
+
+                impl Buffer {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `Buffer`.
+                    pub fn new<T: GuestBuffer>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _BufferRep<T> = Some(val);
+                        let ptr: *mut _BufferRep<T> = _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestBuffer>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestBuffer>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestBuffer>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestBuffer` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _BufferRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestBuffer>(&self) -> *mut _BufferRep<T> {
+                        Buffer::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`Buffer`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct BufferBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a Buffer>,
+                }
+
+                impl<'a> BufferBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestBuffer>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _BufferRep<T> {
+                        Buffer::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for Buffer {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]buffer"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(_handle);
+                        }
+                    }
+                }
+
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct StreamObj {
+                    handle: _rt::Resource<StreamObj>,
+                }
+
+                type _StreamObjRep<T> = Option<T>;
+
+                impl StreamObj {
+                    /// Creates a new resource from the specified representation.
+                    ///
+                    /// This function will create a new resource handle by moving `val` onto
+                    /// the heap and then passing that heap pointer to the component model to
+                    /// create a handle. The owned handle is then returned as `StreamObj`.
+                    pub fn new<T: GuestStreamObj>(val: T) -> Self {
+                        Self::type_guard::<T>();
+                        let val: _StreamObjRep<T> = Some(val);
+                        let ptr: *mut _StreamObjRep<T> = _rt::Box::into_raw(_rt::Box::new(val));
+                        unsafe { Self::from_handle(T::_resource_new(ptr.cast())) }
+                    }
+
+                    /// Gets access to the underlying `T` which represents this resource.
+                    pub fn get<T: GuestStreamObj>(&self) -> &T {
+                        let ptr = unsafe { &*self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    /// Gets mutable access to the underlying `T` which represents this
+                    /// resource.
+                    pub fn get_mut<T: GuestStreamObj>(&mut self) -> &mut T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_mut().unwrap()
+                    }
+
+                    /// Consumes this resource and returns the underlying `T`.
+                    pub fn into_inner<T: GuestStreamObj>(self) -> T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.take().unwrap()
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn from_handle(handle: usize) -> Self {
+                        Self {
+                            handle: _rt::Resource::from_handle(handle),
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub fn take_handle(&self) -> usize {
+                        _rt::Resource::take_handle(&self.handle)
+                    }
+
+                    #[doc(hidden)]
+                    pub fn handle(&self) -> usize {
+                        _rt::Resource::handle(&self.handle)
+                    }
+
+                    // It's theoretically possible to implement the `GuestStreamObj` trait twice
+                    // so guard against using it with two different types here.
+                    #[doc(hidden)]
+                    fn type_guard<T: 'static>() {
+                        use core::any::TypeId;
+                        static mut LAST_TYPE: Option<TypeId> = None;
+                        unsafe {
+                            assert!(!cfg!(target_feature = "atomics"));
+                            let id = TypeId::of::<T>();
+                            match LAST_TYPE {
+                                Some(ty) => assert!(
+                                    ty == id,
+                                    "cannot use two types with this resource type"
+                                ),
+                                None => LAST_TYPE = Some(id),
+                            }
+                        }
+                    }
+
+                    #[doc(hidden)]
+                    pub unsafe fn dtor<T: 'static>(handle: *mut u8) {
+                        Self::type_guard::<T>();
+                        let _ = _rt::Box::from_raw(handle as *mut _StreamObjRep<T>);
+                    }
+
+                    fn as_ptr<T: GuestStreamObj>(&self) -> *mut _StreamObjRep<T> {
+                        StreamObj::type_guard::<T>();
+                        T::_resource_rep(self.handle()).cast()
+                    }
+                }
+
+                /// A borrowed version of [`StreamObj`] which represents a borrowed value
+                /// with the lifetime `'a`.
+                #[derive(Debug)]
+                #[repr(transparent)]
+                pub struct StreamObjBorrow<'a> {
+                    rep: *mut u8,
+                    _marker: core::marker::PhantomData<&'a StreamObj>,
+                }
+
+                impl<'a> StreamObjBorrow<'a> {
+                    #[doc(hidden)]
+                    pub unsafe fn lift(rep: usize) -> Self {
+                        Self {
+                            rep: rep as *mut u8,
+                            _marker: core::marker::PhantomData,
+                        }
+                    }
+
+                    /// Gets access to the underlying `T` in this resource.
+                    pub fn get<T: GuestStreamObj>(&self) -> &T {
+                        let ptr = unsafe { &mut *self.as_ptr::<T>() };
+                        ptr.as_ref().unwrap()
+                    }
+
+                    // NB: mutable access is not allowed due to the component model allowing
+                    // multiple borrows of the same resource.
+
+                    fn as_ptr<T: 'static>(&self) -> *mut _StreamObjRep<T> {
+                        StreamObj::type_guard::<T>();
+                        self.rep.cast()
+                    }
+                }
+
+                unsafe impl _rt::WasmResource for StreamObj {
+                    #[inline]
+                    unsafe fn drop(_handle: usize) {
+                        {
+                            #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")]
+                            extern "C" {
+                                #[cfg_attr(
+                                    target_arch = "wasm32",
+                                    link_name = "[resource-drop]stream-obj"
+                                )]
+                                fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(
+                                    _: usize,
+                                );
+                            }
+
+                            symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(_handle);
+                        }
+                    }
+                }
+
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_constructor_buffer_cabi<T: GuestBuffer>(
+                    arg0: *mut u8,
+                    arg1: i64,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 =
+                        Buffer::new(T::new(Address::from_handle(arg0 as usize), arg1 as u64));
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_buffer_get_address_cabi<T: GuestBuffer>(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::get_address(BufferBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_buffer_get_size_cabi<T: GuestBuffer>(
+                    arg0: *mut u8,
+                ) -> i64 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::get_size(BufferBorrow::lift(arg0 as usize).get());
+                    _rt::as_i64(result0)
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_buffer_set_size_cabi<T: GuestBuffer>(
+                    arg0: *mut u8,
+                    arg1: i64,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::set_size(BufferBorrow::lift(arg0 as usize).get(), arg1 as u64);
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_buffer_capacity_cabi<T: GuestBuffer>(
+                    arg0: *mut u8,
+                ) -> i64 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::capacity(BufferBorrow::lift(arg0 as usize).get());
+                    _rt::as_i64(result0)
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_constructor_stream_obj_cabi<T: GuestStreamObj>() -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = StreamObj::new(T::new());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_clone_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::clone(StreamObjBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_is_write_closed_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                ) -> i32 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::is_write_closed(StreamObjBorrow::lift(arg0 as usize).get());
+                    match result0 {
+                        true => 1,
+                        false => 0,
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_start_reading_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                    arg1: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::start_reading(
+                        StreamObjBorrow::lift(arg0 as usize).get(),
+                        Buffer::from_handle(arg1 as usize),
+                    );
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_write_ready_activate_cabi<
+                    T: GuestStreamObj,
+                >(
+                    arg0: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::write_ready_activate(StreamObjBorrow::lift(arg0 as usize).get());
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_read_ready_subscribe_cabi<
+                    T: GuestStreamObj,
+                >(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 =
+                        T::read_ready_subscribe(StreamObjBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_read_result_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                    arg1: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::read_result(StreamObjBorrow::lift(arg0 as usize).get());
+                    match result0 {
+                        Some(e) => {
+                            *arg1.add(0).cast::<u8>() = (1i32) as u8;
+                            *arg1
+                                .add(core::mem::size_of::<*const u8>())
+                                .cast::<*mut u8>() = (e).take_handle() as *mut u8;
+                        }
+                        None => {
+                            *arg1.add(0).cast::<u8>() = (0i32) as u8;
+                        }
+                    };
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_is_ready_to_write_cabi<
+                    T: GuestStreamObj,
+                >(
+                    arg0: *mut u8,
+                ) -> i32 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::is_ready_to_write(StreamObjBorrow::lift(arg0 as usize).get());
+                    match result0 {
+                        true => 1,
+                        false => 0,
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_write_ready_subscribe_cabi<
+                    T: GuestStreamObj,
+                >(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 =
+                        T::write_ready_subscribe(StreamObjBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_start_writing_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                ) -> *mut u8 {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    let result0 = T::start_writing(StreamObjBorrow::lift(arg0 as usize).get());
+                    (result0).take_handle() as *mut u8
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_finish_writing_cabi<T: GuestStreamObj>(
+                    arg0: *mut u8,
+                    arg1: i32,
+                    arg2: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::finish_writing(
+                        StreamObjBorrow::lift(arg0 as usize).get(),
+                        match arg1 {
+                            0 => None,
+                            1 => {
+                                let e = Buffer::from_handle(arg2 as usize);
+                                Some(e)
+                            }
+                            _ => _rt::invalid_enum_discriminant(),
+                        },
+                    );
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_method_stream_obj_read_ready_activate_cabi<
+                    T: GuestStreamObj,
+                >(
+                    arg0: *mut u8,
+                ) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    T::read_ready_activate(StreamObjBorrow::lift(arg0 as usize).get());
+                }
+                pub trait Guest {
+                    type Address: GuestAddress;
+                    type Buffer: GuestBuffer;
+                    type StreamObj: GuestStreamObj;
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_address_cabi<T: GuestAddress>(arg0: usize) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    Address::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestAddress: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_buffer_cabi<T: GuestBuffer>(arg0: usize) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    Buffer::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestBuffer: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+
+                    fn new(addr: Address, capacity: u64) -> Self;
+                    fn get_address(&self) -> Address;
+                    fn get_size(&self) -> u64;
+                    fn set_size(&self, size: u64) -> ();
+                    fn capacity(&self) -> u64;
+                }
+                #[doc(hidden)]
+                #[allow(non_snake_case)]
+                pub unsafe fn _export_drop_streamObj_cabi<T: GuestStreamObj>(arg0: usize) {
+                    #[cfg(target_arch = "wasm32")]
+                    _rt::run_ctors_once();
+                    StreamObj::dtor::<T>(arg0 as *mut u8);
+                }
+                pub trait GuestStreamObj: 'static {
+                    #[doc(hidden)]
+                    unsafe fn _resource_new(val: *mut u8) -> usize
+                    where
+                        Self: Sized,
+                    {
+                        val as usize
+                    }
+
+                    #[doc(hidden)]
+                    fn _resource_rep(handle: usize) -> *mut u8
+                    where
+                        Self: Sized,
+                    {
+                        handle as *mut u8
+                    }
+
+                    fn new() -> Self;
+                    /// create a new instance e.g. for reading or tasks
+                    fn clone(&self) -> StreamObj;
+                    /// reading (in roughly chronological order)
+                    fn is_write_closed(&self) -> bool;
+                    fn start_reading(&self, buffer: Buffer) -> ();
+                    fn write_ready_activate(&self) -> ();
+                    fn read_ready_subscribe(&self) -> EventSubscription;
+                    /// none is EOF
+                    fn read_result(&self) -> Option<Buffer>;
+                    /// writing
+                    fn is_ready_to_write(&self) -> bool;
+                    fn write_ready_subscribe(&self) -> EventSubscription;
+                    fn start_writing(&self) -> Buffer;
+                    /// none is EOF
+                    fn finish_writing(&self, buffer: Option<Buffer>) -> ();
+                    fn read_ready_activate(&self) -> ();
+                }
+                #[doc(hidden)]
+
+                macro_rules! __export_symmetric_runtime_symmetric_stream_0_1_0_cabi{
+  ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = {
+
+    #[cfg_attr(target_arch = "wasm32", export_name = "[constructor]buffer")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer(arg0: *mut u8,arg1: i64,) -> *mut u8 {
+      $($path_to_types)*::_export_constructor_buffer_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0, arg1)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]buffer.get-address")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_buffer_get_address_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]buffer.get-size")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size(arg0: *mut u8,) -> i64 {
+      $($path_to_types)*::_export_method_buffer_get_size_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]buffer.set-size")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size(arg0: *mut u8,arg1: i64,) {
+      $($path_to_types)*::_export_method_buffer_set_size_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0, arg1)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]buffer.capacity")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity(arg0: *mut u8,) -> i64 {
+      $($path_to_types)*::_export_method_buffer_capacity_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[constructor]stream-obj")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj() -> *mut u8 {
+      $($path_to_types)*::_export_constructor_stream_obj_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>()
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.clone")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_stream_obj_clone_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.is-write-closed")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed(arg0: *mut u8,) -> i32 {
+      $($path_to_types)*::_export_method_stream_obj_is_write_closed_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.start-reading")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading(arg0: *mut u8,arg1: *mut u8,) {
+      $($path_to_types)*::_export_method_stream_obj_start_reading_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.write-ready-activate")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(arg0: *mut u8,) {
+      $($path_to_types)*::_export_method_stream_obj_write_ready_activate_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.read-ready-subscribe")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_stream_obj_read_ready_subscribe_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.read-result")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result(arg0: *mut u8,arg1: *mut u8,) {
+      $($path_to_types)*::_export_method_stream_obj_read_result_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.is-ready-to-write")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write(arg0: *mut u8,) -> i32 {
+      $($path_to_types)*::_export_method_stream_obj_is_ready_to_write_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.write-ready-subscribe")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_stream_obj_write_ready_subscribe_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.start-writing")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing(arg0: *mut u8,) -> *mut u8 {
+      $($path_to_types)*::_export_method_stream_obj_start_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.finish-writing")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: *mut u8,) {
+      $($path_to_types)*::_export_method_stream_obj_finish_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1, arg2)
+    }
+    #[cfg_attr(target_arch = "wasm32", export_name = "[method]stream-obj.read-ready-activate")]
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate(arg0: *mut u8,) {
+      $($path_to_types)*::_export_method_stream_obj_read_ready_activate_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(arg0: usize) {
+      $($path_to_types)*::_export_drop_address_cabi::<<$ty as $($path_to_types)*::Guest>::Address>(arg0)
+    }
+
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(arg0: usize) {
+      $($path_to_types)*::_export_drop_buffer_cabi::<<$ty as $($path_to_types)*::Guest>::Buffer>(arg0)
+    }
+
+    #[cfg_attr(not(target_arch = "wasm32"), no_mangle)]
+    unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(arg0: usize) {
+      $($path_to_types)*::_export_drop_streamObj_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0)
+    }
+
+  };);
+}
+                #[doc(hidden)]
+                pub(crate) use __export_symmetric_runtime_symmetric_stream_0_1_0_cabi;
+            }
+        }
+    }
+}
+mod _rt {
+    #![allow(dead_code, clippy::all)]
+
+    use core::fmt;
+    use core::marker;
+    use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
+
+    /// A type which represents a component model resource, either imported or
+    /// exported into this component.
+    ///
+    /// This is a low-level wrapper which handles the lifetime of the resource
+    /// (namely this has a destructor). The `T` provided defines the component model
+    /// intrinsics that this wrapper uses.
+    ///
+    /// One of the chief purposes of this type is to provide `Deref` implementations
+    /// to access the underlying data when it is owned.
+    ///
+    /// This type is primarily used in generated code for exported and imported
+    /// resources.
+    #[repr(transparent)]
+    pub struct Resource<T: WasmResource> {
+        // NB: This would ideally be `usize` but it is not. The fact that this has
+        // interior mutability is not exposed in the API of this type except for the
+        // `take_handle` method which is supposed to in theory be private.
+        //
+        // This represents, almost all the time, a valid handle value. When it's
+        // invalid it's stored as `0`.
+        handle: AtomicUsize,
+        _marker: marker::PhantomData<T>,
+    }
+
+    /// A trait which all wasm resources implement, namely providing the ability to
+    /// drop a resource.
+    ///
+    /// This generally is implemented by generated code, not user-facing code.
+    #[allow(clippy::missing_safety_doc)]
+    pub unsafe trait WasmResource {
+        /// Invokes the `[resource-drop]...` intrinsic.
+        unsafe fn drop(handle: usize);
+    }
+
+    impl<T: WasmResource> Resource<T> {
+        #[doc(hidden)]
+        pub unsafe fn from_handle(handle: usize) -> Self {
+            debug_assert!(handle != 0);
+            Self {
+                handle: AtomicUsize::new(handle),
+                _marker: marker::PhantomData,
+            }
+        }
+
+        /// Takes ownership of the handle owned by `resource`.
+        ///
+        /// Note that this ideally would be `into_handle` taking `Resource<T>` by
+        /// ownership. The code generator does not enable that in all situations,
+        /// unfortunately, so this is provided instead.
+        ///
+        /// Also note that `take_handle` is in theory only ever called on values
+        /// owned by a generated function. For example a generated function might
+        /// take `Resource<T>` as an argument but then call `take_handle` on a
+        /// reference to that argument. In that sense the dynamic nature of
+        /// `take_handle` should only be exposed internally to generated code, not
+        /// to user code.
+        #[doc(hidden)]
+        pub fn take_handle(resource: &Resource<T>) -> usize {
+            resource.handle.swap(0, Relaxed)
+        }
+
+        #[doc(hidden)]
+        pub fn handle(resource: &Resource<T>) -> usize {
+            resource.handle.load(Relaxed)
+        }
+    }
+
+    impl<T: WasmResource> fmt::Debug for Resource<T> {
+        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+            f.debug_struct("Resource")
+                .field("handle", &self.handle)
+                .finish()
+        }
+    }
+
+    impl<T: WasmResource> Drop for Resource<T> {
+        fn drop(&mut self) {
+            unsafe {
+                match self.handle.load(Relaxed) {
+                    // If this handle was "taken" then don't do anything in the
+                    // destructor.
+                    0 => {}
+
+                    // ... but otherwise do actually destroy it with the imported
+                    // component model intrinsic as defined through `T`.
+                    other => T::drop(other),
+                }
+            }
+        }
+    }
+    pub unsafe fn bool_lift(val: u8) -> bool {
+        if cfg!(debug_assertions) {
+            match val {
+                0 => false,
+                1 => true,
+                _ => panic!("invalid bool discriminant"),
+            }
+        } else {
+            val != 0
+        }
+    }
+
+    pub fn as_i64<T: AsI64>(t: T) -> i64 {
+        t.as_i64()
+    }
+
+    pub trait AsI64 {
+        fn as_i64(self) -> i64;
+    }
+
+    impl<'a, T: Copy + AsI64> AsI64 for &'a T {
+        fn as_i64(self) -> i64 {
+            (*self).as_i64()
+        }
+    }
+
+    impl AsI64 for i64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+
+    impl AsI64 for u64 {
+        #[inline]
+        fn as_i64(self) -> i64 {
+            self as i64
+        }
+    }
+    pub use alloc_crate::boxed::Box;
+
+    #[cfg(target_arch = "wasm32")]
+    pub fn run_ctors_once() {
+        wit_bindgen::rt::run_ctors_once();
+    }
+    pub unsafe fn invalid_enum_discriminant<T>() -> T {
+        if cfg!(debug_assertions) {
+            panic!("invalid enum discriminant")
+        } else {
+            core::hint::unreachable_unchecked()
+        }
+    }
+    extern crate alloc as alloc_crate;
+}
+
+/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as
+/// the root implementation of all generated traits.
+///
+/// For more information see the documentation of `wit_bindgen::generate!`.
+///
+/// ```rust
+/// # macro_rules! export{ ($($t:tt)*) => (); }
+/// # trait Guest {}
+/// struct MyType;
+///
+/// impl Guest for MyType {
+///     // ...
+/// }
+///
+/// export!(MyType);
+/// ```
+#[allow(unused_macros)]
+#[doc(hidden)]
+
+macro_rules! __export_stream_impl_impl {
+  ($ty:ident) => (self::export!($ty with_types_in self););
+  ($ty:ident with_types_in $($path_to_types_root:tt)*) => (
+  $($path_to_types_root)*::exports::symmetric::runtime::symmetric_stream::__export_symmetric_runtime_symmetric_stream_0_1_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::symmetric::runtime::symmetric_stream);
+  )
+}
+#[doc(inline)]
+pub(crate) use __export_stream_impl_impl as export;
+
+#[cfg(target_arch = "wasm32")]
+#[unsafe(link_section = "component-type:wit-bindgen:0.37.0:symmetric:runtime@0.1.0:stream-impl:encoded world")]
+#[doc(hidden)]
+#[allow(clippy::octal_escapes)]
+pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1726] = *b"\
+\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xbc\x0c\x01A\x02\x01\
+A\x05\x01B\x20\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\
+\x04\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x01m\x02\x07\
+started\x0bnot-started\x04\0\x0bcall-status\x03\0\x04\x01m\x02\x07pending\x05rea\
+dy\x04\0\x0ecallback-state\x03\0\x06\x01h\x02\x01@\x01\x04self\x08\0\x7f\x04\0\x20\
+[method]event-subscription.ready\x01\x09\x01i\x02\x01@\x01\x0bnanosecondsw\0\x0a\
+\x04\0'[static]event-subscription.from-timeout\x01\x0b\x01@\x01\x04self\x08\0\x0a\
+\x04\0\x1e[method]event-subscription.dup\x01\x0c\x01@\x01\x04self\x08\x01\0\x04\0\
+\x20[method]event-subscription.reset\x01\x0d\x01i\x03\x01@\0\0\x0e\x04\0\x1c[con\
+structor]event-generator\x01\x0f\x01h\x03\x01@\x01\x04self\x10\0\x0a\x04\0![meth\
+od]event-generator.subscribe\x01\x11\x01@\x01\x04self\x10\x01\0\x04\0\x20[method\
+]event-generator.activate\x01\x12\x01@\0\x01\0\x04\0\x03run\x01\x13\x01i\0\x01i\x01\
+\x01@\x03\x07trigger\x0a\x08callback\x14\x04data\x15\x01\0\x04\0\x08register\x01\
+\x16\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x02\x03\0\0\x12event\
+-subscription\x01B*\x02\x03\x02\x01\x01\x04\0\x12event-subscription\x03\0\0\x04\0\
+\x07address\x03\x01\x04\0\x06buffer\x03\x01\x04\0\x0astream-obj\x03\x01\x01i\x02\
+\x01i\x03\x01@\x02\x04addr\x05\x08capacityw\0\x06\x04\0\x13[constructor]buffer\x01\
+\x07\x01h\x03\x01@\x01\x04self\x08\0\x05\x04\0\x1a[method]buffer.get-address\x01\
+\x09\x01@\x01\x04self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04\
+self\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[metho\
+d]buffer.capacity\x01\x0a\x01i\x04\x01@\0\0\x0c\x04\0\x17[constructor]stream-obj\
+\x01\x0d\x01h\x04\x01@\x01\x04self\x0e\0\x0c\x04\0\x18[method]stream-obj.clone\x01\
+\x0f\x01@\x01\x04self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\
+\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-read\
+ing\x01\x11\x01@\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-acti\
+vate\x01\x12\x01i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-r\
+eady-subscribe\x01\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stre\
+am-obj.read-result\x01\x16\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\
+\0([method]stream-obj.write-ready-subscribe\x01\x14\x01@\x01\x04self\x0e\0\x06\x04\
+\0\x20[method]stream-obj.start-writing\x01\x17\x01@\x02\x04self\x0e\x06buffer\x15\
+\x01\0\x04\0![method]stream-obj.finish-writing\x01\x18\x04\0&[method]stream-obj.\
+read-ready-activate\x01\x12\x04\0(symmetric:runtime/symmetric-stream@0.1.0\x05\x02\
+\x04\0#symmetric:runtime/stream-impl@0.1.0\x04\0\x0b\x11\x01\0\x0bstream-impl\x03\
+\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.223.0\x10wit-\
+bindgen-rust\x060.37.0";
+
+#[inline(never)]
+#[doc(hidden)]
+pub fn __link_custom_section_describing_imports() {
+    wit_bindgen::rt::maybe_link_cabi_realloc();
+}
diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit
new file mode 100644
index 000000000..edc79583c
--- /dev/null
+++ b/crates/symmetric_executor/wit/executor.wit
@@ -0,0 +1,93 @@
+// This interface will only work with symmetric ABI (shared everything),
+// it can't be composed with the canonical ABI
+
+/// Asynchronous executor functionality for symmetric ABI
+interface symmetric-executor {
+    // These pseudo-resources are just used to
+    // pass pointers to register
+
+    /// This wraps a user provided function of type
+    /// `fn (callback-data) -> callback-state`
+    resource callback-function;
+    /// This wraps opaque user data, freed by the callback once
+    /// it returns ready
+    resource callback-data;
+
+    /// The receiving side of an event
+    resource event-subscription {
+        /// Whether the event is active (used by poll implementation)
+        ready: func() -> bool;
+        /// Create a timeout event
+        from-timeout: static func(nanoseconds: u64) -> event-subscription;
+        /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe)
+        dup: func() -> event-subscription;
+        /// Reset subscription to be inactive, only next trigger will ready it
+        reset: func();
+    }
+    /// A user controlled event
+    resource event-generator {
+        constructor();
+        /// Get the receiving side (to pass to other parts of the program)
+        subscribe: func() -> event-subscription;
+        /// Trigger all subscribers
+        activate: func();
+    }
+
+    /// Return value of an async call, lowest bit encoding
+    enum call-status {
+        /// For symmetric this means that processing has started, parameters should still remain valid until null,
+        /// params-read = non-null, results-written,done = null
+        started,
+        /// For symmetric: Retry the call (temporarily out of memory)
+        not-started,
+    }
+
+    /// Return value of an event callback
+    enum callback-state {
+        /// Call the function again
+        pending,
+        /// The function has completed, all results are written, data is freed,
+        /// calling the function again is not permitted as data became invalid!
+        ready,
+    }
+
+    /// Wait until all registered events have completed
+    run: func();
+    /// Register a callback for an event
+    register: func(trigger: event-subscription, callback: callback-function, data: callback-data);
+}
+
+// language neutral stream implementation
+interface symmetric-stream {
+    use symmetric-executor.{event-subscription};
+
+    resource address;
+    // special zero allocation/copy data type (caller provided buffer)
+    resource buffer {
+        constructor(addr: address, capacity: u64);
+        get-address: func () -> address;
+        get-size: func() -> u64;
+        set-size: func(size: u64);
+        capacity: func() -> u64;
+    }
+
+    resource stream-obj {
+        constructor();
+        // create a new instance e.g. for reading or tasks
+        clone: func() -> stream-obj;
+        // reading (in roughly chronological order)
+        is-write-closed: func() -> bool;
+        start-reading: func(buffer: buffer);
+        write-ready-activate: func();
+        read-ready-subscribe: func() -> event-subscription;
+        // none is EOF
+        read-result: func() -> option<buffer>;
+        // writing
+        is-ready-to-write: func() -> bool;
+        write-ready-subscribe: func() -> event-subscription;
+        start-writing: func() -> buffer;
+        // none is EOF
+        finish-writing: func(buffer: option<buffer>);
+        read-ready-activate: func();
+    }
+}
diff --git a/crates/symmetric_executor/wit/world.wit b/crates/symmetric_executor/wit/world.wit
new file mode 100644
index 000000000..57b1fbe86
--- /dev/null
+++ b/crates/symmetric_executor/wit/world.wit
@@ -0,0 +1,14 @@
+package symmetric:runtime@0.1.0;
+
+world executor {
+    export symmetric-executor;
+}
+
+world stream-impl {
+    export symmetric-stream;
+}
+
+world module {
+    import symmetric-executor;
+    import symmetric-stream;
+}
diff --git a/crates/test-rust-wasm/Cargo.toml b/crates/test-rust-wasm/Cargo.toml
index 015e510eb..f082a38d0 100644
--- a/crates/test-rust-wasm/Cargo.toml
+++ b/crates/test-rust-wasm/Cargo.toml
@@ -6,7 +6,10 @@ publish = false
 
 [dependencies]
 wit-bindgen = { path = "../guest-rust" }
+wit-bindgen-rt = { path = '../guest-rust/rt' }
 rust-xcrate-test = { path = './rust-xcrate-test' }
+futures = "0.3"
+once_cell = "1.20"
 
 [lib]
 test = false
diff --git a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml
index 0e3825024..10b92b34e 100644
--- a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml
+++ b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml
@@ -3,5 +3,10 @@ name = "rust-xcrate-test"
 edition = "2024"
 publish = false
 
+[build-dependencies]
+
 [dependencies]
 wit-bindgen = { path = '../../guest-rust' }
+wit-bindgen-rt = { path = '../../guest-rust/rt' }
+futures = "0.3"
+once_cell = "1.20"
diff --git a/crates/test-rust-wasm/src/bin/strings.rs b/crates/test-rust-wasm/src/bin/strings.rs
new file mode 100644
index 000000000..78305fdce
--- /dev/null
+++ b/crates/test-rust-wasm/src/bin/strings.rs
@@ -0,0 +1,3 @@
+include!("../../../../tests/runtime/strings/wasm.rs");
+
+fn main() {}
diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs
index 23d9a96cd..d47198e39 100644
--- a/src/bin/wit-bindgen.rs
+++ b/src/bin/wit-bindgen.rs
@@ -46,6 +46,22 @@ enum Opt {
         #[clap(flatten)]
         args: Common,
     },
+    /// Generates bindings for bridge modules between wasm and native.
+    #[cfg(feature = "bridge")]
+    Bridge {
+        #[clap(flatten)]
+        opts: wit_bindgen_bridge::Opts,
+        #[clap(flatten)]
+        args: Common,
+    },
+    /// Generates bindings for C/CPP host modules.
+    #[cfg(feature = "cpp")]
+    Cpp {
+        #[clap(flatten)]
+        opts: wit_bindgen_cpp::Opts,
+        #[clap(flatten)]
+        args: Common,
+    },
 
     /// Generates bindings for TinyGo-based Go guest modules (Deprecated)
     #[cfg(feature = "go")]
@@ -125,6 +141,10 @@ fn main() -> Result<()> {
         Opt::Moonbit { opts, args } => (opts.build(), args),
         #[cfg(feature = "c")]
         Opt::C { opts, args } => (opts.build(), args),
+        #[cfg(feature = "bridge")]
+        Opt::Bridge { opts, args } => (opts.build(), args),
+        #[cfg(feature = "cpp")]
+        Opt::Cpp { opts, args } => (opts.build(), args),
         #[cfg(feature = "rust")]
         Opt::Rust { opts, args } => (opts.build(), args),
         #[cfg(feature = "go")]
@@ -207,7 +227,8 @@ fn gen_world(
         }
     }
     let (pkg, _files) = resolve.push_path(&opts.wit)?;
-    let world = resolve.select_world(pkg, opts.world.as_deref())?;
+    let mut world = resolve.select_world(pkg, opts.world.as_deref())?;
+    generator.apply_resolve_options(&mut resolve, &mut world);
     generator.generate(&resolve, world, files)?;
 
     Ok(())
diff --git a/tests/autosar/ara.wit b/tests/autosar/ara.wit
new file mode 100644
index 000000000..480159399
--- /dev/null
+++ b/tests/autosar/ara.wit
@@ -0,0 +1,99 @@
+package autosar:ara
+
+interface types {
+    type error-code-type = s32
+
+    resource error-domain {
+        name: func() -> string
+        message: func(n: error-code-type) -> string
+        // strictly no dtor, because of static lifetime
+    }
+        
+    record error-code {
+        value: error-code-type,
+        domain: s32, // borrow<error-domain>,
+    }
+}
+
+interface e2exf {
+    enum configuration-format { json, xml }
+
+    // status-handler-configure: func(
+    //     binding-configuration: string, binding-format: configuration-format, 
+    //     e2exf-configuration: string, e2exf-format: configuration-format) -> bool
+}
+
+interface core {
+    use types.{error-code}
+    resource instance-specifier {
+//        constructor(spec: string)
+        to-string: func() -> string
+        // needed due to SWS_CM_00118 (by value)
+        clone: func() -> instance-specifier
+        create: static func(spec: string) -> result<instance-specifier, error-code>
+    }
+        
+    initialize: func() -> result<_, error-code>
+    deinitialize: func() -> result<_, error-code>
+
+    create-instance-specifier: func(spec: string) -> result<instance-specifier, error-code>
+//    instance-specifier: func() -> result<instance-specifier, error-code>
+}
+
+interface log {
+    resource logger {
+        report: func(level: u32, message: string)
+        constructor(context: string, description: string, level: u32)
+    }
+    
+//    create: func(context: string, description: string, level: u32) -> logger-handle
+}
+interface com {
+    resource instance-identifier {
+        constructor(id: string)
+        to-string: func() -> string
+    }
+
+    enum s-m-state {
+        valid,
+        no-data,
+        init,
+        invalid,
+        state-m-disabled,
+    }
+
+    enum profile-check-status {
+        ok,
+        repeated,
+        wrong-sequence,
+        error,
+        not-available,
+        check-disabled,
+    }
+    
+    use e2exf.{configuration-format}
+    use core.{instance-specifier}
+
+    // use e2exf.{status-handler-configure} (doesn't work for some reason)
+    status-handler-configure: func(
+        binding-configuration: string, binding-format: configuration-format, 
+        e2exf-configuration: string, e2exf-format: configuration-format) -> bool
+    resolve-instance-ids: func(spec: borrow<instance-specifier>) -> list<instance-identifier>
+}
+interface exec {
+    enum execution-state { running }
+
+    resource execution-client {
+        constructor()
+        report-execution-state: func(state: execution-state)
+    }
+    
+//    create-execution-client: func() -> execution-client
+}
+
+world ara {
+    import core
+    import log
+    import com
+    import exec
+}
diff --git a/tests/autosar/deps.lock b/tests/autosar/deps.lock
new file mode 100644
index 000000000..fab8231c5
--- /dev/null
+++ b/tests/autosar/deps.lock
@@ -0,0 +1,26 @@
+[cli]
+url = "https://github.com/WebAssembly/wasi-cli/archive/main.tar.gz"
+sha256 = "fb029d0f9468fcb404a079a58fafd9265ef99c0ee1350835348da7b6e105c597"
+sha512 = "8602e881281adc67b1ac5a4eb0888636d6f50d15bd14e36dcc446a51551f3f9bb3e9eabb776d723bb113bf1e26a702c5042de095e66e897c3d3cf689e0b7d4f9"
+deps = ["clocks", "filesystem", "random", "sockets"]
+
+[clocks]
+sha256 = "89da8eca4cd195516574c89c5b3c24a7b5af3ff2565c16753d20d3bdbc5fc60f"
+sha512 = "244079b3f592d58478a97adbd0bee8d49ae9dd1a3e435651ee40997b50da9fe62cfaba7e3ec7f7406d7d0288d278a43a3a0bc5150226ba40ce0f8ac6d33f7ddb"
+
+[filesystem]
+sha256 = "05952bbc98895aa3aeda6c765a3e521016de59f993f3b60394c724640935c09c"
+sha512 = "2c242489801a75466986fe014d730fb3aa7b5c6e56a230c8735e6672711b58bcbe92ba78a341b78fc3ac69339e55d3859d8bb14410230f0371ee30dbd83add64"
+
+[io]
+url = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz"
+sha256 = "b622db2755978a49d18d35d84d75f66b2b1ed23d7bf413e5c9e152e190cc7d4b"
+sha512 = "d19c9004e75bf3ebe3e34cff498c3d7fee04cd57a7fba7ed12a0c5ad842ba5715c009de77a152c57da0500f6ca0986b6791b6f022829bdd5a024f7bc114c2ff6"
+
+[random]
+sha256 = "11afcbff9920f5f1f72b6764d01e59a5faa2c671f0c59f0c9b405778f3708745"
+sha512 = "cc4fa3d178559a89d9d6a376e3359b892158d1e73317c5db1f797ebc6b0b57abf2422797648d5b2b494c50cf9360720bc451cc27e15def7d278ba875805ccbf5"
+
+[sockets]
+sha256 = "b5c2e9cc87cefbaef06bbe9978f9bc336da9feee2d51747bc28e10164fc46c39"
+sha512 = "3aea6fe0c768b27d5c5cb3adab5e60dc936198f8b677c2cf6c4d57a0460db87eb779e0b577f1240fb2a6bf3ade49919fbffe39b0137bce3242343e6091cc7510"
diff --git a/tests/autosar/deps.toml b/tests/autosar/deps.toml
new file mode 100644
index 000000000..968b655bf
--- /dev/null
+++ b/tests/autosar/deps.toml
@@ -0,0 +1,4 @@
+# Use `wit-deps update` to pull in latest changes from "dynamic" branch references
+#poll = "https://github.com/WebAssembly/wasi-poll/archive/main.tar.gz"
+io = "https://github.com/WebAssembly/wasi-io/archive/main.tar.gz"
+cli = "https://github.com/WebAssembly/wasi-cli/archive/main.tar.gz"
diff --git a/tests/autosar/deps/cli/command.wit b/tests/autosar/deps/cli/command.wit
new file mode 100644
index 000000000..74811d327
--- /dev/null
+++ b/tests/autosar/deps/cli/command.wit
@@ -0,0 +1,7 @@
+package wasi:cli@0.2.0-rc-2023-11-10;
+
+world command {
+  include reactor;
+
+  export run;
+}
diff --git a/tests/autosar/deps/cli/environment.wit b/tests/autosar/deps/cli/environment.wit
new file mode 100644
index 000000000..70065233e
--- /dev/null
+++ b/tests/autosar/deps/cli/environment.wit
@@ -0,0 +1,18 @@
+interface environment {
+  /// Get the POSIX-style environment variables.
+  ///
+  /// Each environment variable is provided as a pair of string variable names
+  /// and string value.
+  ///
+  /// Morally, these are a value import, but until value imports are available
+  /// in the component model, this import function should return the same
+  /// values each time it is called.
+  get-environment: func() -> list<tuple<string, string>>;
+
+  /// Get the POSIX-style arguments to the program.
+  get-arguments: func() -> list<string>;
+
+  /// Return a path that programs should use as their initial current working
+  /// directory, interpreting `.` as shorthand for this.
+  initial-cwd: func() -> option<string>;
+}
diff --git a/tests/autosar/deps/cli/exit.wit b/tests/autosar/deps/cli/exit.wit
new file mode 100644
index 000000000..d0c2b82ae
--- /dev/null
+++ b/tests/autosar/deps/cli/exit.wit
@@ -0,0 +1,4 @@
+interface exit {
+  /// Exit the current instance and any linked instances.
+  exit: func(status: result);
+}
diff --git a/tests/autosar/deps/cli/reactor.wit b/tests/autosar/deps/cli/reactor.wit
new file mode 100644
index 000000000..eafa2fd49
--- /dev/null
+++ b/tests/autosar/deps/cli/reactor.wit
@@ -0,0 +1,31 @@
+package wasi:cli@0.2.0-rc-2023-11-10;
+
+world reactor {
+  import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10;
+  import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10;
+  import wasi:filesystem/types@0.2.0-rc-2023-11-10;
+  import wasi:filesystem/preopens@0.2.0-rc-2023-11-10;
+  import wasi:sockets/instance-network@0.2.0-rc-2023-11-10;
+  import wasi:sockets/ip-name-lookup@0.2.0-rc-2023-11-10;
+  import wasi:sockets/network@0.2.0-rc-2023-11-10;
+  import wasi:sockets/tcp-create-socket@0.2.0-rc-2023-11-10;
+  import wasi:sockets/tcp@0.2.0-rc-2023-11-10;
+  import wasi:sockets/udp-create-socket@0.2.0-rc-2023-11-10;
+  import wasi:sockets/udp@0.2.0-rc-2023-11-10;
+  import wasi:random/random@0.2.0-rc-2023-11-10;
+  import wasi:random/insecure@0.2.0-rc-2023-11-10;
+  import wasi:random/insecure-seed@0.2.0-rc-2023-11-10;
+  import wasi:io/poll@0.2.0-rc-2023-11-10;
+  import wasi:io/streams@0.2.0-rc-2023-11-10;
+
+  import environment;
+  import exit;
+  import stdin;
+  import stdout;
+  import stderr;
+  import terminal-input;
+  import terminal-output;
+  import terminal-stdin;
+  import terminal-stdout;
+  import terminal-stderr;
+}
diff --git a/tests/autosar/deps/cli/run.wit b/tests/autosar/deps/cli/run.wit
new file mode 100644
index 000000000..a70ee8c03
--- /dev/null
+++ b/tests/autosar/deps/cli/run.wit
@@ -0,0 +1,4 @@
+interface run {
+  /// Run the program.
+  run: func() -> result;
+}
diff --git a/tests/autosar/deps/cli/stdio.wit b/tests/autosar/deps/cli/stdio.wit
new file mode 100644
index 000000000..1b653b6e2
--- /dev/null
+++ b/tests/autosar/deps/cli/stdio.wit
@@ -0,0 +1,17 @@
+interface stdin {
+  use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream};
+
+  get-stdin: func() -> input-stream;
+}
+
+interface stdout {
+  use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream};
+
+  get-stdout: func() -> output-stream;
+}
+
+interface stderr {
+  use wasi:io/streams@0.2.0-rc-2023-11-10.{output-stream};
+
+  get-stderr: func() -> output-stream;
+}
diff --git a/tests/autosar/deps/cli/terminal.wit b/tests/autosar/deps/cli/terminal.wit
new file mode 100644
index 000000000..47495769b
--- /dev/null
+++ b/tests/autosar/deps/cli/terminal.wit
@@ -0,0 +1,47 @@
+interface terminal-input {
+    /// The input side of a terminal.
+    resource terminal-input;
+
+    // In the future, this may include functions for disabling echoing,
+    // disabling input buffering so that keyboard events are sent through
+    // immediately, querying supported features, and so on.
+}
+
+interface terminal-output {
+    /// The output side of a terminal.
+    resource terminal-output;
+
+    // In the future, this may include functions for querying the terminal
+    // size, being notified of terminal size changes, querying supported
+    // features, and so on.
+}
+
+/// An interface providing an optional `terminal-input` for stdin as a
+/// link-time authority.
+interface terminal-stdin {
+    use terminal-input.{terminal-input};
+
+    /// If stdin is connected to a terminal, return a `terminal-input` handle
+    /// allowing further interaction with it.
+    get-terminal-stdin: func() -> option<terminal-input>;
+}
+
+/// An interface providing an optional `terminal-output` for stdout as a
+/// link-time authority.
+interface terminal-stdout {
+    use terminal-output.{terminal-output};
+
+    /// If stdout is connected to a terminal, return a `terminal-output` handle
+    /// allowing further interaction with it.
+    get-terminal-stdout: func() -> option<terminal-output>;
+}
+
+/// An interface providing an optional `terminal-output` for stderr as a
+/// link-time authority.
+interface terminal-stderr {
+    use terminal-output.{terminal-output};
+
+    /// If stderr is connected to a terminal, return a `terminal-output` handle
+    /// allowing further interaction with it.
+    get-terminal-stderr: func() -> option<terminal-output>;
+}
diff --git a/tests/autosar/deps/clocks/monotonic-clock.wit b/tests/autosar/deps/clocks/monotonic-clock.wit
new file mode 100644
index 000000000..09ef32c36
--- /dev/null
+++ b/tests/autosar/deps/clocks/monotonic-clock.wit
@@ -0,0 +1,45 @@
+package wasi:clocks@0.2.0-rc-2023-11-10;
+/// WASI Monotonic Clock is a clock API intended to let users measure elapsed
+/// time.
+///
+/// It is intended to be portable at least between Unix-family platforms and
+/// Windows.
+///
+/// A monotonic clock is a clock which has an unspecified initial value, and
+/// successive reads of the clock will produce non-decreasing values.
+///
+/// It is intended for measuring elapsed time.
+interface monotonic-clock {
+    use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable};
+
+    /// An instant in time, in nanoseconds. An instant is relative to an
+    /// unspecified initial value, and can only be compared to instances from
+    /// the same monotonic-clock.
+    type instant = u64;
+
+    /// A duration of time, in nanoseconds.
+    type duration = u64;
+
+    /// Read the current value of the clock.
+    ///
+    /// The clock is monotonic, therefore calling this function repeatedly will
+    /// produce a sequence of non-decreasing values.
+    now: func() -> instant;
+
+    /// Query the resolution of the clock. Returns the duration of time
+    /// corresponding to a clock tick.
+    resolution: func() -> duration;
+
+    /// Create a `pollable` which will resolve once the specified instant
+    /// occured.
+    subscribe-instant: func(
+        when: instant,
+    ) -> pollable;
+
+    /// Create a `pollable` which will resolve once the given duration has
+    /// elapsed, starting at the time at which this function was called.
+    /// occured.
+    subscribe-duration: func(
+        when: duration,
+    ) -> pollable;
+}
diff --git a/tests/autosar/deps/clocks/wall-clock.wit b/tests/autosar/deps/clocks/wall-clock.wit
new file mode 100644
index 000000000..8abb9a0c0
--- /dev/null
+++ b/tests/autosar/deps/clocks/wall-clock.wit
@@ -0,0 +1,42 @@
+package wasi:clocks@0.2.0-rc-2023-11-10;
+/// WASI Wall Clock is a clock API intended to let users query the current
+/// time. The name "wall" makes an analogy to a "clock on the wall", which
+/// is not necessarily monotonic as it may be reset.
+///
+/// It is intended to be portable at least between Unix-family platforms and
+/// Windows.
+///
+/// A wall clock is a clock which measures the date and time according to
+/// some external reference.
+///
+/// External references may be reset, so this clock is not necessarily
+/// monotonic, making it unsuitable for measuring elapsed time.
+///
+/// It is intended for reporting the current date and time for humans.
+interface wall-clock {
+    /// A time and date in seconds plus nanoseconds.
+    record datetime {
+        seconds: u64,
+        nanoseconds: u32,
+    }
+
+    /// Read the current value of the clock.
+    ///
+    /// This clock is not monotonic, therefore calling this function repeatedly
+    /// will not necessarily produce a sequence of non-decreasing values.
+    ///
+    /// The returned timestamps represent the number of seconds since
+    /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
+    /// also known as [Unix Time].
+    ///
+    /// The nanoseconds field of the output is always less than 1000000000.
+    ///
+    /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
+    /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
+    now: func() -> datetime;
+
+    /// Query the resolution of the clock.
+    ///
+    /// The nanoseconds field of the output is always less than 1000000000.
+    resolution: func() -> datetime;
+}
diff --git a/tests/autosar/deps/clocks/world.wit b/tests/autosar/deps/clocks/world.wit
new file mode 100644
index 000000000..8fa080f0e
--- /dev/null
+++ b/tests/autosar/deps/clocks/world.wit
@@ -0,0 +1,6 @@
+package wasi:clocks@0.2.0-rc-2023-11-10;
+
+world imports {
+    import monotonic-clock;
+    import wall-clock;
+}
diff --git a/tests/autosar/deps/filesystem/preopens.wit b/tests/autosar/deps/filesystem/preopens.wit
new file mode 100644
index 000000000..95ec67843
--- /dev/null
+++ b/tests/autosar/deps/filesystem/preopens.wit
@@ -0,0 +1,8 @@
+package wasi:filesystem@0.2.0-rc-2023-11-10;
+
+interface preopens {
+    use types.{descriptor};
+
+    /// Return the set of preopened directories, and their path.
+    get-directories: func() -> list<tuple<descriptor, string>>;
+}
diff --git a/tests/autosar/deps/filesystem/types.wit b/tests/autosar/deps/filesystem/types.wit
new file mode 100644
index 000000000..059722ab8
--- /dev/null
+++ b/tests/autosar/deps/filesystem/types.wit
@@ -0,0 +1,634 @@
+package wasi:filesystem@0.2.0-rc-2023-11-10;
+/// WASI filesystem is a filesystem API primarily intended to let users run WASI
+/// programs that access their files on their existing filesystems, without
+/// significant overhead.
+///
+/// It is intended to be roughly portable between Unix-family platforms and
+/// Windows, though it does not hide many of the major differences.
+///
+/// Paths are passed as interface-type `string`s, meaning they must consist of
+/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
+/// paths which are not accessible by this API.
+///
+/// The directory separator in WASI is always the forward-slash (`/`).
+///
+/// All paths in WASI are relative paths, and are interpreted relative to a
+/// `descriptor` referring to a base directory. If a `path` argument to any WASI
+/// function starts with `/`, or if any step of resolving a `path`, including
+/// `..` and symbolic link steps, reaches a directory outside of the base
+/// directory, or reaches a symlink to an absolute or rooted path in the
+/// underlying filesystem, the function fails with `error-code::not-permitted`.
+///
+/// For more information about WASI path resolution and sandboxing, see
+/// [WASI filesystem path resolution].
+///
+/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
+interface types {
+    use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream, error};
+    use wasi:clocks/wall-clock@0.2.0-rc-2023-11-10.{datetime};
+
+    /// File size or length of a region within a file.
+    type filesize = u64;
+
+    /// The type of a filesystem object referenced by a descriptor.
+    ///
+    /// Note: This was called `filetype` in earlier versions of WASI.
+    enum descriptor-type {
+        /// The type of the descriptor or file is unknown or is different from
+        /// any of the other types specified.
+        unknown,
+        /// The descriptor refers to a block device inode.
+        block-device,
+        /// The descriptor refers to a character device inode.
+        character-device,
+        /// The descriptor refers to a directory inode.
+        directory,
+        /// The descriptor refers to a named pipe.
+        fifo,
+        /// The file refers to a symbolic link inode.
+        symbolic-link,
+        /// The descriptor refers to a regular file inode.
+        regular-file,
+        /// The descriptor refers to a socket.
+        socket,
+    }
+
+    /// Descriptor flags.
+    ///
+    /// Note: This was called `fdflags` in earlier versions of WASI.
+    flags descriptor-flags {
+        /// Read mode: Data can be read.
+        read,
+        /// Write mode: Data can be written to.
+        write,
+        /// Request that writes be performed according to synchronized I/O file
+        /// integrity completion. The data stored in the file and the file's
+        /// metadata are synchronized. This is similar to `O_SYNC` in POSIX.
+        ///
+        /// The precise semantics of this operation have not yet been defined for
+        /// WASI. At this time, it should be interpreted as a request, and not a
+        /// requirement.
+        file-integrity-sync,
+        /// Request that writes be performed according to synchronized I/O data
+        /// integrity completion. Only the data stored in the file is
+        /// synchronized. This is similar to `O_DSYNC` in POSIX.
+        ///
+        /// The precise semantics of this operation have not yet been defined for
+        /// WASI. At this time, it should be interpreted as a request, and not a
+        /// requirement.
+        data-integrity-sync,
+        /// Requests that reads be performed at the same level of integrety
+        /// requested for writes. This is similar to `O_RSYNC` in POSIX.
+        ///
+        /// The precise semantics of this operation have not yet been defined for
+        /// WASI. At this time, it should be interpreted as a request, and not a
+        /// requirement.
+        requested-write-sync,
+        /// Mutating directories mode: Directory contents may be mutated.
+        ///
+        /// When this flag is unset on a descriptor, operations using the
+        /// descriptor which would create, rename, delete, modify the data or
+        /// metadata of filesystem objects, or obtain another handle which
+        /// would permit any of those, shall fail with `error-code::read-only` if
+        /// they would otherwise succeed.
+        ///
+        /// This may only be set on directories.
+        mutate-directory,
+    }
+
+    /// File attributes.
+    ///
+    /// Note: This was called `filestat` in earlier versions of WASI.
+    record descriptor-stat {
+        /// File type.
+        %type: descriptor-type,
+        /// Number of hard links to the file.
+        link-count: link-count,
+        /// For regular files, the file size in bytes. For symbolic links, the
+        /// length in bytes of the pathname contained in the symbolic link.
+        size: filesize,
+        /// Last data access timestamp.
+        ///
+        /// If the `option` is none, the platform doesn't maintain an access
+        /// timestamp for this file.
+        data-access-timestamp: option<datetime>,
+        /// Last data modification timestamp.
+        ///
+        /// If the `option` is none, the platform doesn't maintain a
+        /// modification timestamp for this file.
+        data-modification-timestamp: option<datetime>,
+        /// Last file status-change timestamp.
+        ///
+        /// If the `option` is none, the platform doesn't maintain a
+        /// status-change timestamp for this file.
+        status-change-timestamp: option<datetime>,
+    }
+
+    /// Flags determining the method of how paths are resolved.
+    flags path-flags {
+        /// As long as the resolved path corresponds to a symbolic link, it is
+        /// expanded.
+        symlink-follow,
+    }
+
+    /// Open flags used by `open-at`.
+    flags open-flags {
+        /// Create file if it does not exist, similar to `O_CREAT` in POSIX.
+        create,
+        /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
+        directory,
+        /// Fail if file already exists, similar to `O_EXCL` in POSIX.
+        exclusive,
+        /// Truncate file to size 0, similar to `O_TRUNC` in POSIX.
+        truncate,
+    }
+
+    /// Number of hard links to an inode.
+    type link-count = u64;
+
+    /// When setting a timestamp, this gives the value to set it to.
+    variant new-timestamp {
+        /// Leave the timestamp set to its previous value.
+        no-change,
+        /// Set the timestamp to the current time of the system clock associated
+        /// with the filesystem.
+        now,
+        /// Set the timestamp to the given value.
+        timestamp(datetime),
+    }
+
+    /// A directory entry.
+    record directory-entry {
+        /// The type of the file referred to by this directory entry.
+        %type: descriptor-type,
+
+        /// The name of the object.
+        name: string,
+    }
+
+    /// Error codes returned by functions, similar to `errno` in POSIX.
+    /// Not all of these error codes are returned by the functions provided by this
+    /// API; some are used in higher-level library layers, and others are provided
+    /// merely for alignment with POSIX.
+    enum error-code {
+        /// Permission denied, similar to `EACCES` in POSIX.
+        access,
+        /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX.
+        would-block,
+        /// Connection already in progress, similar to `EALREADY` in POSIX.
+        already,
+        /// Bad descriptor, similar to `EBADF` in POSIX.
+        bad-descriptor,
+        /// Device or resource busy, similar to `EBUSY` in POSIX.
+        busy,
+        /// Resource deadlock would occur, similar to `EDEADLK` in POSIX.
+        deadlock,
+        /// Storage quota exceeded, similar to `EDQUOT` in POSIX.
+        quota,
+        /// File exists, similar to `EEXIST` in POSIX.
+        exist,
+        /// File too large, similar to `EFBIG` in POSIX.
+        file-too-large,
+        /// Illegal byte sequence, similar to `EILSEQ` in POSIX.
+        illegal-byte-sequence,
+        /// Operation in progress, similar to `EINPROGRESS` in POSIX.
+        in-progress,
+        /// Interrupted function, similar to `EINTR` in POSIX.
+        interrupted,
+        /// Invalid argument, similar to `EINVAL` in POSIX.
+        invalid,
+        /// I/O error, similar to `EIO` in POSIX.
+        io,
+        /// Is a directory, similar to `EISDIR` in POSIX.
+        is-directory,
+        /// Too many levels of symbolic links, similar to `ELOOP` in POSIX.
+        loop,
+        /// Too many links, similar to `EMLINK` in POSIX.
+        too-many-links,
+        /// Message too large, similar to `EMSGSIZE` in POSIX.
+        message-size,
+        /// Filename too long, similar to `ENAMETOOLONG` in POSIX.
+        name-too-long,
+        /// No such device, similar to `ENODEV` in POSIX.
+        no-device,
+        /// No such file or directory, similar to `ENOENT` in POSIX.
+        no-entry,
+        /// No locks available, similar to `ENOLCK` in POSIX.
+        no-lock,
+        /// Not enough space, similar to `ENOMEM` in POSIX.
+        insufficient-memory,
+        /// No space left on device, similar to `ENOSPC` in POSIX.
+        insufficient-space,
+        /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
+        not-directory,
+        /// Directory not empty, similar to `ENOTEMPTY` in POSIX.
+        not-empty,
+        /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
+        not-recoverable,
+        /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
+        unsupported,
+        /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
+        no-tty,
+        /// No such device or address, similar to `ENXIO` in POSIX.
+        no-such-device,
+        /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
+        overflow,
+        /// Operation not permitted, similar to `EPERM` in POSIX.
+        not-permitted,
+        /// Broken pipe, similar to `EPIPE` in POSIX.
+        pipe,
+        /// Read-only file system, similar to `EROFS` in POSIX.
+        read-only,
+        /// Invalid seek, similar to `ESPIPE` in POSIX.
+        invalid-seek,
+        /// Text file busy, similar to `ETXTBSY` in POSIX.
+        text-file-busy,
+        /// Cross-device link, similar to `EXDEV` in POSIX.
+        cross-device,
+    }
+
+    /// File or memory access pattern advisory information.
+    enum advice {
+        /// The application has no advice to give on its behavior with respect
+        /// to the specified data.
+        normal,
+        /// The application expects to access the specified data sequentially
+        /// from lower offsets to higher offsets.
+        sequential,
+        /// The application expects to access the specified data in a random
+        /// order.
+        random,
+        /// The application expects to access the specified data in the near
+        /// future.
+        will-need,
+        /// The application expects that it will not access the specified data
+        /// in the near future.
+        dont-need,
+        /// The application expects to access the specified data once and then
+        /// not reuse it thereafter.
+        no-reuse,
+    }
+
+    /// A 128-bit hash value, split into parts because wasm doesn't have a
+    /// 128-bit integer type.
+    record metadata-hash-value {
+       /// 64 bits of a 128-bit hash value.
+       lower: u64,
+       /// Another 64 bits of a 128-bit hash value.
+       upper: u64,
+    }
+
+    /// A descriptor is a reference to a filesystem object, which may be a file,
+    /// directory, named pipe, special file, or other object on which filesystem
+    /// calls may be made.
+    resource descriptor {
+        /// Return a stream for reading from a file, if available.
+        ///
+        /// May fail with an error-code describing why the file cannot be read.
+        ///
+        /// Multiple read, write, and append streams may be active on the same open
+        /// file and they do not interfere with each other.
+        ///
+        /// Note: This allows using `read-stream`, which is similar to `read` in POSIX.
+        read-via-stream: func(
+            /// The offset within the file at which to start reading.
+            offset: filesize,
+        ) -> result<input-stream, error-code>;
+
+        /// Return a stream for writing to a file, if available.
+        ///
+        /// May fail with an error-code describing why the file cannot be written.
+        ///
+        /// Note: This allows using `write-stream`, which is similar to `write` in
+        /// POSIX.
+        write-via-stream: func(
+            /// The offset within the file at which to start writing.
+            offset: filesize,
+        ) -> result<output-stream, error-code>;
+
+        /// Return a stream for appending to a file, if available.
+        ///
+        /// May fail with an error-code describing why the file cannot be appended.
+        ///
+        /// Note: This allows using `write-stream`, which is similar to `write` with
+        /// `O_APPEND` in in POSIX.
+        append-via-stream: func() -> result<output-stream, error-code>;
+
+        /// Provide file advisory information on a descriptor.
+        ///
+        /// This is similar to `posix_fadvise` in POSIX.
+        advise: func(
+            /// The offset within the file to which the advisory applies.
+            offset: filesize,
+            /// The length of the region to which the advisory applies.
+            length: filesize,
+            /// The advice.
+            advice: advice
+        ) -> result<_, error-code>;
+
+        /// Synchronize the data of a file to disk.
+        ///
+        /// This function succeeds with no effect if the file descriptor is not
+        /// opened for writing.
+        ///
+        /// Note: This is similar to `fdatasync` in POSIX.
+        sync-data: func() -> result<_, error-code>;
+
+        /// Get flags associated with a descriptor.
+        ///
+        /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
+        ///
+        /// Note: This returns the value that was the `fs_flags` value returned
+        /// from `fdstat_get` in earlier versions of WASI.
+        get-flags: func() -> result<descriptor-flags, error-code>;
+
+        /// Get the dynamic type of a descriptor.
+        ///
+        /// Note: This returns the same value as the `type` field of the `fd-stat`
+        /// returned by `stat`, `stat-at` and similar.
+        ///
+        /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
+        /// by `fstat` in POSIX.
+        ///
+        /// Note: This returns the value that was the `fs_filetype` value returned
+        /// from `fdstat_get` in earlier versions of WASI.
+        get-type: func() -> result<descriptor-type, error-code>;
+
+        /// Adjust the size of an open file. If this increases the file's size, the
+        /// extra bytes are filled with zeros.
+        ///
+        /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
+        set-size: func(size: filesize) -> result<_, error-code>;
+
+        /// Adjust the timestamps of an open file or directory.
+        ///
+        /// Note: This is similar to `futimens` in POSIX.
+        ///
+        /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
+        set-times: func(
+            /// The desired values of the data access timestamp.
+            data-access-timestamp: new-timestamp,
+            /// The desired values of the data modification timestamp.
+            data-modification-timestamp: new-timestamp,
+        ) -> result<_, error-code>;
+
+        /// Read from a descriptor, without using and updating the descriptor's offset.
+        ///
+        /// This function returns a list of bytes containing the data that was
+        /// read, along with a bool which, when true, indicates that the end of the
+        /// file was reached. The returned list will contain up to `length` bytes; it
+        /// may return fewer than requested, if the end of the file is reached or
+        /// if the I/O operation is interrupted.
+        ///
+        /// In the future, this may change to return a `stream<u8, error-code>`.
+        ///
+        /// Note: This is similar to `pread` in POSIX.
+        read: func(
+            /// The maximum number of bytes to read.
+            length: filesize,
+            /// The offset within the file at which to read.
+            offset: filesize,
+        ) -> result<tuple<list<u8>, bool>, error-code>;
+
+        /// Write to a descriptor, without using and updating the descriptor's offset.
+        ///
+        /// It is valid to write past the end of a file; the file is extended to the
+        /// extent of the write, with bytes between the previous end and the start of
+        /// the write set to zero.
+        ///
+        /// In the future, this may change to take a `stream<u8, error-code>`.
+        ///
+        /// Note: This is similar to `pwrite` in POSIX.
+        write: func(
+            /// Data to write
+            buffer: list<u8>,
+            /// The offset within the file at which to write.
+            offset: filesize,
+        ) -> result<filesize, error-code>;
+
+        /// Read directory entries from a directory.
+        ///
+        /// On filesystems where directories contain entries referring to themselves
+        /// and their parents, often named `.` and `..` respectively, these entries
+        /// are omitted.
+        ///
+        /// This always returns a new stream which starts at the beginning of the
+        /// directory. Multiple streams may be active on the same directory, and they
+        /// do not interfere with each other.
+        read-directory: func() -> result<directory-entry-stream, error-code>;
+
+        /// Synchronize the data and metadata of a file to disk.
+        ///
+        /// This function succeeds with no effect if the file descriptor is not
+        /// opened for writing.
+        ///
+        /// Note: This is similar to `fsync` in POSIX.
+        sync: func() -> result<_, error-code>;
+
+        /// Create a directory.
+        ///
+        /// Note: This is similar to `mkdirat` in POSIX.
+        create-directory-at: func(
+            /// The relative path at which to create the directory.
+            path: string,
+        ) -> result<_, error-code>;
+
+        /// Return the attributes of an open file or directory.
+        ///
+        /// Note: This is similar to `fstat` in POSIX, except that it does not return
+        /// device and inode information. For testing whether two descriptors refer to
+        /// the same underlying filesystem object, use `is-same-object`. To obtain
+        /// additional data that can be used do determine whether a file has been
+        /// modified, use `metadata-hash`.
+        ///
+        /// Note: This was called `fd_filestat_get` in earlier versions of WASI.
+        stat: func() -> result<descriptor-stat, error-code>;
+
+        /// Return the attributes of a file or directory.
+        ///
+        /// Note: This is similar to `fstatat` in POSIX, except that it does not
+        /// return device and inode information. See the `stat` description for a
+        /// discussion of alternatives.
+        ///
+        /// Note: This was called `path_filestat_get` in earlier versions of WASI.
+        stat-at: func(
+            /// Flags determining the method of how the path is resolved.
+            path-flags: path-flags,
+            /// The relative path of the file or directory to inspect.
+            path: string,
+        ) -> result<descriptor-stat, error-code>;
+
+        /// Adjust the timestamps of a file or directory.
+        ///
+        /// Note: This is similar to `utimensat` in POSIX.
+        ///
+        /// Note: This was called `path_filestat_set_times` in earlier versions of
+        /// WASI.
+        set-times-at: func(
+            /// Flags determining the method of how the path is resolved.
+            path-flags: path-flags,
+            /// The relative path of the file or directory to operate on.
+            path: string,
+            /// The desired values of the data access timestamp.
+            data-access-timestamp: new-timestamp,
+            /// The desired values of the data modification timestamp.
+            data-modification-timestamp: new-timestamp,
+        ) -> result<_, error-code>;
+
+        /// Create a hard link.
+        ///
+        /// Note: This is similar to `linkat` in POSIX.
+        link-at: func(
+            /// Flags determining the method of how the path is resolved.
+            old-path-flags: path-flags,
+            /// The relative source path from which to link.
+            old-path: string,
+            /// The base directory for `new-path`.
+            new-descriptor: borrow<descriptor>,
+            /// The relative destination path at which to create the hard link.
+            new-path: string,
+        ) -> result<_, error-code>;
+
+        /// Open a file or directory.
+        ///
+        /// The returned descriptor is not guaranteed to be the lowest-numbered
+        /// descriptor not currently open/ it is randomized to prevent applications
+        /// from depending on making assumptions about indexes, since this is
+        /// error-prone in multi-threaded contexts. The returned descriptor is
+        /// guaranteed to be less than 2**31.
+        ///
+        /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
+        /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
+        /// `open-at` fails with `error-code::read-only`.
+        ///
+        /// If `flags` contains `write` or `mutate-directory`, or `open-flags`
+        /// contains `truncate` or `create`, and the base descriptor doesn't have
+        /// `descriptor-flags::mutate-directory` set, `open-at` fails with
+        /// `error-code::read-only`.
+        ///
+        /// Note: This is similar to `openat` in POSIX.
+        open-at: func(
+            /// Flags determining the method of how the path is resolved.
+            path-flags: path-flags,
+            /// The relative path of the object to open.
+            path: string,
+            /// The method by which to open the file.
+            open-flags: open-flags,
+            /// Flags to use for the resulting descriptor.
+            %flags: descriptor-flags,
+        ) -> result<descriptor, error-code>;
+
+        /// Read the contents of a symbolic link.
+        ///
+        /// If the contents contain an absolute or rooted path in the underlying
+        /// filesystem, this function fails with `error-code::not-permitted`.
+        ///
+        /// Note: This is similar to `readlinkat` in POSIX.
+        readlink-at: func(
+            /// The relative path of the symbolic link from which to read.
+            path: string,
+        ) -> result<string, error-code>;
+
+        /// Remove a directory.
+        ///
+        /// Return `error-code::not-empty` if the directory is not empty.
+        ///
+        /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+        remove-directory-at: func(
+            /// The relative path to a directory to remove.
+            path: string,
+        ) -> result<_, error-code>;
+
+        /// Rename a filesystem object.
+        ///
+        /// Note: This is similar to `renameat` in POSIX.
+        rename-at: func(
+            /// The relative source path of the file or directory to rename.
+            old-path: string,
+            /// The base directory for `new-path`.
+            new-descriptor: borrow<descriptor>,
+            /// The relative destination path to which to rename the file or directory.
+            new-path: string,
+        ) -> result<_, error-code>;
+
+        /// Create a symbolic link (also known as a "symlink").
+        ///
+        /// If `old-path` starts with `/`, the function fails with
+        /// `error-code::not-permitted`.
+        ///
+        /// Note: This is similar to `symlinkat` in POSIX.
+        symlink-at: func(
+            /// The contents of the symbolic link.
+            old-path: string,
+            /// The relative destination path at which to create the symbolic link.
+            new-path: string,
+        ) -> result<_, error-code>;
+
+        /// Unlink a filesystem object that is not a directory.
+        ///
+        /// Return `error-code::is-directory` if the path refers to a directory.
+        /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+        unlink-file-at: func(
+            /// The relative path to a file to unlink.
+            path: string,
+        ) -> result<_, error-code>;
+
+        /// Test whether two descriptors refer to the same filesystem object.
+        ///
+        /// In POSIX, this corresponds to testing whether the two descriptors have the
+        /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
+        /// wasi-filesystem does not expose device and inode numbers, so this function
+        /// may be used instead.
+        is-same-object: func(other: borrow<descriptor>) -> bool;
+
+        /// Return a hash of the metadata associated with a filesystem object referred
+        /// to by a descriptor.
+        ///
+        /// This returns a hash of the last-modification timestamp and file size, and
+        /// may also include the inode number, device number, birth timestamp, and
+        /// other metadata fields that may change when the file is modified or
+        /// replaced. It may also include a secret value chosen by the
+        /// implementation and not otherwise exposed.
+        ///
+        /// Implementations are encourated to provide the following properties:
+        ///
+        ///  - If the file is not modified or replaced, the computed hash value should
+        ///    usually not change.
+        ///  - If the object is modified or replaced, the computed hash value should
+        ///    usually change.
+        ///  - The inputs to the hash should not be easily computable from the
+        ///    computed hash.
+        ///
+        /// However, none of these is required.
+        metadata-hash: func() -> result<metadata-hash-value, error-code>;
+
+        /// Return a hash of the metadata associated with a filesystem object referred
+        /// to by a directory descriptor and a relative path.
+        ///
+        /// This performs the same hash computation as `metadata-hash`.
+        metadata-hash-at: func(
+            /// Flags determining the method of how the path is resolved.
+            path-flags: path-flags,
+            /// The relative path of the file or directory to inspect.
+            path: string,
+        ) -> result<metadata-hash-value, error-code>;
+    }
+
+    /// A stream of directory entries.
+    resource directory-entry-stream {
+        /// Read a single directory entry from a `directory-entry-stream`.
+        read-directory-entry: func() -> result<option<directory-entry>, error-code>;
+    }
+
+    /// Attempts to extract a filesystem-related `error-code` from the stream
+    /// `error` provided.
+    ///
+    /// Stream operations which return `stream-error::last-operation-failed`
+    /// have a payload with more information about the operation that failed.
+    /// This payload can be passed through to this function to see if there's
+    /// filesystem-related information about the error to return.
+    ///
+    /// Note that this function is fallible because not all stream-related
+    /// errors are filesystem-related errors.
+    filesystem-error-code: func(err: borrow<error>) -> option<error-code>;
+}
diff --git a/tests/autosar/deps/filesystem/world.wit b/tests/autosar/deps/filesystem/world.wit
new file mode 100644
index 000000000..285e0bae9
--- /dev/null
+++ b/tests/autosar/deps/filesystem/world.wit
@@ -0,0 +1,6 @@
+package wasi:filesystem@0.2.0-rc-2023-11-10;
+
+world imports {
+    import types;
+    import preopens;
+}
diff --git a/tests/autosar/deps/io/error.wit b/tests/autosar/deps/io/error.wit
new file mode 100644
index 000000000..31918acbb
--- /dev/null
+++ b/tests/autosar/deps/io/error.wit
@@ -0,0 +1,34 @@
+package wasi:io@0.2.0-rc-2023-11-10;
+
+
+interface error {
+    /// A resource which represents some error information.
+    ///
+    /// The only method provided by this resource is `to-debug-string`,
+    /// which provides some human-readable information about the error.
+    ///
+    /// In the `wasi:io` package, this resource is returned through the
+    /// `wasi:io/streams/stream-error` type.
+    ///
+    /// To provide more specific error information, other interfaces may
+    /// provide functions to further "downcast" this error into more specific
+    /// error information. For example, `error`s returned in streams derived
+    /// from filesystem types to be described using the filesystem's own
+    /// error-code type, using the function
+    /// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter
+    /// `borrow<error>` and returns
+    /// `option<wasi:filesystem/types/error-code>`.
+    ///
+    /// The set of functions which can "downcast" an `error` into a more
+    /// concrete type is open.
+    resource error {
+        /// Returns a string that is suitable to assist humans in debugging
+        /// this error.
+        ///
+        /// WARNING: The returned string should not be consumed mechanically!
+        /// It may change across platforms, hosts, or other implementation
+        /// details. Parsing this string is a major platform-compatibility
+        /// hazard.
+        to-debug-string: func() -> string;
+    }
+}
diff --git a/tests/autosar/deps/io/poll.wit b/tests/autosar/deps/io/poll.wit
new file mode 100644
index 000000000..81b1cab99
--- /dev/null
+++ b/tests/autosar/deps/io/poll.wit
@@ -0,0 +1,41 @@
+package wasi:io@0.2.0-rc-2023-11-10;
+
+/// A poll API intended to let users wait for I/O events on multiple handles
+/// at once.
+interface poll {
+    /// `pollable` represents a single I/O event which may be ready, or not.
+    resource pollable {
+
+      /// Return the readiness of a pollable. This function never blocks.
+      ///
+      /// Returns `true` when the pollable is ready, and `false` otherwise.
+      ready: func() -> bool;
+
+      /// `block` returns immediately if the pollable is ready, and otherwise
+      /// blocks until ready.
+      ///
+      /// This function is equivalent to calling `poll.poll` on a list
+      /// containing only this pollable.
+      block: func();
+    }
+
+    /// Poll for completion on a set of pollables.
+    ///
+    /// This function takes a list of pollables, which identify I/O sources of
+    /// interest, and waits until one or more of the events is ready for I/O.
+    ///
+    /// The result `list<u32>` contains one or more indices of handles in the
+    /// argument list that is ready for I/O.
+    ///
+    /// If the list contains more elements than can be indexed with a `u32`
+    /// value, this function traps.
+    ///
+    /// A timeout can be implemented by adding a pollable from the
+    /// wasi-clocks API to the list.
+    ///
+    /// This function does not return a `result`; polling in itself does not
+    /// do any I/O so it doesn't fail. If any of the I/O sources identified by
+    /// the pollables has an error, it is indicated by marking the source as
+    /// being reaedy for I/O.
+    poll: func(in: list<borrow<pollable>>) -> list<u32>;
+}
diff --git a/tests/autosar/deps/io/streams.wit b/tests/autosar/deps/io/streams.wit
new file mode 100644
index 000000000..f6f7fe0e8
--- /dev/null
+++ b/tests/autosar/deps/io/streams.wit
@@ -0,0 +1,251 @@
+package wasi:io@0.2.0-rc-2023-11-10;
+
+/// WASI I/O is an I/O abstraction API which is currently focused on providing
+/// stream types.
+///
+/// In the future, the component model is expected to add built-in stream types;
+/// when it does, they are expected to subsume this API.
+interface streams {
+    use error.{error};
+    use poll.{pollable};
+
+    /// An error for input-stream and output-stream operations.
+    variant stream-error {
+        /// The last operation (a write or flush) failed before completion.
+        ///
+        /// More information is available in the `error` payload.
+        last-operation-failed(error),
+        /// The stream is closed: no more input will be accepted by the
+        /// stream. A closed output-stream will return this error on all
+        /// future operations.
+        closed
+    }
+
+    /// An input bytestream.
+    ///
+    /// `input-stream`s are *non-blocking* to the extent practical on underlying
+    /// platforms. I/O operations always return promptly; if fewer bytes are
+    /// promptly available than requested, they return the number of bytes promptly
+    /// available, which could even be zero. To wait for data to be available,
+    /// use the `subscribe` function to obtain a `pollable` which can be polled
+    /// for using `wasi:io/poll`.
+    resource input-stream {
+        /// Perform a non-blocking read from the stream.
+        ///
+        /// This function returns a list of bytes containing the read data,
+        /// when successful. The returned list will contain up to `len` bytes;
+        /// it may return fewer than requested, but not more. The list is
+        /// empty when no bytes are available for reading at this time. The
+        /// pollable given by `subscribe` will be ready when more bytes are
+        /// available.
+        ///
+        /// This function fails with a `stream-error` when the operation
+        /// encounters an error, giving `last-operation-failed`, or when the
+        /// stream is closed, giving `closed`.
+        ///
+        /// When the caller gives a `len` of 0, it represents a request to
+        /// read 0 bytes. If the stream is still open, this call should
+        /// succeed and return an empty list, or otherwise fail with `closed`.
+        ///
+        /// The `len` parameter is a `u64`, which could represent a list of u8 which
+        /// is not possible to allocate in wasm32, or not desirable to allocate as
+        /// as a return value by the callee. The callee may return a list of bytes
+        /// less than `len` in size while more bytes are available for reading.
+        read: func(
+            /// The maximum number of bytes to read
+            len: u64
+        ) -> result<list<u8>, stream-error>;
+
+        /// Read bytes from a stream, after blocking until at least one byte can
+        /// be read. Except for blocking, behavior is identical to `read`.
+        blocking-read: func(
+            /// The maximum number of bytes to read
+            len: u64
+        ) -> result<list<u8>, stream-error>;
+
+        /// Skip bytes from a stream. Returns number of bytes skipped.
+        ///
+        /// Behaves identical to `read`, except instead of returning a list
+        /// of bytes, returns the number of bytes consumed from the stream.
+        skip: func(
+            /// The maximum number of bytes to skip.
+            len: u64,
+        ) -> result<u64, stream-error>;
+
+        /// Skip bytes from a stream, after blocking until at least one byte
+        /// can be skipped. Except for blocking behavior, identical to `skip`.
+        blocking-skip: func(
+            /// The maximum number of bytes to skip.
+            len: u64,
+        ) -> result<u64, stream-error>;
+
+        /// Create a `pollable` which will resolve once either the specified stream
+        /// has bytes available to read or the other end of the stream has been
+        /// closed.
+        /// The created `pollable` is a child resource of the `input-stream`.
+        /// Implementations may trap if the `input-stream` is dropped before
+        /// all derived `pollable`s created with this function are dropped.
+        subscribe: func() -> pollable;
+    }
+
+
+    /// An output bytestream.
+    ///
+    /// `output-stream`s are *non-blocking* to the extent practical on
+    /// underlying platforms. Except where specified otherwise, I/O operations also
+    /// always return promptly, after the number of bytes that can be written
+    /// promptly, which could even be zero. To wait for the stream to be ready to
+    /// accept data, the `subscribe` function to obtain a `pollable` which can be
+    /// polled for using `wasi:io/poll`.
+    resource output-stream {
+        /// Check readiness for writing. This function never blocks.
+        ///
+        /// Returns the number of bytes permitted for the next call to `write`,
+        /// or an error. Calling `write` with more bytes than this function has
+        /// permitted will trap.
+        ///
+        /// When this function returns 0 bytes, the `subscribe` pollable will
+        /// become ready when this function will report at least 1 byte, or an
+        /// error.
+        check-write: func() -> result<u64, stream-error>;
+
+        /// Perform a write. This function never blocks.
+        ///
+        /// Precondition: check-write gave permit of Ok(n) and contents has a
+        /// length of less than or equal to n. Otherwise, this function will trap.
+        ///
+        /// returns Err(closed) without writing if the stream has closed since
+        /// the last call to check-write provided a permit.
+        write: func(
+            contents: list<u8>
+        ) -> result<_, stream-error>;
+
+        /// Perform a write of up to 4096 bytes, and then flush the stream. Block
+        /// until all of these operations are complete, or an error occurs.
+        ///
+        /// This is a convenience wrapper around the use of `check-write`,
+        /// `subscribe`, `write`, and `flush`, and is implemented with the
+        /// following pseudo-code:
+        ///
+        /// ```text
+        /// let pollable = this.subscribe();
+        /// while !contents.is_empty() {
+        ///     // Wait for the stream to become writable
+        ///     pollable.block();
+        ///     let Ok(n) = this.check-write(); // eliding error handling
+        ///     let len = min(n, contents.len());
+        ///     let (chunk, rest) = contents.split_at(len);
+        ///     this.write(chunk  );            // eliding error handling
+        ///     contents = rest;
+        /// }
+        /// this.flush();
+        /// // Wait for completion of `flush`
+        /// pollable.block();
+        /// // Check for any errors that arose during `flush`
+        /// let _ = this.check-write();         // eliding error handling
+        /// ```
+        blocking-write-and-flush: func(
+            contents: list<u8>
+        ) -> result<_, stream-error>;
+
+        /// Request to flush buffered output. This function never blocks.
+        ///
+        /// This tells the output-stream that the caller intends any buffered
+        /// output to be flushed. the output which is expected to be flushed
+        /// is all that has been passed to `write` prior to this call.
+        ///
+        /// Upon calling this function, the `output-stream` will not accept any
+        /// writes (`check-write` will return `ok(0)`) until the flush has
+        /// completed. The `subscribe` pollable will become ready when the
+        /// flush has completed and the stream can accept more writes.
+        flush: func() -> result<_, stream-error>;
+
+        /// Request to flush buffered output, and block until flush completes
+        /// and stream is ready for writing again.
+        blocking-flush: func() -> result<_, stream-error>;
+
+        /// Create a `pollable` which will resolve once the output-stream
+        /// is ready for more writing, or an error has occured. When this
+        /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an
+        /// error.
+        ///
+        /// If the stream is closed, this pollable is always ready immediately.
+        ///
+        /// The created `pollable` is a child resource of the `output-stream`.
+        /// Implementations may trap if the `output-stream` is dropped before
+        /// all derived `pollable`s created with this function are dropped.
+        subscribe: func() -> pollable;
+
+        /// Write zeroes to a stream.
+        ///
+        /// This should be used precisely like `write` with the exact same
+        /// preconditions (must use check-write first), but instead of
+        /// passing a list of bytes, you simply pass the number of zero-bytes
+        /// that should be written.
+        write-zeroes: func(
+            /// The number of zero-bytes to write
+            len: u64
+        ) -> result<_, stream-error>;
+
+        /// Perform a write of up to 4096 zeroes, and then flush the stream.
+        /// Block until all of these operations are complete, or an error
+        /// occurs.
+        ///
+        /// This is a convenience wrapper around the use of `check-write`,
+        /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with
+        /// the following pseudo-code:
+        ///
+        /// ```text
+        /// let pollable = this.subscribe();
+        /// while num_zeroes != 0 {
+        ///     // Wait for the stream to become writable
+        ///     pollable.block();
+        ///     let Ok(n) = this.check-write(); // eliding error handling
+        ///     let len = min(n, num_zeroes);
+        ///     this.write-zeroes(len);         // eliding error handling
+        ///     num_zeroes -= len;
+        /// }
+        /// this.flush();
+        /// // Wait for completion of `flush`
+        /// pollable.block();
+        /// // Check for any errors that arose during `flush`
+        /// let _ = this.check-write();         // eliding error handling
+        /// ```
+        blocking-write-zeroes-and-flush: func(
+            /// The number of zero-bytes to write
+            len: u64
+        ) -> result<_, stream-error>;
+
+        /// Read from one stream and write to another.
+        ///
+        /// The behavior of splice is equivelant to:
+        /// 1. calling `check-write` on the `output-stream`
+        /// 2. calling `read` on the `input-stream` with the smaller of the
+        /// `check-write` permitted length and the `len` provided to `splice`
+        /// 3. calling `write` on the `output-stream` with that read data.
+        ///
+        /// Any error reported by the call to `check-write`, `read`, or
+        /// `write` ends the splice and reports that error.
+        ///
+        /// This function returns the number of bytes transferred; it may be less
+        /// than `len`.
+        splice: func(
+            /// The stream to read from
+            src: borrow<input-stream>,
+            /// The number of bytes to splice
+            len: u64,
+        ) -> result<u64, stream-error>;
+
+        /// Read from one stream and write to another, with blocking.
+        ///
+        /// This is similar to `splice`, except that it blocks until the
+        /// `output-stream` is ready for writing, and the `input-stream`
+        /// is ready for reading, before performing the `splice`.
+        blocking-splice: func(
+            /// The stream to read from
+            src: borrow<input-stream>,
+            /// The number of bytes to splice
+            len: u64,
+        ) -> result<u64, stream-error>;
+    }
+}
diff --git a/tests/autosar/deps/io/world.wit b/tests/autosar/deps/io/world.wit
new file mode 100644
index 000000000..8243da2ee
--- /dev/null
+++ b/tests/autosar/deps/io/world.wit
@@ -0,0 +1,6 @@
+package wasi:io@0.2.0-rc-2023-11-10;
+
+world imports {
+    import streams;
+    import poll;
+}
diff --git a/tests/autosar/deps/random/insecure-seed.wit b/tests/autosar/deps/random/insecure-seed.wit
new file mode 100644
index 000000000..f76e87dad
--- /dev/null
+++ b/tests/autosar/deps/random/insecure-seed.wit
@@ -0,0 +1,25 @@
+package wasi:random@0.2.0-rc-2023-11-10;
+/// The insecure-seed interface for seeding hash-map DoS resistance.
+///
+/// It is intended to be portable at least between Unix-family platforms and
+/// Windows.
+interface insecure-seed {
+    /// Return a 128-bit value that may contain a pseudo-random value.
+    ///
+    /// The returned value is not required to be computed from a CSPRNG, and may
+    /// even be entirely deterministic. Host implementations are encouraged to
+    /// provide pseudo-random values to any program exposed to
+    /// attacker-controlled content, to enable DoS protection built into many
+    /// languages' hash-map implementations.
+    ///
+    /// This function is intended to only be called once, by a source language
+    /// to initialize Denial Of Service (DoS) protection in its hash-map
+    /// implementation.
+    ///
+    /// # Expected future evolution
+    ///
+    /// This will likely be changed to a value import, to prevent it from being
+    /// called multiple times and potentially used for purposes other than DoS
+    /// protection.
+    insecure-seed: func() -> tuple<u64, u64>;
+}
diff --git a/tests/autosar/deps/random/insecure.wit b/tests/autosar/deps/random/insecure.wit
new file mode 100644
index 000000000..ec7b99737
--- /dev/null
+++ b/tests/autosar/deps/random/insecure.wit
@@ -0,0 +1,22 @@
+package wasi:random@0.2.0-rc-2023-11-10;
+/// The insecure interface for insecure pseudo-random numbers.
+///
+/// It is intended to be portable at least between Unix-family platforms and
+/// Windows.
+interface insecure {
+    /// Return `len` insecure pseudo-random bytes.
+    ///
+    /// This function is not cryptographically secure. Do not use it for
+    /// anything related to security.
+    ///
+    /// There are no requirements on the values of the returned bytes, however
+    /// implementations are encouraged to return evenly distributed values with
+    /// a long period.
+    get-insecure-random-bytes: func(len: u64) -> list<u8>;
+
+    /// Return an insecure pseudo-random `u64` value.
+    ///
+    /// This function returns the same type of pseudo-random data as
+    /// `get-insecure-random-bytes`, represented as a `u64`.
+    get-insecure-random-u64: func() -> u64;
+}
diff --git a/tests/autosar/deps/random/random.wit b/tests/autosar/deps/random/random.wit
new file mode 100644
index 000000000..7a7dfa27a
--- /dev/null
+++ b/tests/autosar/deps/random/random.wit
@@ -0,0 +1,26 @@
+package wasi:random@0.2.0-rc-2023-11-10;
+/// WASI Random is a random data API.
+///
+/// It is intended to be portable at least between Unix-family platforms and
+/// Windows.
+interface random {
+    /// Return `len` cryptographically-secure random or pseudo-random bytes.
+    ///
+    /// This function must produce data at least as cryptographically secure and
+    /// fast as an adequately seeded cryptographically-secure pseudo-random
+    /// number generator (CSPRNG). It must not block, from the perspective of
+    /// the calling program, under any circumstances, including on the first
+    /// request and on requests for numbers of bytes. The returned data must
+    /// always be unpredictable.
+    ///
+    /// This function must always return fresh data. Deterministic environments
+    /// must omit this function, rather than implementing it with deterministic
+    /// data.
+    get-random-bytes: func(len: u64) -> list<u8>;
+
+    /// Return a cryptographically-secure random or pseudo-random `u64` value.
+    ///
+    /// This function returns the same type of data as `get-random-bytes`,
+    /// represented as a `u64`.
+    get-random-u64: func() -> u64;
+}
diff --git a/tests/autosar/deps/random/world.wit b/tests/autosar/deps/random/world.wit
new file mode 100644
index 000000000..49e5743b4
--- /dev/null
+++ b/tests/autosar/deps/random/world.wit
@@ -0,0 +1,7 @@
+package wasi:random@0.2.0-rc-2023-11-10;
+
+world imports {
+    import random;
+    import insecure;
+    import insecure-seed;
+}
diff --git a/tests/autosar/deps/sockets/instance-network.wit b/tests/autosar/deps/sockets/instance-network.wit
new file mode 100644
index 000000000..e455d0ff7
--- /dev/null
+++ b/tests/autosar/deps/sockets/instance-network.wit
@@ -0,0 +1,9 @@
+
+/// This interface provides a value-export of the default network handle..
+interface instance-network {
+    use network.{network};
+
+    /// Get a handle to the default network.
+    instance-network: func() -> network;
+
+}
diff --git a/tests/autosar/deps/sockets/ip-name-lookup.wit b/tests/autosar/deps/sockets/ip-name-lookup.wit
new file mode 100644
index 000000000..931ccf7e0
--- /dev/null
+++ b/tests/autosar/deps/sockets/ip-name-lookup.wit
@@ -0,0 +1,51 @@
+
+interface ip-name-lookup {
+    use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable};
+    use network.{network, error-code, ip-address};
+
+
+    /// Resolve an internet host name to a list of IP addresses.
+    ///
+    /// Unicode domain names are automatically converted to ASCII using IDNA encoding.
+    /// If the input is an IP address string, the address is parsed and returned
+    /// as-is without making any external requests.
+    ///
+    /// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
+    ///
+    /// This function never blocks. It either immediately fails or immediately
+    /// returns successfully with a `resolve-address-stream` that can be used
+    /// to (asynchronously) fetch the results.
+    ///
+    /// # Typical errors
+    /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address.
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
+    /// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
+    resolve-addresses: func(network: borrow<network>, name: string) -> result<resolve-address-stream, error-code>;
+
+    resource resolve-address-stream {
+        /// Returns the next address from the resolver.
+        ///
+        /// This function should be called multiple times. On each call, it will
+        /// return the next address in connection order preference. If all
+        /// addresses have been exhausted, this function returns `none`.
+        ///
+        /// This function never returns IPv4-mapped IPv6 addresses.
+        ///
+        /// # Typical errors
+        /// - `name-unresolvable`:          Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
+        /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
+        /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
+        /// - `would-block`:                A result is not available yet. (EWOULDBLOCK, EAGAIN)
+        resolve-next-address: func() -> result<option<ip-address>, error-code>;
+
+        /// Create a `pollable` which will resolve once the stream is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
+}
diff --git a/tests/autosar/deps/sockets/network.wit b/tests/autosar/deps/sockets/network.wit
new file mode 100644
index 000000000..6bb07cd6f
--- /dev/null
+++ b/tests/autosar/deps/sockets/network.wit
@@ -0,0 +1,147 @@
+
+interface network {
+    /// An opaque resource that represents access to (a subset of) the network.
+    /// This enables context-based security for networking.
+    /// There is no need for this to map 1:1 to a physical network interface.
+    resource network;
+
+    /// Error codes.
+    ///
+    /// In theory, every API can return any error code.
+    /// In practice, API's typically only return the errors documented per API
+    /// combined with a couple of errors that are always possible:
+    /// - `unknown`
+    /// - `access-denied`
+    /// - `not-supported`
+    /// - `out-of-memory`
+    /// - `concurrency-conflict`
+    ///
+    /// See each individual API for what the POSIX equivalents are. They sometimes differ per API.
+    enum error-code {
+        // ### GENERAL ERRORS ###
+
+        /// Unknown error
+        unknown,
+
+        /// Access denied.
+        ///
+        /// POSIX equivalent: EACCES, EPERM
+        access-denied,
+
+        /// The operation is not supported.
+        ///
+        /// POSIX equivalent: EOPNOTSUPP
+        not-supported,
+
+        /// One of the arguments is invalid.
+        ///
+        /// POSIX equivalent: EINVAL
+        invalid-argument,
+
+        /// Not enough memory to complete the operation.
+        ///
+        /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
+        out-of-memory,
+
+        /// The operation timed out before it could finish completely.
+        timeout,
+
+        /// This operation is incompatible with another asynchronous operation that is already in progress.
+        ///
+        /// POSIX equivalent: EALREADY
+        concurrency-conflict,
+
+        /// Trying to finish an asynchronous operation that:
+        /// - has not been started yet, or:
+        /// - was already finished by a previous `finish-*` call.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        not-in-progress,
+
+        /// The operation has been aborted because it could not be completed immediately.
+        ///
+        /// Note: this is scheduled to be removed when `future`s are natively supported.
+        would-block,
+
+
+
+        // ### TCP & UDP SOCKET ERRORS ###
+
+        /// The operation is not valid in the socket's current state.
+        invalid-state,
+
+        /// A new socket resource could not be created because of a system limit.
+        new-socket-limit,
+
+        /// A bind operation failed because the provided address is not an address that the `network` can bind to.
+        address-not-bindable,
+
+        /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
+        address-in-use,
+
+        /// The remote address is not reachable
+        remote-unreachable,
+
+
+        // ### TCP SOCKET ERRORS ###
+
+        /// The connection was forcefully rejected
+        connection-refused,
+
+        /// The connection was reset.
+        connection-reset,
+
+        /// A connection was aborted.
+        connection-aborted,
+
+
+        // ### UDP SOCKET ERRORS ###
+        datagram-too-large,
+
+
+        // ### NAME LOOKUP ERRORS ###
+
+        /// Name does not exist or has no suitable associated IP addresses.
+        name-unresolvable,
+
+        /// A temporary failure in name resolution occurred.
+        temporary-resolver-failure,
+
+        /// A permanent failure in name resolution occurred.
+        permanent-resolver-failure,
+    }
+
+    enum ip-address-family {
+        /// Similar to `AF_INET` in POSIX.
+        ipv4,
+
+        /// Similar to `AF_INET6` in POSIX.
+        ipv6,
+    }
+
+    type ipv4-address = tuple<u8, u8, u8, u8>;
+    type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;
+
+    variant ip-address {
+        ipv4(ipv4-address),
+        ipv6(ipv6-address),
+    }
+
+    record ipv4-socket-address {
+        port: u16, // sin_port
+        address: ipv4-address, // sin_addr
+    }
+
+    record ipv6-socket-address {
+        port: u16, // sin6_port
+        flow-info: u32, // sin6_flowinfo
+        address: ipv6-address, // sin6_addr
+        scope-id: u32, // sin6_scope_id
+    }
+
+    variant ip-socket-address {
+        ipv4(ipv4-socket-address),
+        ipv6(ipv6-socket-address),
+    }
+
+}
diff --git a/tests/autosar/deps/sockets/tcp-create-socket.wit b/tests/autosar/deps/sockets/tcp-create-socket.wit
new file mode 100644
index 000000000..768a07c85
--- /dev/null
+++ b/tests/autosar/deps/sockets/tcp-create-socket.wit
@@ -0,0 +1,26 @@
+
+interface tcp-create-socket {
+    use network.{network, error-code, ip-address-family};
+    use tcp.{tcp-socket};
+
+    /// Create a new TCP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`listen`/`connect`
+    /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
+}
diff --git a/tests/autosar/deps/sockets/tcp.wit b/tests/autosar/deps/sockets/tcp.wit
new file mode 100644
index 000000000..b01b65e6c
--- /dev/null
+++ b/tests/autosar/deps/sockets/tcp.wit
@@ -0,0 +1,321 @@
+
+interface tcp {
+    use wasi:io/streams@0.2.0-rc-2023-11-10.{input-stream, output-stream};
+    use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable};
+    use wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10.{duration};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
+
+    enum shutdown-type {
+        /// Similar to `SHUT_RD` in POSIX.
+        receive,
+
+        /// Similar to `SHUT_WR` in POSIX.
+        send,
+
+        /// Similar to `SHUT_RDWR` in POSIX.
+        both,
+    }
+
+
+    /// A TCP socket handle.
+    resource tcp-socket {
+        /// Bind the socket to a specific network on the provided IP address and port.
+        ///
+        /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which
+        /// network interface(s) to bind to.
+        /// If the TCP/UDP port is zero, the socket will be bound to a random free port.
+        ///
+        /// When a socket is not explicitly bound, the first invocation to a listen or connect operation will
+        /// implicitly bind the socket.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+        /// - `invalid-argument`:          `local-address` is not a unicast address. (EINVAL)
+        /// - `invalid-argument`:          `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
+        /// - `address-in-use`:            Address is already in use. (EADDRINUSE)
+        /// - `address-not-bindable`:      `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
+
+        /// Connect to a remote endpoint.
+        ///
+        /// On success:
+        /// - the socket is transitioned into the Connection state
+        /// - a pair of streams is returned that can be used to read & write to the connection
+        ///
+        /// POSIX mentions:
+        /// > If connect() fails, the state of the socket is unspecified. Conforming applications should
+        /// > close the file descriptor and create a new socket before attempting to reconnect.
+        ///
+        /// WASI prescribes the following behavior:
+        /// - If `connect` fails because an input/state validation error, the socket should remain usable.
+        /// - If a connection was actually attempted but failed, the socket should become unusable for further network communication.
+        ///   Besides `drop`, any method after such a failure may return an error.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:          `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
+        /// - `invalid-argument`:          `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `invalid-argument`:          The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
+        /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
+        /// - `invalid-argument`:          The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN)
+        /// - `invalid-state`:             The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows)
+        ///
+        /// # Typical `finish` errors
+        /// - `timeout`:                   Connection timed out. (ETIMEDOUT)
+        /// - `connection-refused`:        The connection was forcefully rejected. (ECONNREFUSED)
+        /// - `connection-reset`:          The connection was reset. (ECONNRESET)
+        /// - `connection-aborted`:        The connection was aborted. (ECONNABORTED)
+        /// - `remote-unreachable`:        The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `not-in-progress`:           A `connect` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
+        finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
+
+        /// Start listening for new connections.
+        ///
+        /// Transitions the socket into the Listener state.
+        ///
+        /// Unlike POSIX:
+        /// - this function is async. This enables interactive WASI hosts to inject permission prompts.
+        /// - the socket must already be explicitly bound.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-state`:             The socket is not bound to any local address. (EDESTADDRREQ)
+        /// - `invalid-state`:             The socket is already in the Connection state. (EISCONN, EINVAL on BSD)
+        /// - `invalid-state`:             The socket is already in the Listener state.
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
+        /// - `not-in-progress`:           A `listen` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
+        /// - <https://man7.org/linux/man-pages/man2/listen.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
+        start-listen: func() -> result<_, error-code>;
+        finish-listen: func() -> result<_, error-code>;
+
+        /// Accept a new client socket.
+        ///
+        /// The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket:
+        /// - `address-family`
+        /// - `ipv6-only`
+        /// - `keep-alive-enabled`
+        /// - `keep-alive-idle-time`
+        /// - `keep-alive-interval`
+        /// - `keep-alive-count`
+        /// - `hop-limit`
+        /// - `receive-buffer-size`
+        /// - `send-buffer-size`
+        ///
+        /// On success, this function returns the newly accepted client socket along with
+        /// a pair of streams that can be used to read & write to the connection.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:      Socket is not in the Listener state. (EINVAL)
+        /// - `would-block`:        No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
+        /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
+        /// - `new-socket-limit`:   The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
+        /// - <https://man7.org/linux/man-pages/man2/accept.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
+        accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
+
+        /// Get the bound local address.
+        ///
+        /// POSIX mentions:
+        /// > If the socket has not been bound to a local name, the value
+        /// > stored in the object pointed to by `address` is unspecified.
+        ///
+        /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not bound to any local address.
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Get the remote address.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Whether the socket is listening for new connections.
+        ///
+        /// Equivalent to the SO_ACCEPTCONN socket option.
+        is-listening: func() -> bool;
+
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
+
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
+
+        /// Hints the desired listen queue size. Implementations are free to ignore this.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (set) The platform does not support changing the backlog size after the initial listen.
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
+
+        /// Enables or disables keepalive.
+        ///
+        /// The keepalive behavior can be adjusted using:
+        /// - `keep-alive-idle-time`
+        /// - `keep-alive-interval`
+        /// - `keep-alive-count`
+        /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true.
+        ///
+        /// Equivalent to the SO_KEEPALIVE socket option.
+        keep-alive-enabled: func() -> result<bool, error-code>;
+        set-keep-alive-enabled: func(value: bool) -> result<_, error-code>;
+
+        /// Amount of time the connection has to be idle before TCP starts sending keepalive packets.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        /// I.e. after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS)
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        keep-alive-idle-time: func() -> result<duration, error-code>;
+        set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>;
+
+        /// The time between keepalive packets.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        /// I.e. after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Equivalent to the TCP_KEEPINTVL socket option.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        keep-alive-interval: func() -> result<duration, error-code>;
+        set-keep-alive-interval: func(value: duration) -> result<_, error-code>;
+
+        /// The maximum amount of keepalive packets TCP should send before aborting the connection.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        /// I.e. after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Equivalent to the TCP_KEEPCNT socket option.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        keep-alive-count: func() -> result<u32, error-code>;
+        set-keep-alive-count: func(value: u32) -> result<_, error-code>;
+
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        hop-limit: func() -> result<u8, error-code>;
+        set-hop-limit: func(value: u8) -> result<_, error-code>;
+
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        /// I.e. after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        /// - `invalid-state`:        (set) The socket is already in the Connection state.
+        /// - `invalid-state`:        (set) The socket is already in the Listener state.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+
+        /// Initiate a graceful shutdown.
+        ///
+        /// - receive: the socket is not expecting to receive any more data from the peer. All subsequent read
+        ///   operations on the `input-stream` associated with this socket will return an End Of Stream indication.
+        ///   Any data still in the receive queue at time of calling `shutdown` will be discarded.
+        /// - send: the socket is not expecting to send any more data to the peer. All subsequent write
+        ///   operations on the `output-stream` associated with this socket will return an error.
+        /// - both: same effect as receive & send combined.
+        ///
+        /// The shutdown function does not close (drop) the socket.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not in the Connection state. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
+        /// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
+        shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
+    }
+}
diff --git a/tests/autosar/deps/sockets/udp-create-socket.wit b/tests/autosar/deps/sockets/udp-create-socket.wit
new file mode 100644
index 000000000..cc58234d8
--- /dev/null
+++ b/tests/autosar/deps/sockets/udp-create-socket.wit
@@ -0,0 +1,26 @@
+
+interface udp-create-socket {
+    use network.{network, error-code, ip-address-family};
+    use udp.{udp-socket};
+
+    /// Create a new UDP socket.
+    ///
+    /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+    ///
+    /// This function does not require a network capability handle. This is considered to be safe because
+    /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called,
+    /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+    ///
+    /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+    ///
+    /// # Typical errors
+    /// - `not-supported`:     The specified `address-family` is not supported. (EAFNOSUPPORT)
+    /// - `new-socket-limit`:  The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+    ///
+    /// # References:
+    /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+    /// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+    /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+    /// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+    create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
+}
diff --git a/tests/autosar/deps/sockets/udp.wit b/tests/autosar/deps/sockets/udp.wit
new file mode 100644
index 000000000..c8dafadfc
--- /dev/null
+++ b/tests/autosar/deps/sockets/udp.wit
@@ -0,0 +1,277 @@
+
+interface udp {
+    use wasi:io/poll@0.2.0-rc-2023-11-10.{pollable};
+    use network.{network, error-code, ip-socket-address, ip-address-family};
+
+    /// A received datagram.
+    record incoming-datagram {
+        /// The payload.
+        /// 
+        /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
+        data: list<u8>,
+
+        /// The source address.
+        ///
+        /// This field is guaranteed to match the remote address the stream was initialized with, if any.
+        ///
+        /// Equivalent to the `src_addr` out parameter of `recvfrom`.
+        remote-address: ip-socket-address,
+    }
+
+    /// A datagram to be sent out.
+    record outgoing-datagram {
+        /// The payload.
+        data: list<u8>,
+
+        /// The destination address.
+        ///
+        /// The requirements on this field depend on how the stream was initialized:
+        /// - with a remote address: this field must be None or match the stream's remote address exactly.
+        /// - without a remote address: this field is required.
+        ///
+        /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`.
+        remote-address: option<ip-socket-address>,
+    }
+
+
+
+    /// A UDP socket handle.
+    resource udp-socket {
+        /// Bind the socket to a specific network on the provided IP address and port.
+        ///
+        /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which
+        /// network interface(s) to bind to.
+        /// If the port is zero, the socket will be bound to a random free port.
+        ///
+        /// Unlike in POSIX, this function is async. This enables interactive WASI hosts to inject permission prompts.
+        ///
+        /// # Typical `start` errors
+        /// - `invalid-argument`:          The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+        /// - `invalid-state`:             The socket is already bound. (EINVAL)
+        ///
+        /// # Typical `finish` errors
+        /// - `address-in-use`:            No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
+        /// - `address-in-use`:            Address is already in use. (EADDRINUSE)
+        /// - `address-not-bindable`:      `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
+        /// - `not-in-progress`:           A `bind` operation is not in progress.
+        /// - `would-block`:               Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+        /// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+        start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
+        finish-bind: func() -> result<_, error-code>;
+
+        /// Set up inbound & outbound communication channels, optionally to a specific peer.
+        ///
+        /// This function only changes the local socket configuration and does not generate any network traffic.
+        /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well,
+        /// based on the best network path to `remote-address`.
+        ///
+        /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer:
+        /// - `send` can only be used to send to this destination.
+        /// - `receive` will only return datagrams sent from the provided `remote-address`.
+        ///
+        /// This method may be called multiple times on the same socket to change its association, but
+        /// only the most recently returned pair of streams will be operational. Implementations may trap if
+        /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again.
+        /// 
+        /// The POSIX equivalent in pseudo-code is:
+        /// ```text
+        /// if (was previously connected) {
+        /// 	connect(s, AF_UNSPEC)
+        /// }
+        /// if (remote_address is Some) {
+        /// 	connect(s, remote_address)
+        /// }
+        /// ```
+        ///
+        /// Unlike in POSIX, the socket must already be explicitly bound.
+        /// 
+        /// # Typical errors
+        /// - `invalid-argument`:          The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:          `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `invalid-argument`:          The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+        /// - `invalid-argument`:          The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
+        /// - `invalid-state`:             The socket is not bound.
+        /// - `address-in-use`:            Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+        /// - `remote-unreachable`:        The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:        The connection was refused. (ECONNREFUSED)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+        /// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+        /// - <https://man.freebsd.org/cgi/man.cgi?connect>
+        %stream: func(remote-address: option<ip-socket-address>) -> result<tuple<incoming-datagram-stream, outgoing-datagram-stream>, error-code>;
+
+        /// Get the current bound address.
+        ///
+        /// POSIX mentions:
+        /// > If the socket has not been bound to a local name, the value
+        /// > stored in the object pointed to by `address` is unspecified.
+        ///
+        /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet.
+        /// 
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not bound to any local address.
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+        /// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+        /// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+        local-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Get the address the socket is currently streaming to.
+        ///
+        /// # Typical errors
+        /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+        /// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+        remote-address: func() -> result<ip-socket-address, error-code>;
+
+        /// Whether this is a IPv4 or IPv6 socket.
+        ///
+        /// Equivalent to the SO_DOMAIN socket option.
+        address-family: func() -> ip-address-family;
+
+        /// Whether IPv4 compatibility (dual-stack) mode is disabled or not.
+        ///
+        /// Equivalent to the IPV6_V6ONLY socket option.
+        ///
+        /// # Typical errors
+        /// - `not-supported`:        (get/set) `this` socket is an IPv4 socket.
+        /// - `invalid-state`:        (set) The socket is already bound.
+        /// - `not-supported`:        (set) Host does not support dual-stack sockets. (Implementations are not required to.)
+        ipv6-only: func() -> result<bool, error-code>;
+        set-ipv6-only: func(value: bool) -> result<_, error-code>;
+
+        /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The TTL value must be 1 or higher.
+        unicast-hop-limit: func() -> result<u8, error-code>;
+        set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
+
+        /// The kernel buffer space reserved for sends/receives on this socket.
+        ///
+        /// If the provided value is 0, an `invalid-argument` error is returned.
+        /// Any other value will never cause an error, but it might be silently clamped and/or rounded.
+        /// I.e. after setting a value, reading the same setting back may return a different value.
+        ///
+        /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:     (set) The provided value was 0.
+        receive-buffer-size: func() -> result<u64, error-code>;
+        set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
+        send-buffer-size: func() -> result<u64, error-code>;
+        set-send-buffer-size: func(value: u64) -> result<_, error-code>;
+
+        /// Create a `pollable` which will resolve once the socket is ready for I/O.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
+
+    resource incoming-datagram-stream {
+        /// Receive messages on the socket.
+        ///
+        /// This function attempts to receive up to `max-results` datagrams on the socket without blocking.
+        /// The returned list may contain fewer elements than requested, but never more.
+        ///
+        /// This function returns successfully with an empty list when either:
+        /// - `max-results` is 0, or:
+        /// - `max-results` is greater than 0, but no results are immediately available.
+        /// This function never returns `error(would-block)`.
+        ///
+        /// # Typical errors
+        /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`: The connection was refused. (ECONNREFUSED)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/recv.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
+        /// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
+        receive: func(max-results: u64) -> result<list<incoming-datagram>, error-code>;
+
+        /// Create a `pollable` which will resolve once the stream is ready to receive again.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
+
+    resource outgoing-datagram-stream {
+        /// Check readiness for sending. This function never blocks.
+        ///
+        /// Returns the number of datagrams permitted for the next call to `send`,
+        /// or an error. Calling `send` with more datagrams than this function has
+        /// permitted will trap.
+        ///
+        /// When this function returns ok(0), the `subscribe` pollable will
+        /// become ready when this function will report at least ok(1), or an
+        /// error.
+        /// 
+        /// Never returns `would-block`.
+        check-send: func() -> result<u64, error-code>;
+
+        /// Send messages on the socket.
+        ///
+        /// This function attempts to send all provided `datagrams` on the socket without blocking and
+        /// returns how many messages were actually sent (or queued for sending). This function never
+        /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned.
+        ///
+        /// This function semantically behaves the same as iterating the `datagrams` list and sequentially
+        /// sending each individual datagram until either the end of the list has been reached or the first error occurred.
+        /// If at least one datagram has been sent successfully, this function never returns an error.
+        ///
+        /// If the input list is empty, the function returns `ok(0)`.
+        ///
+        /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if
+        /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted.
+        ///
+        /// # Typical errors
+        /// - `invalid-argument`:        The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+        /// - `invalid-argument`:        `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa)
+        /// - `invalid-argument`:        The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+        /// - `invalid-argument`:        The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
+        /// - `invalid-argument`:        The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN)
+        /// - `invalid-argument`:        The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
+        /// - `remote-unreachable`:      The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+        /// - `connection-refused`:      The connection was refused. (ECONNREFUSED)
+        /// - `datagram-too-large`:      The datagram is too large. (EMSGSIZE)
+        ///
+        /// # References
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
+        /// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
+        /// - <https://man7.org/linux/man-pages/man2/send.2.html>
+        /// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
+        /// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
+        /// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
+        send: func(datagrams: list<outgoing-datagram>) -> result<u64, error-code>;
+        
+        /// Create a `pollable` which will resolve once the stream is ready to send again.
+        ///
+        /// Note: this function is here for WASI Preview2 only.
+        /// It's planned to be removed when `future` is natively supported in Preview3.
+        subscribe: func() -> pollable;
+    }
+}
diff --git a/tests/autosar/deps/sockets/world.wit b/tests/autosar/deps/sockets/world.wit
new file mode 100644
index 000000000..49ad8d3d9
--- /dev/null
+++ b/tests/autosar/deps/sockets/world.wit
@@ -0,0 +1,11 @@
+package wasi:sockets@0.2.0-rc-2023-11-10;
+
+world imports {
+    import instance-network;
+    import network;
+    import udp;
+    import udp-create-socket;
+    import tcp;
+    import tcp-create-socket;
+    import ip-name-lookup;
+}
diff --git a/tests/autosar/deps/threads/threads.wit b/tests/autosar/deps/threads/threads.wit
new file mode 100644
index 000000000..224339d54
--- /dev/null
+++ b/tests/autosar/deps/threads/threads.wit
@@ -0,0 +1,16 @@
+interface threads {
+    /// The result of the `thread-spawn()` function.
+    /// If spawning the thread was successful, the value is positive
+    /// and represents a unique thread identifier. Otherwise, the
+    /// value is negative and it represents error code.
+    type thread-spawn-result = s32
+
+    /// A reference to data passed to the start function (`wasi_thread_start()`) called by the newly spawned thread.
+    type start-arg = u32
+
+    /// Creates a new thread.
+    thread-spawn: func(
+        /// A value being passed to a start function (`wasi_thread_start()`).
+        start-arg: start-arg,
+    ) -> thread-spawn-result
+}
diff --git a/tests/autosar/deps/threads/world.wit b/tests/autosar/deps/threads/world.wit
new file mode 100644
index 000000000..9065146c4
--- /dev/null
+++ b/tests/autosar/deps/threads/world.wit
@@ -0,0 +1,5 @@
+package wasi:threads
+
+world example-world {
+    import threads
+}
diff --git a/tests/autosar/fusion.wit b/tests/autosar/fusion.wit
new file mode 100644
index 000000000..e6ddf07ba
--- /dev/null
+++ b/tests/autosar/fusion.wit
@@ -0,0 +1,12 @@
+world fusion {
+    import radar
+    export radar-callbacks
+    
+    import core
+    import log
+    import com
+    import exec
+    import wasi:threads/threads
+    import wasi:io/poll
+    export wasi:cli/run
+}
diff --git a/tests/autosar/poll.wit b/tests/autosar/poll.wit
new file mode 100644
index 000000000..2b59045d2
--- /dev/null
+++ b/tests/autosar/poll.wit
@@ -0,0 +1,37 @@
+/// A poll API intended to let users wait for I/O events on multiple handles
+/// at once.
+interface poll {
+    /// A "pollable" handle.
+    ///
+    /// This is conceptually represents a `stream<_, _>`, or in other words,
+    /// a stream that one can wait on, repeatedly, but which does not itself
+    /// produce any data. It's temporary scaffolding until component-model's
+    /// async features are ready.
+    ///
+    /// And at present, it is a `u32` instead of being an actual handle, until
+    /// the wit-bindgen implementation of handles and resources is ready.
+    ///
+    /// `pollable` lifetimes are not automatically managed. Users must ensure
+    /// that they do not outlive the resource they reference.
+    ///
+    /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources).
+    type pollable = u32
+
+    /// Dispose of the specified `pollable`, after which it may no longer
+    /// be used.
+    drop-pollable: func(this: pollable)
+
+    /// Poll for completion on a set of pollables.
+    ///
+    /// The "oneoff" in the name refers to the fact that this function must do a
+    /// linear scan through the entire list of subscriptions, which may be
+    /// inefficient if the number is large and the same subscriptions are used
+    /// many times. In the future, this is expected to be obsoleted by the
+    /// component model async proposal, which will include a scalable waiting
+    /// facility.
+    ///
+    /// The result list<bool> is the same length as the argument
+    /// list<pollable>, and indicates the readiness of each corresponding
+    /// element in that / list, with true indicating ready.
+    poll-oneoff: func(in: list<pollable>) -> list<bool>
+}
diff --git a/tests/autosar/radar.wit b/tests/autosar/radar.wit
new file mode 100644
index 000000000..1d23d88b4
--- /dev/null
+++ b/tests/autosar/radar.wit
@@ -0,0 +1,135 @@
+// package autosar:fusion
+
+// TODO figure out whether this is bidirectional
+interface com-add {
+    use com.{instance-identifier}
+    use com.{profile-check-status}
+
+    resource instance-handle {
+        get-instance-id: func() -> instance-identifier
+        clone: func() -> instance-handle
+    }
+
+    resource sample-ptr {
+        get: func() -> s32
+        get-profile-check-status: func() -> profile-check-status
+    }
+
+    resource stream-control {
+        suspend: func()
+        resume: func()
+        abort: static func(self: own<stream-control>)
+    }
+
+    resource abort-handle {
+        abort: static func(self: own<abort-handle>)
+    }
+
+    resource resume-handle {
+        resume: static func(self: own<resume-handle>)
+    }
+}
+
+interface radar-types {
+    record position {
+        x: s32,
+        y: s32,
+        z: s32,
+    }
+    record radar-objects {
+        active: bool,
+        object-vector: list<u8>,
+    }
+    enum fusion-variant {
+        china, usa, europe, russia,
+    }
+
+    record adjust-output {
+        success: bool, 
+        effective-position: position,
+    }
+    record calibrate-output {
+        call-result: bool
+    }
+}
+
+interface radar-consumer {
+    use types.{error-code}
+    use radar-types.{adjust-output, calibrate-output}
+    use com-add.{instance-handle, sample-ptr, resume-handle}
+
+    resource promise-result-adjust-output-error-code {
+        ready: static func(self: own<promise-result-adjust-output-error-code>, value: result<adjust-output, error-code>)
+    }
+    resource promise-result-calibrate-output-error-code {
+        ready: static func(self: own<promise-result-calibrate-output-error-code>, value: result<calibrate-output, error-code>)
+    }
+    resource promise-u32 {
+        ready: static func(self: own<promise-u32>, value: u32)
+    }
+    resource promise-u16 {
+        ready: static func(self: own<promise-u16>, value: u16)
+    }
+
+    resource stream-instance-handle {
+        // returns the value if the queue is filled up (backpressure)
+        ready: func(value: option<own<instance-handle>>, resume: resume-handle) -> option<own<instance-handle>>
+    }
+    resource stream-sample-ptr {
+        ready: func(value: option<own<sample-ptr>>, resume: resume-handle) -> option<own<sample-ptr>>
+    }
+}
+
+interface radar-provider {
+    use types.{error-code}
+    use core.{instance-specifier}
+    use com-add.{instance-handle, stream-control, abort-handle}
+    use radar-consumer.{promise-result-adjust-output-error-code, promise-result-calibrate-output-error-code, stream-instance-handle, stream-sample-ptr, promise-u16, promise-u32}
+    use radar-types.{position, fusion-variant}
+
+    resource instance {
+        constructor(handle: borrow<instance-handle>)
+
+        adjust: func (promise: promise-result-adjust-output-error-code, target-position: position) -> option<abort-handle>
+        calibrate: func (promise: promise-result-calibrate-output-error-code, configuration: string, radar-variant: fusion-variant) -> option<abort-handle>
+        echo: func (text: string)
+    
+        subscribe-brake-event: func (max-sample-count: u32, receiver: stream-sample-ptr) -> result<stream-control, error-code>
+        subscribe-parking-brake-event: func (max-sample-count: u32, receiver: stream-sample-ptr) -> result<stream-control, error-code>
+    
+        // field
+        get-update-rate: func(promise: promise-u32) -> option<abort-handle>
+        set-update-rate: func(promise: promise-u32, value: u32) -> option<abort-handle>
+        subscribe-update-rate: func(max-sample-count: u32, receiver: stream-sample-ptr) -> result<stream-control, error-code>
+        subscribe-front-object-distance: func(max-sample-count: u32, receiver: stream-sample-ptr) -> result<stream-control, error-code>
+        get-rear-object-distance: func(promise: promise-u16) -> option<abort-handle>
+        set-object-detection-limit: func(promise: promise-u16, value: u16) -> option<abort-handle>
+    }
+
+    start-find-service: func(spec: borrow<instance-specifier>, receiver: stream-instance-handle) -> option<stream-control>
+}
+
+// interface service {
+//     use radar.{instance}
+//     use core.{instance-specifier}
+//     use com-add.{abort-handle}
+//     enum method-call-processing-mode {
+//         poll, event, event-single-thread,
+//     }
+//     // perhaps use own here?
+//     offer-service: func(service: borrow<instance>, spec: borrow<instance-specifier>, mode: method-call-processing-mode) -> abort-handle
+//     //stop-offer-service: func (service: borrow<instance>)
+// }
+
+world radar-world {
+    export radar-provider
+    import radar-consumer
+
+    import core
+    import log
+    import com
+    import exec
+    // import service
+    import wasi:threads/threads
+    export wasi:cli/run
+}
diff --git a/tests/autosar/wit b/tests/autosar/wit
new file mode 120000
index 000000000..945c9b46d
--- /dev/null
+++ b/tests/autosar/wit
@@ -0,0 +1 @@
+.
\ No newline at end of file
diff --git a/tests/codegen/trivial-lists.wit b/tests/codegen/trivial-lists.wit
new file mode 100644
index 000000000..d60707043
--- /dev/null
+++ b/tests/codegen/trivial-lists.wit
@@ -0,0 +1,13 @@
+package foo:foo;
+
+// a subset of simple-lists
+interface trivial-lists {
+  trivial-list1: func(l: list<u32>);
+  trivial-list2: func() -> list<u32>;
+  trivial-list3: func(a: list<u32>, b: list<u32>) -> list<u32>;
+}
+
+world my-world {
+  import trivial-lists;
+  export trivial-lists;
+}
diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp
new file mode 100644
index 000000000..0e4481a6d
--- /dev/null
+++ b/tests/runtime/lists/wasm.new.cpp
@@ -0,0 +1,185 @@
+#include <assert.h>
+#include <lists_cpp.h>
+#include <float.h>
+#include <math.h>
+
+uint32_t exports::lists::AllocatedBytes() {
+    return 0;
+}
+
+static bool equal(wit::string const&a, std::string_view b) {
+    return a.get_view() == b;
+}
+static bool equal(wit::string const&a, const char x[]) {
+    return a.get_view() == x;
+}
+template <class T, class S>
+static bool equal(T const&a, S const& b) {
+    return a == b;
+}
+template<class R, class S>
+static bool equal(wit::span<R> const&a, wit::span<S> const& b) {
+    if (a.size() != b.size()) { return false; }
+    for (uint32_t i = 0; i<a.size(); ++i) {
+        if (!equal(a[i], b[i])) { return false; }
+    }
+    return true;
+}
+template<class R>
+static bool equal(wit::vector<R> const&a, wit::span<R> const& b) {
+    return equal(a.get_view(), b);
+}
+template<class R>
+static bool equal(wit::span<const R> const&a, wit::vector<R> const& b) {
+    return equal(b, a);
+}
+template<class R>
+static bool equal(wit::span<const R> const&a, std::vector<R> const& b) {
+    return equal(a, wit::span<R>(b));
+}
+template<class R>
+static bool equal(wit::vector<R> const&a, std::vector<R> const& b) {
+    return equal(a.get_view(), wit::span<R>(b));
+}
+static bool equal(wit::vector<wit::string> const&a, std::vector<std::string_view> const& b) {
+    return equal(a.get_view(), wit::span<std::string_view>(b));
+}
+template<class R,class S, class T, class U>
+static bool equal(std::tuple<R,S> const&a, std::tuple<T,U> const& b) {
+    return equal(std::get<0>(a), std::get<0>(b)) && equal(std::get<1>(a), std::get<1>(b));
+}
+
+void exports::lists::TestImports() {
+    //let _guard = testRust_wasm::guard();
+
+    test::lists::test::EmptyListParam(wit::span<const uint8_t>(std::vector<uint8_t>()));
+    test::lists::test::EmptyStringParam("");
+    assert(test::lists::test::EmptyListResult().empty());
+    assert(test::lists::test::EmptyStringResult().empty());
+
+    test::lists::test::ListParam(std::vector<uint8_t>{1, 2, 3, 4});
+    test::lists::test::ListParam2("foo");
+    test::lists::test::ListParam3(std::vector<std::string_view>{"foo", "bar", "baz"});
+    test::lists::test::ListParam4(std::vector<wit::span<const std::string_view>>{
+        std::vector<std::string_view>{"foo", "bar"},
+        std::vector<std::string_view>{"baz"},
+    });
+    assert(equal(test::lists::test::ListResult(), std::vector<uint8_t>{1, 2, 3, 4, 5}));
+    assert(equal(test::lists::test::ListResult2(), "hello!"));
+    assert(equal(test::lists::test::ListResult3(), std::vector<std::string_view>{"hello,", "world!"}));
+
+    assert(equal(test::lists::test::ListRoundtrip(wit::span<const uint8_t>(std::vector<uint8_t>())), std::vector<uint8_t>()));
+    assert(equal(test::lists::test::ListRoundtrip(wit::span<const uint8_t>(std::vector<uint8_t>{'x'})), std::vector<uint8_t>{'x'}));
+    assert(equal(test::lists::test::ListRoundtrip(wit::span<const uint8_t>(std::vector<uint8_t>{'h', 'e', 'l', 'l', 'o'})), std::vector<uint8_t>{'h', 'e', 'l', 'l', 'o'}));
+
+    assert(equal(test::lists::test::StringRoundtrip("x"), "x"));
+    assert(equal(test::lists::test::StringRoundtrip(""), ""));
+    assert(equal(test::lists::test::StringRoundtrip("hello"), "hello"));
+    assert(equal(test::lists::test::StringRoundtrip("hello âš‘ world"), "hello âš‘ world"));
+
+    assert(equal(
+        test::lists::test::ListMinmax8(std::vector<uint8_t>{0, UINT8_MAX}, std::vector<int8_t>{INT8_MIN, INT8_MAX}),
+        std::make_tuple(std::vector<uint8_t>{0, UINT8_MAX}, std::vector<int8_t>{INT8_MIN, INT8_MAX})
+    ));
+    assert(equal(
+        test::lists::test::ListMinmax16(std::vector<uint16_t>{0, UINT16_MAX}, std::vector<int16_t>{INT16_MIN, INT16_MAX}),
+        std::make_tuple(std::vector<uint16_t>{0, UINT16_MAX}, std::vector<int16_t>{INT16_MIN, INT16_MAX})
+    ));
+    assert(equal(
+        test::lists::test::ListMinmax32(std::vector<uint32_t>{0, UINT32_MAX}, std::vector<int32_t>{INT32_MIN, INT32_MAX}),
+        std::make_tuple(std::vector<uint32_t>{0, UINT32_MAX}, std::vector<int32_t>{INT32_MIN, INT32_MAX})
+    ));
+    assert(equal(
+        test::lists::test::ListMinmax64(std::vector<uint64_t>{0, UINT64_MAX}, std::vector<int64_t>{INT64_MIN, INT64_MAX}),
+        std::make_tuple(std::vector<uint64_t>{0, UINT64_MAX}, std::vector<int64_t>{INT64_MIN, INT64_MAX})
+    ));
+    assert(equal(
+        test::lists::test::ListMinmaxFloat(
+            std::vector<float>{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF},
+            std::vector<double>{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL}
+        ),
+        std::make_tuple(
+            std::vector<float>{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF},
+            std::vector<double>{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL}
+        )
+    ));
+}
+
+
+void exports::test::lists::test::EmptyListParam(wit::span<uint8_t const> a) {
+    assert(a.empty());
+}
+
+void exports::test::lists::test::EmptyStringParam(std::string_view a) {
+    assert(a.empty());
+}
+
+wit::vector<uint8_t> exports::test::lists::test::EmptyListResult() {
+    return wit::vector<uint8_t>();
+}
+
+wit::string exports::test::lists::test::EmptyStringResult() {
+    return wit::string::from_view(std::string_view());
+}
+
+void exports::test::lists::test::ListParam(wit::span<const uint8_t> list) {
+    assert(equal(list, std::vector<uint8_t>{1, 2, 3, 4}));
+}
+
+void exports::test::lists::test::ListParam2(std::string_view ptr) {
+    assert(equal(ptr, std::string_view("foo")));
+}
+
+void exports::test::lists::test::ListParam3(wit::span<const std::string_view> ptr) {
+    assert(equal(ptr.size(), size_t(3)));
+    assert(equal(ptr[0], std::string_view("foo")));
+    assert(equal(ptr[1], std::string_view("bar")));
+    assert(equal(ptr[2], std::string_view("baz")));
+}
+
+void exports::test::lists::test::ListParam4(wit::span<const wit::span<const std::string_view>> ptr) {
+    assert(equal(ptr.size(), size_t(2)));
+    assert(equal(ptr[0][0], std::string_view("foo")));
+    assert(equal(ptr[0][1], std::string_view("bar")));
+    assert(equal(ptr[1][0], std::string_view("baz")));
+}
+
+wit::vector<uint8_t> exports::test::lists::test::ListResult() {
+    return wit::vector<uint8_t>::from_view(wit::span<uint8_t>(std::vector<uint8_t>{1, 2, 3, 4, 5}));
+}
+
+wit::string exports::test::lists::test::ListResult2() {
+    return wit::string::from_view("hello!");
+}
+
+wit::vector<wit::string> exports::test::lists::test::ListResult3() {
+    return wit::vector<wit::string>::from_view(wit::span<wit::string>(std::vector<wit::string>{wit::string::from_view("hello,"), wit::string::from_view("world!")}));
+}
+
+wit::vector<uint8_t> exports::test::lists::test::ListRoundtrip(wit::span<const uint8_t> x) {
+    return wit::vector<uint8_t>::from_view(x);
+}
+
+wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) {
+    return wit::string::from_view(x);
+}
+
+std::tuple<wit::vector<uint8_t>, wit::vector<int8_t>> exports::test::lists::test::ListMinmax8(wit::span<uint8_t const> a, wit::span<int8_t const> b) {
+    return std::make_tuple(wit::vector<uint8_t>::from_view(a), wit::vector<int8_t>::from_view(b));
+}
+
+std::tuple<wit::vector<uint16_t>, wit::vector<int16_t>> exports::test::lists::test::ListMinmax16(wit::span<uint16_t const> a, wit::span<int16_t const> b) {
+    return std::make_tuple(wit::vector<uint16_t>::from_view(a), wit::vector<int16_t>::from_view(b));
+}
+
+std::tuple<wit::vector<uint32_t>, wit::vector<int32_t>> exports::test::lists::test::ListMinmax32(wit::span<uint32_t const> a, wit::span<int32_t const> b) {
+    return std::make_tuple(wit::vector<uint32_t>::from_view(a), wit::vector<int32_t>::from_view(b));
+}
+
+std::tuple<wit::vector<uint64_t>, wit::vector<int64_t>> exports::test::lists::test::ListMinmax64(wit::span<uint64_t const> a, wit::span<int64_t const> b) {
+    return std::make_tuple(wit::vector<uint64_t>::from_view(a), wit::vector<int64_t>::from_view(b));
+}
+
+std::tuple<wit::vector<float>, wit::vector<double>> exports::test::lists::test::ListMinmaxFloat(wit::span<float const> a, wit::span<double const> b) {
+    return std::make_tuple(wit::vector<float>::from_view(a), wit::vector<double>::from_view(b));
+}
diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs
index 44204c436..9da8c6d41 100644
--- a/tests/runtime/main.rs
+++ b/tests/runtime/main.rs
@@ -122,6 +122,7 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
     let mut c = Vec::new();
     let mut java = Vec::new();
     let mut c_sharp: Vec<PathBuf> = Vec::new();
+    let mut cpp = Vec::new();
     for file in dir.read_dir()? {
         let path = file?.path();
         match path.extension().and_then(|s| s.to_str()) {
@@ -135,6 +136,7 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
                 );
             }
             Some("cs") => c_sharp.push(path),
+            Some("cpp") => cpp.push(path),
             _ => {}
         }
     }
@@ -269,6 +271,100 @@ fn tests(name: &str, dir_name: &str) -> Result<Vec<PathBuf>> {
         }
     }
 
+    #[cfg(feature = "cpp")]
+    if !cpp.is_empty() {
+        let (resolve, world) = resolve_wit_dir(&dir);
+        for path in cpp.iter() {
+            let world_name = &resolve.worlds[world].name;
+            let out_dir = out_dir.join(format!(
+                "cpp-{}",
+                path.file_name()
+                    .and_then(|os| os.to_str())
+                    .unwrap_or(world_name)
+            ));
+            drop(fs::remove_dir_all(&out_dir));
+            fs::create_dir_all(&out_dir).unwrap();
+
+            let snake = world_name.replace("-", "_");
+            let mut files = Default::default();
+            let mut opts = wit_bindgen_cpp::Opts::default();
+            if let Some(path) = path.file_name().and_then(|s| s.to_str()) {
+                if path.contains(".new.") {
+                    opts.new_api = true;
+                }
+            }
+            opts.build().generate(&resolve, world, &mut files).unwrap();
+
+            for (file, contents) in files.iter() {
+                let dst = out_dir.join(file);
+                fs::write(dst, contents).unwrap();
+            }
+
+            let sdk = PathBuf::from(std::env::var_os("WASI_SDK_PATH").expect(
+                "point the `WASI_SDK_PATH` environment variable to the path of your wasi-sdk",
+            ));
+            // Test both C mode and C++ mode.
+            let compiler = "bin/clang++";
+            let mut cmd = Command::new(sdk.join(compiler));
+            let out_wasm = out_dir.join(format!(
+                "cpp-{}.wasm",
+                path.file_stem().and_then(|s| s.to_str()).unwrap()
+            ));
+            cmd.arg("--sysroot").arg(sdk.join("share/wasi-sysroot"));
+            cmd.arg(path)
+                .arg(out_dir.join(format!("{snake}.cpp")))
+                .arg(out_dir.join(format!("{snake}_component_type.o")))
+                .arg("-I")
+                .arg(&out_dir)
+                .arg("-I")
+                //                .arg(&(String::from(env!("CARGO_MANIFEST_DIR")) + "/crates/cpp/helper-types"))
+                .arg(&(String::from(env!("CARGO_MANIFEST_DIR")) + "/crates/cpp/test_headers"))
+                .arg("-Wall")
+                .arg("-Wextra")
+                //                .arg("-Werror")
+                .arg("-Wno-unused-parameter")
+                .arg("-mexec-model=reactor")
+                // for now avoid exceptions on allocation failures
+                .arg("-fno-exceptions")
+                .arg("-std=c++17")
+                .arg("-g")
+                .arg("-o")
+                .arg(&out_wasm);
+            println!("{:?}", cmd);
+            let output = match cmd.output() {
+                Ok(output) => output,
+                Err(e) => panic!("failed to spawn compiler: {}", e),
+            };
+
+            if !output.status.success() {
+                println!("status: {}", output.status);
+                println!("stdout: ------------------------------------------");
+                println!("{}", String::from_utf8_lossy(&output.stdout));
+                println!("stderr: ------------------------------------------");
+                println!("{}", String::from_utf8_lossy(&output.stderr));
+                panic!("failed to compile");
+            }
+
+            // Translate the canonical ABI module into a component.
+            let module = fs::read(&out_wasm).expect("failed to read wasm file");
+            let component = ComponentEncoder::default()
+                .module(module.as_slice())
+                .expect("pull custom sections from module")
+                .validate(true)
+                .adapter("wasi_snapshot_preview1", &wasi_adapter)
+                .expect("adapter failed to get loaded")
+                .encode()
+                .expect(&format!(
+                    "module {:?} can be translated to a component",
+                    out_wasm
+                ));
+            let component_path = out_wasm.with_extension("component.wasm");
+            fs::write(&component_path, component).expect("write component to disk");
+
+            result.push(component_path);
+        }
+    }
+
     #[cfg(feature = "csharp-mono")]
     if cfg!(windows) && !c_sharp.is_empty() {
         let (resolve, world) = resolve_wit_dir(&dir);
diff --git a/tests/runtime/many_arguments/wasm.new.cpp b/tests/runtime/many_arguments/wasm.new.cpp
new file mode 100644
index 000000000..80b70603e
--- /dev/null
+++ b/tests/runtime/many_arguments/wasm.new.cpp
@@ -0,0 +1,46 @@
+#include <assert.h>
+#include <many_arguments_cpp.h>
+
+template <class T>
+bool equal(T const&a, T const&b) {
+    return a==b;
+}
+
+void exports::many_arguments::ManyArguments(
+    uint64_t a1,
+    uint64_t a2,
+    uint64_t a3,
+    uint64_t a4,
+    uint64_t a5,
+    uint64_t a6,
+    uint64_t a7,
+    uint64_t a8,
+    uint64_t a9,
+    uint64_t a10,
+    uint64_t a11,
+    uint64_t a12,
+    uint64_t a13,
+    uint64_t a14,
+    uint64_t a15,
+    uint64_t a16
+) {
+    assert(equal(a1, (uint64_t)1));
+    assert(equal(a2, (uint64_t)2));
+    assert(equal(a3, (uint64_t)3));
+    assert(equal(a4, (uint64_t)4));
+    assert(equal(a5, (uint64_t)5));
+    assert(equal(a6, (uint64_t)6));
+    assert(equal(a7, (uint64_t)7));
+    assert(equal(a8, (uint64_t)8));
+    assert(equal(a9, (uint64_t)9));
+    assert(equal(a10, (uint64_t)10));
+    assert(equal(a11, (uint64_t)11));
+    assert(equal(a12, (uint64_t)12));
+    assert(equal(a13, (uint64_t)13));
+    assert(equal(a14, (uint64_t)14));
+    assert(equal(a15, (uint64_t)15));
+    assert(equal(a16, (uint64_t)16));
+    ::test::many_arguments::ManyArguments(
+        a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16
+    );
+}
diff --git a/tests/runtime/numbers/wasm.cpp b/tests/runtime/numbers/wasm.cpp
new file mode 100644
index 000000000..4da3f1ce9
--- /dev/null
+++ b/tests/runtime/numbers/wasm.cpp
@@ -0,0 +1,112 @@
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <numbers_cpp.h>
+
+uint8_t exports::test::numbers::test::RoundtripU8(uint8_t a) {
+  return a;
+}
+
+int8_t exports::test::numbers::test::RoundtripS8(int8_t a) {
+  return a;
+}
+
+uint16_t exports::test::numbers::test::RoundtripU16(uint16_t a) {
+  return a;
+}
+
+int16_t exports::test::numbers::test::RoundtripS16(int16_t a) {
+  return a;
+}
+
+uint32_t exports::test::numbers::test::RoundtripU32(uint32_t a) {
+  return a;
+}
+
+int32_t exports::test::numbers::test::RoundtripS32(int32_t a) {
+  return a;
+}
+
+uint64_t exports::test::numbers::test::RoundtripU64(uint64_t a) {
+  return a;
+}
+
+int64_t exports::test::numbers::test::RoundtripS64(int64_t a) {
+  return a;
+}
+
+float exports::test::numbers::test::RoundtripF32(float a) {
+  return a;
+}
+
+double exports::test::numbers::test::RoundtripF64(double a) {
+  return a;
+}
+
+uint32_t exports::test::numbers::test::RoundtripChar(uint32_t a) {
+  return a;
+}
+
+static uint32_t SCALAR = 0;
+
+void exports::test::numbers::test::SetScalar(uint32_t a) {
+  SCALAR = a;
+}
+
+uint32_t exports::test::numbers::test::GetScalar(void) {
+  return SCALAR;
+}
+
+
+void exports::numbers::TestImports() {
+  assert(::test::numbers::test::RoundtripU8(1) == 1);
+  assert(::test::numbers::test::RoundtripU8(0) == 0);
+  assert(::test::numbers::test::RoundtripU8(UCHAR_MAX) == UCHAR_MAX);
+
+  assert(::test::numbers::test::RoundtripS8(1) == 1);
+  assert(::test::numbers::test::RoundtripS8(SCHAR_MIN) == SCHAR_MIN);
+  assert(::test::numbers::test::RoundtripS8(SCHAR_MAX) == SCHAR_MAX);
+
+  assert(::test::numbers::test::RoundtripU16(1) == 1);
+  assert(::test::numbers::test::RoundtripU16(0) == 0);
+  assert(::test::numbers::test::RoundtripU16(USHRT_MAX) == USHRT_MAX);
+
+  assert(::test::numbers::test::RoundtripS16(1) == 1);
+  assert(::test::numbers::test::RoundtripS16(SHRT_MIN) == SHRT_MIN);
+  assert(::test::numbers::test::RoundtripS16(SHRT_MAX) == SHRT_MAX);
+
+  assert(::test::numbers::test::RoundtripU32(1) == 1);
+  assert(::test::numbers::test::RoundtripU32(0) == 0);
+  assert(::test::numbers::test::RoundtripU32(UINT_MAX) == UINT_MAX);
+
+  assert(::test::numbers::test::RoundtripS32(1) == 1);
+  assert(::test::numbers::test::RoundtripS32(INT_MIN) == INT_MIN);
+  assert(::test::numbers::test::RoundtripS32(INT_MAX) == INT_MAX);
+
+  assert(::test::numbers::test::RoundtripU64(1) == 1);
+  assert(::test::numbers::test::RoundtripU64(0) == 0);
+  assert(::test::numbers::test::RoundtripU64(ULONG_MAX) == ULONG_MAX);
+
+  assert(::test::numbers::test::RoundtripS64(1) == 1);
+  assert(::test::numbers::test::RoundtripS64(LONG_MIN) == LONG_MIN);
+  assert(::test::numbers::test::RoundtripS64(LONG_MAX) == LONG_MAX);
+
+  assert(::test::numbers::test::RoundtripF32(1.0) == 1.0);
+  assert(::test::numbers::test::RoundtripF32(INFINITY) == INFINITY);
+  assert(::test::numbers::test::RoundtripF32(-INFINITY) == -INFINITY);
+  assert(isnan(::test::numbers::test::RoundtripF32(NAN)));
+
+  assert(::test::numbers::test::RoundtripF64(1.0) == 1.0);
+  assert(::test::numbers::test::RoundtripF64(INFINITY) == INFINITY);
+  assert(::test::numbers::test::RoundtripF64(-INFINITY) == -INFINITY);
+  assert(isnan(::test::numbers::test::RoundtripF64(NAN)));
+
+  assert(::test::numbers::test::RoundtripChar('a') == 'a');
+  assert(::test::numbers::test::RoundtripChar(' ') == ' ');
+  assert(::test::numbers::test::RoundtripChar(U'🚩') == U'🚩');
+
+  ::test::numbers::test::SetScalar(2);
+  assert(::test::numbers::test::GetScalar() == 2);
+  ::test::numbers::test::SetScalar(4);
+  assert(::test::numbers::test::GetScalar() == 4);
+}
diff --git a/tests/runtime/options/wasm.new.cpp b/tests/runtime/options/wasm.new.cpp
new file mode 100644
index 000000000..8925a3f74
--- /dev/null
+++ b/tests/runtime/options/wasm.new.cpp
@@ -0,0 +1,58 @@
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <options_cpp.h>
+
+template <class T>
+static bool equal(T const& a, T const& b) {
+    return a==b;
+}
+static bool equal(wit::string const& a, std::string const& b) {
+    return a.get_view() == std::string_view(b);
+}
+static bool equal(std::optional<wit::string> const& a, std::optional<std::string> const& b) {
+    if (a.has_value() != b.has_value()) return false;
+    if (a.has_value()) {
+        return equal(a.value(), b.value());
+    }
+    return true;
+}
+
+void exports::options::TestImports() {
+    using namespace test::options::test;
+
+    OptionNoneParam(std::optional<std::string_view>());
+    OptionSomeParam(std::optional<std::string_view>("foo"));
+    assert(!OptionNoneResult());
+    assert(equal(OptionSomeResult(), std::optional<std::string>("foo")));
+    assert(equal(OptionRoundtrip(std::optional<std::string_view>("foo")), std::optional<std::string>("foo")));
+    assert(equal(DoubleOptionRoundtrip(std::optional<std::optional<uint32_t>>(std::optional<uint32_t>(42))), std::optional<std::optional<uint32_t>>(std::optional<uint32_t>(42))));
+    assert(equal(DoubleOptionRoundtrip(std::optional<std::optional<uint32_t>>(std::optional<uint32_t>())), std::optional<std::optional<uint32_t>>(std::optional<uint32_t>())));
+    assert(equal(DoubleOptionRoundtrip(std::optional<std::optional<uint32_t>>()), std::optional<std::optional<uint32_t>>()));
+}
+
+void exports::test::options::test::OptionNoneParam(std::optional<std::string_view> a)
+{
+    assert(!a.has_value());
+}
+
+std::optional<wit::string> exports::test::options::test::OptionNoneResult() {
+    return std::optional<wit::string>();
+}
+
+void exports::test::options::test::OptionSomeParam(std::optional<std::string_view> a) {
+    assert(equal(a, std::optional<std::string_view>("foo")));
+}
+
+std::optional<wit::string> exports::test::options::test::OptionSomeResult() {
+    return std::optional<wit::string>(wit::string::from_view("foo"));
+}
+
+std::optional<wit::string> exports::test::options::test::OptionRoundtrip(std::optional<std::string_view> a) {
+    if (!a.has_value()) return std::optional<wit::string>();
+    return std::optional<wit::string>(wit::string::from_view(*a));
+}
+
+std::optional<std::optional<uint32_t>> exports::test::options::test::DoubleOptionRoundtrip(std::optional<std::optional<uint32_t>> a) {
+    return a;
+}
diff --git a/tests/runtime/records/wasm.new.cpp b/tests/runtime/records/wasm.new.cpp
new file mode 100644
index 000000000..d5b276f46
--- /dev/null
+++ b/tests/runtime/records/wasm.new.cpp
@@ -0,0 +1,75 @@
+#include <assert.h>
+#include <records_cpp.h>
+
+template <class T>
+bool equal(T const&a, T const&b) {
+    return a==b;
+}
+
+void exports::records::TestImports() {
+    using namespace ::test::records::test;
+
+    assert(equal(MultipleResults(), std::tuple<uint8_t, uint16_t>(4, 5)));
+
+    assert(equal(SwapTuple(std::tuple<uint8_t, uint32_t>(1, 2)), std::tuple<uint32_t, uint8_t>(2, 1)));
+    assert(equal(RoundtripFlags1(::test::records::test::F1::kA), ::test::records::test::F1::kA));
+    assert(equal(RoundtripFlags1(::test::records::test::F1::k_None), ::test::records::test::F1::k_None));
+    assert(equal(RoundtripFlags1(::test::records::test::F1::kB), ::test::records::test::F1::kB));
+    assert(equal(RoundtripFlags1(::test::records::test::F1::kA | ::test::records::test::F1::kB), ::test::records::test::F1::kA | ::test::records::test::F1::kB));
+
+    assert(equal(RoundtripFlags2(::test::records::test::F2::kC), ::test::records::test::F2::kC));
+    assert(equal(RoundtripFlags2(::test::records::test::F2::k_None), ::test::records::test::F2::k_None));
+    assert(equal(RoundtripFlags2(::test::records::test::F2::kD), ::test::records::test::F2::kD));
+    assert(equal(RoundtripFlags2(::test::records::test::F2::kC | ::test::records::test::F2::kE), ::test::records::test::F2::kC | ::test::records::test::F2::kE));
+
+    assert(equal(
+        RoundtripFlags3(::test::records::test::Flag8::kB0, ::test::records::test::Flag16::kB1, ::test::records::test::Flag32::kB2),
+        std::tuple<::test::records::test::Flag8, ::test::records::test::Flag16, ::test::records::test::Flag32>(::test::records::test::Flag8::kB0, ::test::records::test::Flag16::kB1, ::test::records::test::Flag32::kB2)
+    ));
+
+    {
+        auto r = RoundtripRecord1(::test::records::test::R1 {
+            8,
+            ::test::records::test::F1::k_None,
+        });
+        assert(equal(r.a, (uint8_t)8));
+        assert(equal(r.b, ::test::records::test::F1::k_None));
+    }
+
+    auto r = RoundtripRecord1(::test::records::test::R1 {
+        0,
+        ::test::records::test::F1::kA | ::test::records::test::F1::kB,
+    });
+    assert(equal(r.a, (uint8_t)0));
+    assert(equal(r.b, ::test::records::test::F1::kA | ::test::records::test::F1::kB));
+
+    assert(equal(Tuple1(std::tuple<uint8_t>(1)), std::tuple<uint8_t>(1)));
+}
+
+std::tuple<uint8_t, uint16_t> exports::test::records::test::MultipleResults() {
+    return std::tuple<uint8_t, uint16_t>(100, 200);
+}
+
+std::tuple<uint32_t, uint8_t> exports::test::records::test::SwapTuple(std::tuple<uint8_t, uint32_t> a) {
+    return std::tuple<uint32_t, uint8_t>(std::get<1>(a), std::get<0>(a));
+}
+
+test::records::test::F1 exports::test::records::test::RoundtripFlags1(::test::records::test::F1 a) {
+    return a;
+}
+
+test::records::test::F2 exports::test::records::test::RoundtripFlags2(::test::records::test::F2 a) {
+    return a;
+}
+
+std::tuple<test::records::test::Flag8, test::records::test::Flag16, test::records::test::Flag32> exports::test::records::test::RoundtripFlags3(::test::records::test::Flag8 a, ::test::records::test::Flag16 b, ::test::records::test::Flag32 c) {
+    return std::tuple<::test::records::test::Flag8, ::test::records::test::Flag16, ::test::records::test::Flag32>(a, b, c);
+}
+
+test::records::test::R1 exports::test::records::test::RoundtripRecord1(::test::records::test::R1 a) {
+    return a;
+}
+
+std::tuple<uint8_t> exports::test::records::test::Tuple1(std::tuple<uint8_t> a) {
+    return std::tuple<uint8_t>(std::get<0>(a));
+}
diff --git a/tests/runtime/results/wasm.new.cpp b/tests/runtime/results/wasm.new.cpp
new file mode 100644
index 000000000..68ea1e46a
--- /dev/null
+++ b/tests/runtime/results/wasm.new.cpp
@@ -0,0 +1,56 @@
+#include <assert.h>
+#include <results_cpp.h>
+
+template <class T>
+bool equal(T const&a, T const&b) {
+    return a==b;
+}
+
+std::expected<float, wit::string> exports::test::results::test::StringError(float a) {
+    return ::test::results::test::StringError(a);
+}
+
+std::expected<float, ::test::results::test::E> exports::test::results::test::EnumError(float a) {
+    auto result = ::test::results::test::EnumError(a);
+    if (result.has_value()) { return result.value(); }
+    return std::unexpected(result.error());
+    // if (result.error()==::test::results::test::E::kA) { return std::unexpected(::test::results::test::E::kA); }
+    // if (result.error()==::test::results::test::E::kB) { return std::unexpected(::test::results::test::E::kB); }
+    // if (result.error()==::test::results::test::E::kC) { return std::unexpected(::test::results::test::E::kC); }
+}
+
+std::expected<float, ::test::results::test::E2> exports::test::results::test::RecordError(float a) {
+    auto result = ::test::results::test::RecordError(a);
+    if (result.has_value()) { return result.value(); }
+    return std::unexpected(::test::results::test::E2{ result.error().line, result.error().column });
+}
+
+std::expected<float, ::test::results::test::E3> exports::test::results::test::VariantError(float a) {
+    auto result = ::test::results::test::VariantError(a);
+    if (result.has_value()) { return result.value(); }
+    return std::unexpected(result.error());
+
+    // match test_imports::variant_error(a) {
+    //     Ok(b) => Ok(b),
+    //     Err(test_imports::E3::E1(test_imports::E::A)) => {
+    //         Err(test_exports::E3::E1(test_exports::E::A))
+    //     }
+    //     Err(test_imports::E3::E1(test_imports::E::B)) => {
+    //         Err(test_exports::E3::E1(test_exports::E::B))
+    //     }
+    //     Err(test_imports::E3::E1(test_imports::E::C)) => {
+    //         Err(test_exports::E3::E1(test_exports::E::C))
+    //     }
+    //     Err(test_imports::E3::E2(test_imports::E2 { line, column })) => {
+    //         Err(test_exports::E3::E2(test_exports::E2 { line, column }))
+    //     }
+    // }
+}
+
+std::expected<uint32_t, wit::Void> exports::test::results::test::EmptyError(uint32_t a) {
+    return ::test::results::test::EmptyError(a);
+}
+
+std::expected<std::expected<void, wit::string>, wit::string> exports::test::results::test::DoubleError(uint32_t a) {
+    return ::test::results::test::DoubleError(a);
+}
diff --git a/tests/runtime/smoke/wasm.cpp b/tests/runtime/smoke/wasm.cpp
new file mode 100644
index 000000000..a850741e3
--- /dev/null
+++ b/tests/runtime/smoke/wasm.cpp
@@ -0,0 +1,6 @@
+#include <smoke_cpp.h>
+//#include <stdio.h>
+
+void exports::smoke::Thunk() {
+    test::smoke::imports::Thunk();
+}
diff --git a/tests/runtime/strings/wasm.cpp b/tests/runtime/strings/wasm.cpp
new file mode 100644
index 000000000..6ab504089
--- /dev/null
+++ b/tests/runtime/strings/wasm.cpp
@@ -0,0 +1,28 @@
+#include <assert.h>
+#include <strings_cpp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+void assert_str(std::string_view str, const char* expected) {
+  size_t expected_len = strlen(expected);
+  assert(str.size() == expected_len);
+  assert(memcmp(str.data(), expected, expected_len) == 0);
+}
+
+void exports::strings::TestImports() {
+  test::strings::imports::TakeBasic(std::string_view("latin utf16"));
+
+  wit::string str2 = test::strings::imports::ReturnUnicode();
+  assert_str(str2.get_view(), "🚀🚀🚀 𠈄𓀀");
+}
+
+wit::string exports::strings::ReturnEmpty() {
+  // return a non-zero address (follows cabi_realloc logic)
+  return wit::string((char const*)1, 0);
+}
+
+wit::string exports::strings::Roundtrip(wit::string &&str) {
+  assert(str.size() > 0);
+  return std::move(str);
+}
diff --git a/tests/runtime/strings/wasm.new.cpp b/tests/runtime/strings/wasm.new.cpp
new file mode 100644
index 000000000..6988f224d
--- /dev/null
+++ b/tests/runtime/strings/wasm.new.cpp
@@ -0,0 +1,29 @@
+#include <assert.h>
+#include <strings_cpp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+void assert_str(std::string_view str, const char* expected) {
+  size_t expected_len = strlen(expected);
+  assert(str.size() == expected_len);
+  assert(memcmp(str.data(), expected, expected_len) == 0);
+}
+
+void exports::strings::TestImports() {
+  test::strings::imports::TakeBasic(std::string_view("latin utf16"));
+
+  wit::string str2 = test::strings::imports::ReturnUnicode();
+  assert_str(str2.get_view(), "🚀🚀🚀 𠈄𓀀");
+}
+
+wit::string exports::strings::ReturnEmpty() {
+  // return a non-zero address (follows cabi_realloc logic)
+  return wit::string((char const*)1, 0);
+}
+
+// new API: Identical for guest import and export
+wit::string exports::strings::Roundtrip(std::string_view str) {
+  assert(str.size() > 0);
+  return wit::string::from_view(str);
+}
diff --git a/tests/runtime/strings/wasm.rs b/tests/runtime/strings/wasm.rs
new file mode 100644
index 000000000..9fe6d4c9b
--- /dev/null
+++ b/tests/runtime/strings/wasm.rs
@@ -0,0 +1,25 @@
+wit_bindgen::generate!({
+    path: "../../tests/runtime/strings",
+});
+
+struct Exports;
+
+export!(Exports);
+
+impl Guest for Exports {
+    fn test_imports() -> () {
+        test::strings::imports::take_basic("latin utf16");
+
+        let str2 = test::strings::imports::return_unicode();
+        assert_eq!(str2, "🚀🚀🚀 𠈄𓀀");
+    }
+
+    fn return_empty() -> String {
+        Default::default()
+    }
+
+    fn roundtrip(s: String) -> String {
+        assert!(!s.is_empty());
+        s
+    }
+}
diff --git a/wasm-tools.patch b/wasm-tools.patch
new file mode 100644
index 000000000..d2cabf2c6
--- /dev/null
+++ b/wasm-tools.patch
@@ -0,0 +1,15 @@
+diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs
+index 1c383384..e5cbb2c1 100644
+--- a/crates/wit-parser/src/abi.rs
++++ b/crates/wit-parser/src/abi.rs
+@@ -148,6 +148,10 @@ impl Resolve {
+             params.truncate(0);
+             params.push(WasmType::Pointer);
+             indirect_params = true;
++        } else {
++            if matches!((&func.kind,variant), (crate::FunctionKind::Method(_),AbiVariant::GuestExport)) {
++                params.get_mut(0).map(|p| *p=WasmType::Pointer);
++            }
+         }
+ 
+         let mut results = Vec::new();